import { Feature , FeatureCollection , FeatureGeometry , GeometryCollection } from "@here/harp-datasource-protocol" ; import { GeoBox , GeoCoordinates } from "@here/harp-geoutils" ; import { MapView } from "@here/harp-mapview" ; import { LoggerManager } from "@here/harp-utils" ; import { GeoJsonDataProvider , GeoJsonDataProviderOptions , VectorTileDataSource , VectorTileDataSourceParameters } from "@here/harp-vectortile-datasource" ; import { MapViewFeature } from "./Features" ; const logger = LoggerManager . instance . create ( "FeaturesDataSource" ) ; const NAME = "user-features-datasource" ; const DEFAULT_GEOJSON = { type : "FeatureCollection" , features : [ ] } ; /**
 * Options for [[FeaturesDataSource]].
 */ export interface FeatureDataSourceOptions extends VectorTileDataSourceParameters , GeoJsonDataProviderOptions { /**
     * Initial set of features for new instance of [[FeaturesDataSource]].
     *
     * Shortcut for calling [[FeaturesDataSource.add]] after construction.
     */ features ? : MapViewFeature [ ] ; /**
     * Initial GeoJSON load for new instance of [[FeaturesDataSource]].
     *
     * Shortcut for calling [[FeaturesDataSource.setFromGeojson]] after construction.
     */ geojson ? : FeatureCollection | GeometryCollection | Feature ; } /**
 * [[DataSource]] implementation to use for the addition of custom features.
 */ export class FeaturesDataSource extends VectorTileDataSource { private m_isAttached = false ; private m_featureCollection : FeatureCollection = this . emptyGeojson ( ) ; param options constructor ( options ? ) { super ( { ... options , dataProvider : new GeoJsonDataProvider ( NAME , DEFAULT_GEOJSON , options ) , addGroundPlane : false } ) ; if ( options !== undefined ) { if ( options . features !== undefined ) { this . add ( ... options . features ) ; } if ( options . geojson !== undefined ) { this . setFromGeojson ( options . geojson ) ; } } } param geojson setFromGeojson ( geojson ) { if ( geojson . type === "FeatureCollection" ) { this . m_featureCollection = geojson ; } else if ( geojson . type === "Feature" ) { this . m_featureCollection = this . emptyGeojson ( ) ; this . m_featureCollection . features . push ( geojson ) ; } else if ( geojson . type === "GeometryCollection" ) { this . m_featureCollection = this . emptyGeojson ( ) ; for ( const geometry of geojson . geometries ) { this . m_featureCollection . features . push ( { type : "Feature" , geometry } ) ; } } else { throw new TypeError ( "The provided object is not a valid GeoJSON object." ) ; } this . update ( ) ; return this ; } param features add ( ... features ) { for ( const feature of features ) { this . addFeature ( feature ) ; } this . update ( ) ; return this ; } param features remove ( ... features ) { for ( const feature of features ) { this . removeFeature ( feature ) ; } this . update ( ) ; return this ; } /**
     * Removes all the custom features in this `FeaturesDataSource`.
     */ clear ( ) { this . m_featureCollection = this . emptyGeojson ( ) ; this . update ( ) ; } @override  async connect ( ) { await super . connect ( ) ; if ( this . m_featureCollection . features . length > 0 ) { await this . update ( ) ; } } param mapView @override
      attach ( mapView ) { super . attach ( mapView ) ; this . m_isAttached = true ; } param mapView @override
      detach ( mapView ) { super . detach ( mapView ) ; this . m_isAttached = false ; } /**
     * Get [[GeoBox]] containing all the points in datasource.
     *
     * Returns undefined if there were no features added to this DS.
     */ getGeoBox ( ) { let result ; const addPoint = ( geoJsonCoords ) => { const coords = new GeoCoordinates ( geoJsonCoords [ 1 ] , geoJsonCoords [ 0 ] ) ; if ( result === undefined ) { result = new GeoBox ( coords , coords . clone ( ) ) ; } else { result . growToContain ( coords ) ; } } ; for ( const feature of this . m_featureCollection . features ) { switch ( feature . geometry . type ) { case "Point" : addPoint ( feature . geometry . coordinates ) ; break ; case "MultiPoint" : case "LineString" : feature . geometry . coordinates . forEach ( addPoint ) ; break ; case "MultiLineString" : case "Polygon" : feature . geometry . coordinates . forEach ( segment => segment . forEach ( addPoint ) ) ; break ; case "MultiPolygon" : feature . geometry . coordinates . forEach ( polygon => polygon . forEach ( segment => segment . forEach ( addPoint ) ) ) ; break ; } } return result ; } private addFeature ( feature ) { const hasFeature = this . m_featureCollection . features . some ( _feature => _feature . properties . __mapViewUuid === feature . uuid ) ; if ( hasFeature ) { return ; } const geometry = { type : feature . type , coordinates : feature . coordinates } as any ; const geojsonFeature = { type : "Feature" , geometry , properties : { ... feature . properties , __mapViewUuid : feature . uuid } } ; this . m_featureCollection . features . push ( geojsonFeature ) ; } private removeFeature ( feature ) { const index = this . m_featureCollection . features . findIndex ( _feature => _feature . properties . __mapViewUuid === feature . uuid ) ; if ( index === - 1 ) { return ; } this . m_featureCollection . features . splice ( index , 1 ) ; } private async update ( ) { const dataProvider = this . dataProvider ( ) as GeoJsonDataProvider ; if ( ! this . m_isAttached || ! dataProvider . ready ( ) ) { return ; } try { await dataProvider . updateInput ( this . m_featureCollection ) ; if ( this . m_isAttached ) { this . mapView . markTilesDirty ( this ) ; } } catch ( error ) { logger . error ( `[${ this . name }]: failed to update tile index` , error ) ; } } private emptyGeojson ( ) { return { features : [ ] , type : "FeatureCollection" } ; } }