terriajs
Version:
Geospatial data visualization platform.
236 lines (157 loc) • 10.2 kB
Markdown
# Feature picking
How Terria handles picking features on a map.
This document mainly serves as a guide to debug or extend feature picking functionality.
It is a work in progress.
**Not included in doc**:
- How picked features are shared
## Related docs
- [Feature Info Template](../connecting-to-data/customizing-data-appearance/feature-info-template.md)
## Outline
1. Catalog item creates "mappable" items:
- [Cesium Imagery Provider](https://cesium.com/learn/cesiumjs/ref-doc/ImageryProvider.html)
- [Cesium Data Source](https://cesium.com/learn/cesiumjs/ref-doc/DataSource.html)
- [Cesium Terrain Provider](https://cesium.com/learn/cesiumjs/ref-doc/TerrainProvider.html)
- [Cesium Abstract Primitive](https://cesium.com/learn/cesiumjs/ref-doc/Primitive.html)
1. Catalog item returns `mapItems` which are rendered by Leaflet or Cesium
1. User clicks on map
- Cesium/Leaflet resolves features
- `FeatureInfoUrlMixin.getFeaturesFromPickResult` is called if applicable
- Feature highlight is created if applicable
1. Terria `PickedFeatures` is populated
1. Picked features appear in [`FeatureInfoPanel`](#featureinfopanel)
- Catalog item for each feature is resolved
- Each catalog item renders [`FeatureInfoCatalogItem`](#featureinfocatalogitem)
- Each feature in catalog item renders [`FeatureInfoSection`](#featureinfosection)
1. Feature info is rendered by [`FeatureInfoSection`](#featureinfosection)
- Pre-processing/cleaning of feature properties
- Setup Mustache template context data (eg custom expressions)
- `FeatureInfoContext.featureInfoContext()` is called if applicable - and merged into Mustache template context
- Feature info template is rendered
- Mustache
- parseCustomHtmlToReact
**Why is this so complicated?**
Because there is no explicit link between Terria model layer and map renderers (Cesium/Leaflet).
## Thoughts
When creating mappable items there are things you must do to ensure feature picking functions correctly:
- What is a feature?
- Vector/cesium primitive
- Pixel value
- 3D tile
- ...
- How is it picked/selected?
- Does it require a network request? (eg WMS `GetFeatureInfo`)
- Will Cesium handle it automatically - or needs manual implementation
- When a feature is picked, can it's owner (catalog item) be resolved?
- Can it's underlying data be resolved (eg a row in a CSV)?
- What feature information is to be shown to the user?
- Is a feature info template needed?
- Does the feature information require data that isn't stored in the feature itself?
- Do additional network requests need to be made?
- Does the feature change over time?
- How should this be handled when the timeline changes?
- How is the feature shared (in share link/story)?
- Does geometry need to be saved - or is feature a product of a network request
This varies for each type of mappable item. Cesium will give you some of them for free - depending on feature type.
## How features are resolved by map renderer
[`Cesium.ts`](../../lib/Models/Cesium.ts) and [`Leaflet.ts`](../../lib/Models/Leaflet.ts) handle turning map features into Terria [`PickedFeatures`](../../lib/Map/PickedFeatures/PickedFeatures.ts)
There is also common feature picking functionality in [`GlobeOrMap.ts`](/lib/Models/GlobeOrMap.ts)
### [Cesium Imagery Provider](https://cesium.com/learn/cesiumjs/ref-doc/ImageryProvider.html)
Most imagery providers are handled automatically by Cesium like so:
1. Click on imagery provider on map (Leaflet or Cesium)
2. Network request is made by Cesium (`ImageryProvider.pickFeatures`)
3. Returns `ImageryLayerFeatureInfo`
4. Convert into Terria [`Feature`](#terria-feature-object)
5. [`featureDataToGeoJson`](../../lib/Map/PickedFeatures/featureDataToGeoJson.ts) will attempt to convert [`Feature`](#terria-feature-object) into geoJSON for feature highlighting
### [Cesium Data Source](https://cesium.com/learn/cesiumjs/ref-doc/DataSource.html)
When picking features, Cesium will return an [`Entity`](https://cesium.com/learn/cesiumjs/ref-doc/Entity.html) or `EntityCollection` - which is converted into a Terria [`Feature`](#terria-feature-object) object (see [`Cesium.pickVectorFeatures`](/lib/Models/Cesium.ts))
Leaflet is a bit more complicated - as it doesn't natively support Cesium Data Sources - see [`LeafletScene`](/lib/Map/Leaflet/LeafletScene.ts) and [`LeafletVisualizer`](/lib/Map/Leaflet/LeafletVisualizer.ts).
### [Cesium Terrain Provider](https://cesium.com/learn/cesiumjs/ref-doc/TerrainProvider.html)
No feature picking is implemented
### [Cesium Abstract Primitive](https://cesium.com/learn/cesiumjs/ref-doc/Primitive.html)
No feature picking is implemented
## How Terria specific info is attached to features
### Terria `Feature` object
See [lib/Models/Feature/Feature.ts](/lib/Models/Feature/Feature.ts)
`Feature` is a wrapper around a Cesium [`Entity`](https://cesium.com/learn/cesiumjs/ref-doc/Entity.html)
- `data` property contains [`TerriaFeatureData`](#terriafeaturedata)
- `_catalogItem` - owner of feature
- `imageryProvider` - if feature picked is from imagery provider
- `loadingFeatureInfoUrl`
- `cesiumEntity` - original cesium entity (when picked)
- `cesiumPrimitive` - original cesium primitive (when picked)
#### `TerriaFeatureData`
See [lib/Models/Feature/FeatureData.ts](/lib/Models/Feature/FeatureData.ts)
This property of [`Feature`](#terria-feature-object) should be used for Terria specific data - it should only be added to [`Feature`](#terria-feature-object) objects when they are created.
Current properties:
- `rowIds` - array of table row IDS that correspond to the feature. This is required for TableMixin to find original data after a [`Feature`](#terria-feature-object) has been picked,
- `timeIntervalCollection` - if feature is time varying, this property can be used instead of `properties` for convenience.
#### Example usage
`TableMixin` example usage - enabling time-series charts in feature info:
- `TableMixin` adds `rowIds` to `data` property here [`lib/Table/createLongitudeLatitudeFeaturePerRow.ts`](/lib/Table/createLongitudeLatitudeFeaturePerRow.ts#L83)
- A feature is picked, which triggers `TableMixin.featureInfoContext()`.
- It calls [`lib/Table/tableFeatureInfoContext.ts`](/lib/Table/tableFeatureInfoContext.ts) which uses `data.rowIds` to add "Mustache context data" to the picked feature.
- The Mustache context data contain time series chart functionality
### `ImageryLayerFeatureInfo`
This is a Cesium object - unchanged in Terria. It is converted to a [`Feature`](#terria-feature-object) object when picked.
Note use of `data` property and how we use `featureDataToGeoJson` to convert `ImageryLayerFeatureInfo` `data` to geoJSON for feature highlighting
### Custom `buildFeatureFromPickResult`
`FeatureInfoUrlTemplateMixin` provides abstract function `buildFeatureFromPickResult` which can be used to implement custom feature picking.
## Feature info panel (view layer)
There are three nested React components
- [`FeatureInfoPanel`](/lib/ReactViews/FeatureInfo/FeatureInfoPanel.tsx)
- [`FeatureInfoCatalogItem`](/lib/ReactViews/FeatureInfo/FeatureInfoCatalogItem.tsx) for each catalog item
- [`FeatureInfoSection`](/lib/ReactViews/FeatureInfo/FeatureInfoSection.tsx) for each feature in each catalog item
### `FeatureInfoPanel`
Top level component
- Pulls features from `Terria.pickedFeatures`
- Matches features with catalog items
- Renders `FeatureInfoCatalogItem` for each.
### `FeatureInfoCatalogItem`
Simple component
- Applied limit to how many features are shown
- Renders `FeatureInfoSection` for each feature in specified catalog item
### `FeatureInfoSection`
Renders feature information.
There are two methods of rendering feature info:
- **"Raw data"** - presents all feature properties as a table
- **"Curated data"** - applies Mustache template using feature properties (and context data) to render complex view of feature properties
- Curated data requires a Mustache [Feature Info Template](/doc/connecting-to-data/customizing-data-appearance/feature-info-template.md)
#### Cleans/pre-processes feature properties
- For time-varying features, gets values for `currentTime`
- Processes nested JSON values
- Replaces values which may interfere with Mustache templates
- Applies `FeatureInfoTraits.format` options (eg `number.toLocaleString` options)
#### Raw data: generate table
Cleaned feature properties are turned into a table for presentation
See [`generateCesiumInfoHTMLFromProperties`](/lib/ReactViews/FeatureInfo/generateCesiumInfoHTMLFromProperties.ts)
#### Curated data: Create "mustache context data"
This is an object with properties which can be used by Mustache templates:
- All (cleaned) feature properties - this forms the base of the object
- `properties` = array of key:value pairs of feature properties
- `terria` magical object
- a bunch of custom mustache expressions
- `partialByName`
- `formatNumber`
- `formatDateTime`
- `urlEncodeComponent`
- `urlEncode`
- `coords` with `latitude` and `longitude`
- `currentTime`
- properties provided by catalog item through `featureInfoContext` function
##### Example mustache template with context data
```md
Some Text: {{someFeatureProperty}}
A magical property from Terria: {{terria.currentTime}}
A custom expression - which formats `terria.currentTime` as `"dd-mm-yyyy HH:MM:ss"`
{{#terria.formatDateTime}}{"format": "dd-mm-yyyy HH:MM:ss"}{{terria.currentTime}}{{/terria.formatDateTime}}
```
#### Curated data: Render mustache template to HTML/markdown
Using three components:
- The template - see `FeatureInfoTraits.template`
- "Mustache context data" - see above
- Partials - see `FeatureInfoTraits.partials`
The output HTML/markdown may contain Custom Components. These are handled by the next step
#### Render HTML/markdown to React
This step is applied to "Raw" and "Curated" data
Uses [`parseCustomMarkdownToReact.ts`](/lib/ReactViews/Custom/parseCustomMarkdownToReact.ts) function to turn HTML/markdown with custom components to React.
This step may cause model layer side-effects - eg `CSVChartCustomComponent`