Visualisations cartographiques
Demandez à Chat de tracer des données géographiques sur une carte interactive — en reliant les liaisons d'ontologie dotées de propriétés lat/lng au bloc de carte intégré.
Chat peut répondre aux questions géospatiales avec une carte interactive intégrée — des marqueurs affichés sur un canevas MapLibre GL / OpenStreetMap, avec une bascule par couche et une fenêtre contextuelle au clic.
Les blocs de carte exigent que l'ontologie comporte au moins un type d'objet doté de propriétés flottantes latitude et longitude, adossé à une liaison silver_table vers une source de données active. Consultez Création : ontologies pour savoir comment les déclarer.
Fonctionnement
Lorsque Chat appelle l'outil query_map en réponse à une question géospatiale (par ex. « montre les drones et les aéronefs militaires près du flanc est »), il :
- Interroge l'ontologie pour les types d'objets demandés via la route de requête géographique de la plateforme.
- Normalise chaque instance en
{ lat, lng, label, properties }. - Construit un bloc de contenu
mapavec une couche par catégorie de ressource. - Transmet en continu le bloc au panneau de chat, où la carte s'affiche en ligne avec des bascules de couches.
Aucune configuration de carte n'est requise de la part de l'opérateur — l'outil utilise les propriétés latitude/longitude présentes sur le type d'objet et ajuste automatiquement la vue aux données.
Déclencher une carte
Interrogez Chat naturellement. Parmi les formulations qui déclenchent de manière fiable l'outil de carte :
- « montre-moi sur une carte les drones et les aéronefs militaires près du flanc est »
- « trace toutes les positions d'aéronefs des 15 dernières minutes »
- « cartographie l'activité des drones dans la zone d'intérêt »
L'outil choisit les couches à afficher en fonction de la question — vous pouvez aussi demander une seule couche (« uniquement les drones »). Vos mots sont mis en correspondance avec les types d'objets de l'ontologie, de sorte que vous n'avez jamais besoin des slugs de type exacts : « drones » trouve à la fois l'entité Drone et ses positions DroneObservation, « bases » trouve MilitaryBase, et la réponse indique comment chaque mot a été mis en correspondance. Demander certains types de choses ne trace que ces couches ; demander « tout » trace chaque type cartographiable.
Le bloc de contenu map
Un bloc de carte a la forme suivante :
{
"type": "map",
"title": "Airborne activity — Suwałki Gap AOI",
"center": { "lat": 54.1, "lng": 23.0 },
"zoom": 6,
"layers": [
{
"id": "drones",
"label": "Drones",
"color": "#ffb107",
"markers": [
{ "id": "d1", "lat": 54.3, "lng": 23.2, "label": "UAV-ALPHA", "description": "Bayraktar TB2 · friendly" }
]
},
{
"id": "aircraft",
"label": "Aircraft",
"color": "#0069cc",
"markers": [
{ "id": "a1", "lat": 54.0, "lng": 23.5, "label": "NATO01", "description": "30000 ft" }
]
}
]
}Le composant MapView affiche ce bloc. Ses props sont :
| Prop | Type | Description |
|---|---|---|
layers | MapLayer[] | Obligatoire. Chaque couche possède un id, un label, une color et des markers. |
center | { lat, lng } | Facultatif. S'il est omis, la vue s'ajuste automatiquement aux marqueurs. |
zoom | number | 6 par défaut. Niveau de zoom initial (ignoré en cas d'ajustement automatique). |
height | number | string | 420 par défaut. Hauteur du canevas en pixels ou valeur CSS. |
Un marqueur peut aussi porter deux objets facultatifs, copiés tels quels depuis l'entité d'ontologie à partir de laquelle il a été construit. Un marqueur qui ne les a pas ne porte aucune des deux clés.
| Champ du marqueur | Forme | Signification |
|---|---|---|
track | { headingDeg?, speedMps?, observedAt? } | Route fond en degrés dans le sens horaire depuis le nord vrai, vitesse fond en mètres par seconde, et l'heure ISO-8601 à laquelle la position a été observée à la source. |
sensor | { azimuthDeg, fieldOfViewDeg, rangeMeters } | Le secteur couvert par un capteur de l'objet : relèvement central, largeur angulaire totale en degrés, et portée en mètres. |
L'image tactique
Lorsque des marqueurs portent un track ou un sensor, la carte les dessine comme une image tactique. Une couche de
marqueurs simples s'affiche exactement comme avant.
| Ce que vous voyez | Dessiné quand | Signification |
|---|---|---|
| Tige de direction de déplacement | track.headingDeg est défini et la piste n'est pas à l'arrêt | Une ligne partant du symbole dans la direction du cap. Elle s'allonge pour les pistes plus rapides : courte en dessous d'environ 30 kn (ou lorsque la vitesse est inconnue), moyenne en dessous d'environ 195 kn, longue au-delà. |
| Cône de mouvement | track a à la fois un cap et une vitesse supérieure à zéro | Un secteur translucide dans la couleur de la couche, couvrant l'endroit où la piste pourrait se trouver dans les cinq prochaines minutes. Il s'élargit d'un degré pour chaque dizaine de secondes d'âge de la position, jusqu'à 40° de part et d'autre. |
| Empreinte de capteur | sensor est défini | Un secteur en pointillés, légèrement rempli : le champ de vision jusqu'à sa portée. |
| Piste périmée | la position date de plus de deux minutes, ou observedAt est absent ou illisible | Le symbole et l'étiquette s'estompent et l'étiquette affiche l'âge, par exemple · 3m ago, ou · time unknown. Une position sans heure d'observation n'est jamais présentée comme actuelle. |
| Traînée | le même marqueur arrive à de nouvelles positions (données en direct) | Une ligne passant par ses 30 dernières positions, s'estompant de la plus récente à la plus ancienne. |
| Cercles de distance | le marqueur est sur la couche de focus, ou vous avez choisi Show range rings dans sa fenêtre contextuelle | Des cercles en pointillés à 5, 10 et 20 km, mesurés sur l'ellipsoïde WGS84. |
Une légende de ces styles apparaît sous les bascules de couches dès qu'une couche porte des pistes ou des capteurs. La carte affiche aussi une barre d'échelle métrique et, tant que le pointeur la survole, la référence de grille MGRS sous le curseur. Cette référence n'est jamais plus précise qu'un pixel d'écran au zoom courant.
La péremption et la largeur des cônes sont réévaluées une fois par seconde, de sorte qu'une piste s'estompe d'elle-même lorsque son flux s'arrête. Une valeur de piste que la carte ne peut pas dessiner, comme un champ de vision de largeur nulle ou une latitude au-delà de 90°, est laissée hors de la carte ; le reste de la carte s'affiche quand même.
Importez MapView depuis @scrydon/ui/map-viewer :
import { MapView } from '@scrydon/ui/map-viewer'
import type { MapLayer } from '@scrydon/ui/map-viewer'Acheminer les données géographiques via l'ontologie
Les blocs de carte sont alimentés par des données qui transitent par le pipeline d'ontologie de la plateforme. Le câblage complet :
Data source → StarRocks table → silver_table binding → ontology object type
↓
lat/lng properties projected
↓
query_map tool → map blockDéfinissez une source de données qui écrit des lignes avec des colonnes de latitude et de longitude. Le nom logique de la table (par ex. drone_observation) doit correspondre octet pour octet entre la source et la liaison.
// In a pack (declarative JSON source) or in-platform (code source with produce())
defineDataSource({
kind: 'table',
id: 'my-asset-tracker',
vendor: 'my-vendor',
scope: 'org',
table: {
name: 'asset_position',
columns: [
{ name: 'assetId', dataType: 'string', isPrimaryKey: true },
{ name: 'latitude', dataType: 'decimal' },
{ name: 'longitude', dataType: 'decimal' },
{ name: 'observedAt', dataType: 'timestamp', isPrimaryKey: true },
],
primaryKey: ['assetId', 'observedAt'],
timestampColumn: 'observedAt',
},
ingest: { mode: 'poll', intervalSec: 60, writeMode: 'append' },
})Déclarez le type d'objet avec des propriétés flottantes latitude et longitude dans le manifeste de votre ontologie.
defineObjectType({
slug: 'AssetPosition',
displayName: 'Asset position',
kind: 'transactional',
properties: [
defineProperty({ slug: 'assetId', displayName: 'Asset ID', scalarType: 'string', cardinality: 'required' }),
defineProperty({ slug: 'latitude', displayName: 'Latitude', scalarType: 'float', cardinality: 'required' }),
defineProperty({ slug: 'longitude', displayName: 'Longitude', scalarType: 'float', cardinality: 'required' }),
defineProperty({ slug: 'observedAt', displayName: 'Observed at', scalarType: 'timestamp', cardinality: 'required' }),
],
})Ajoutez une liaison silver_table qui associe les colonnes de la table aux propriétés de votre type d'objet. La valeur source.table doit correspondre au table.name de la source de données.
defineBinding({
objectTypeSlug: 'AssetPosition',
kind: 'silver_table',
source: { table: 'asset_position' },
identitySpec: { identityColumns: ['assetId', 'observedAt'] },
columnMap: {
assetId: 'assetId',
latitude: 'latitude',
longitude: 'longitude',
observedAt: 'observedAt',
},
})Ajoutez des règles d'identité afin que les instances soient correctement dédupliquées.
defineIdentityRule({
objectTypeSlug: 'AssetPosition',
deterministicKeys: ['assetId', 'observedAt'],
})Construisez et installez le pack. Une fois installé, demandez à Chat de tracer les données :
« Affiche les objets AssetPosition sur une carte. »
Chat résout la liaison d'ontologie, projette les propriétés lat/lng et affiche un bloc de carte en ligne.
Conventions de nommage des propriétés
La route de requête géographique résout les coordonnées à l'aide d'un repli sur plusieurs noms. Utilisez les noms standard pour garantir une résolution automatique :
| Coordonnée | Slugs de propriété acceptés |
|---|---|
| Latitude | latitude, lat |
| Longitude | longitude, lng, lon |
La première valeur flottante finie correspondante, dans l'ordre de priorité ci-dessus, est utilisée. Les propriétés hors de cette liste sont ignorées pour la résolution des coordonnées (elles apparaissent tout de même dans les fenêtres contextuelles des marqueurs via properties).
Utilisez callsign, droneId, registration ou icao24 comme libellé d'affichage principal — ces propriétés sont résolues en premier lors de la construction du libellé du marqueur. Toute autre propriété de type chaîne se rabat sur l'id de l'instance.
Utiliser MapView directement
Si vous construisez une surface React personnalisée (par ex. un tableau de bord analytique ou une micro-application intégrée à un pack), importez MapView directement depuis @scrydon/ui/map-viewer. Le composant s'exécute uniquement côté client — il ne doit pas être rendu côté serveur.
import { MapView, type MapLayer } from '@scrydon/ui/map-viewer'
const layers: MapLayer[] = [
{
id: 'assets',
label: 'My assets',
color: '#0069cc',
markers: features.map((f) => ({
id: f.id,
lat: f.lat,
lng: f.lng,
label: f.label,
description: f.description,
})),
},
]
return <MapView layers={layers} center={{ lat: 54.1, lng: 23.0 }} zoom={6} height={480} />Ajoutez @import "maplibre-gl/dist/maplibre-gl.css" à la feuille de style racine de votre application. Sans cela, le canevas de la carte s'affiche, mais les contrôles de navigation et les marqueurs risquent de ne pas s'afficher correctement.
Le composant MapView :
- Utilise les fonds de carte CARTO Positron et Dark Matter, selon le thème clair ou sombre de l'application — aucune clé d'API n'est requise.
- Cadre la vue sur les marqueurs visibles lorsque
centerest omis — une seule fois, sur les premières données reçues. Les mises à jour suivantes (positions en direct) ne déplacent jamais la caméra, qui reste là où l'opérateur l'a laissée. - Affiche une barre de bascule des couches en haut de la carte pour les jeux de données multicouches.
- Libère l'instance MapLibre lors du démontage.
Couches de géométrie linéaire (itinéraires)
En plus des marqueurs ponctuels, MapView peut afficher des itinéraires sous forme de polylignes —
utiles pour les tracés de câbles, les corridors de transit ou toute entité linéaire. Une couche peut
porter à la fois des markers et des lines ; la bascule masque les deux.
MapLayer.lines
Ajoutez un tableau lines à n'importe quelle couche. Chaque entrée est une MapBlockLine :
import { MapView, type MapLayer, type MapBlockLine } from '@scrydon/ui/map-viewer'
const layers: MapLayer[] = [
{
id: 'cables',
label: 'Submarine cables',
color: '#e84040',
markers: [], // anchor points, if any
lines: [
{
id: 'cable-1',
label: 'North Atlantic Express',
path: [[-73.9, 40.7], [-20.1, 64.1], [-6.3, 53.3]], // [lng, lat] pairs
description: 'Trans-Atlantic cable system',
},
],
},
]
return <MapView layers={layers} height={480} />Le path est un tableau de paires [longitude, latitude] (ordre des coordonnées
GeoJSON). MapView affiche chaque ligne avec la couleur de la couche et inclut les
sommets des lignes lors de l'ajustement automatique des limites de la vue.
Créer une source de données à géométrie linéaire
Pour exposer une géométrie d'itinéraire via le pipeline d'ontologie, ajoutez une colonne pathJson
(chaîne) à la table de votre source de données. La route de requête géographique de la plateforme
reconnaît les clés pathJson, routeJson et geometryJson et les analyse
comme des tableaux [[lng, lat], …].
defineDataSource({
kind: 'table',
id: 'my-cable-source',
vendor: 'my-vendor',
scope: 'org',
table: {
name: 'cable_route',
columns: [
{ name: 'cableId', dataType: 'string', isPrimaryKey: true },
{ name: 'cableName', dataType: 'string' },
{ name: 'anchorLatitude', dataType: 'decimal' }, // midpoint for bounds/label
{ name: 'anchorLongitude', dataType: 'decimal' },
{ name: 'pathJson', dataType: 'string' }, // JSON [[lng,lat],…]
{ name: 'updatedAt', dataType: 'timestamp' },
],
primaryKey: ['cableId'],
timestampColumn: 'updatedAt',
},
ingest: { mode: 'poll', intervalSec: 86400, writeMode: 'upsert' },
})Déclarez pathJson comme propriété de type string sur le type d'objet correspondant et
incluez-la dans le columnMap de la liaison silver_table. La route géographique analysera et
validera automatiquement le chemin ; les coordonnées malformées ou hors limites sont
ignorées silencieusement.
defineObjectType({
slug: 'CableRoute',
displayName: 'Cable route',
kind: 'reference',
properties: [
defineProperty({ slug: 'cableId', displayName: 'Cable ID', scalarType: 'string', cardinality: 'required' }),
defineProperty({ slug: 'cableName', displayName: 'Name', scalarType: 'string', cardinality: 'optional' }),
defineProperty({ slug: 'anchorLatitude', displayName: 'Anchor lat', scalarType: 'float', cardinality: 'optional' }),
defineProperty({ slug: 'anchorLongitude', displayName: 'Anchor lng', scalarType: 'float', cardinality: 'optional' }),
defineProperty({ slug: 'pathJson', displayName: 'Route path', scalarType: 'string', cardinality: 'optional' }),
],
})Lorsqu'une réponse query_map contient des entités dont le chemin a été résolu, ces
entités sont placées dans un tableau lines de la couche plutôt que dans markers.
Aucune configuration de l'outil n'est requise — l'appel query_map de l'agent gère cette
répartition automatiquement.
Attribution ODbL pour les couches dérivées d'OpenStreetMap
Si vos données d'itinéraire sont dérivées d'OpenStreetMap (par ex. des câbles sous-marins extraits via Overpass), vous devez inclure une attribution partout où la couche est affichée ou distribuée :
© OpenStreetMap contributors, licensed under the Open Database License (ODbL).
L'ODbL exige également le partage à l'identique : toute base de données dérivée de données ODbL doit être publiée sous la même licence. Consultez opendatacommons.org/licenses/odbl pour le texte intégral. Les sources de données à usage exclusivement non commercial (comme la Submarine Cable Map de TeleGeography, sous licence CC BY-NC-SA) ne conviennent pas à une utilisation dans des déploiements clients — utilisez plutôt OSM/ODbL.