UNPKG

@polygonjs/plugin-mapbox

Version:

Mapbox plugin for the 3D engine https://polygonjs.com

119 lines (106 loc) 3.81 kB
/** * Creates Mapbox layers. * * @remarks * This is best used with the Mapbox camera. * Note that you will need a mapbox key to use this node. */ import {Object3D} from 'three'; import {CoreString} from '@polygonjs/polygonjs/dist/src/core/String'; import {FeatureConverter} from '../../../core/mapbox/FeatureConverter'; // const MULTILINESTRING = 'MultiLineString' // const LINESTRING = 'LineString' const DEFAULT_LIST: Readonly<string> = [ // 'road-motorway-trunk', // not found in prod, need to investigate 'road-primary', 'road-secondary-tertiary', 'road-street', ].join(' '); import {NodeParamsConfig, ParamConfig} from '@polygonjs/polygonjs/dist/src/engine/nodes/utils/params/ParamsConfig'; import {MapboxListenerParamConfig, MapboxListenerSopNode} from './utils/MapboxListener'; import {MapUtils} from '@polygonjs/polygonjs/dist/src/core/MapUtils'; import {ArrayUtils} from '@polygonjs/polygonjs/dist/src/core/ArrayUtils'; // use_bounds: false, // update_always_allowed: false class MapboxLayerSopParamsConfig extends MapboxListenerParamConfig(NodeParamsConfig) { /** @param names of layers to create */ layers = ParamConfig.STRING(DEFAULT_LIST); } const ParamsConfig = new MapboxLayerSopParamsConfig(); export class MapboxLayerSopNode extends MapboxListenerSopNode<MapboxLayerSopParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return 'mapboxLayer'; } override cook() { this._mapboxListener.cook(); } _postInitController() { if (!this._cameraNode) { return; } const firstMap = this._cameraNode.firstMap(); if (firstMap == null) { this.states.error.set('map not initialized yet'); return; } const layerNames = CoreString.attribNames(this.pv.layers); const existingLayerNames: string[] = []; for (let layerName of layerNames) { if (firstMap.getLayer(layerName)) { existingLayerNames.push(layerName); } else { // const layers = first_map.getStyle().layers; this.states.error.set(`layer ${layerName} does not exist`); return; } } const features = firstMap.queryRenderedFeatures(undefined, { layers: existingLayerNames, }); const objects: Object3D[] = []; if (features) { const featuresByName = this._groupFeaturesByName(features); featuresByName.forEach((featuresForName, featureName) => { const converter = new FeatureConverter(this, featureName, featuresForName); const new_object = converter.createObject(); if (new_object) { objects.push(new_object); } }); } this.setObjects(objects); } private _features_by_name: Map<string, mapboxgl.MapboxGeoJSONFeature[]> = new Map(); private _groupFeaturesByName( features: mapboxgl.MapboxGeoJSONFeature[] ): Map<string, mapboxgl.MapboxGeoJSONFeature[]> { this._features_by_name.clear(); for (let feature of features) { const name = this._feature_name(feature); if (name) { MapUtils.pushOnArrayAtEntry(this._features_by_name, name, feature); } } return this._features_by_name; } private _feature_name(feature: mapboxgl.MapboxGeoJSONFeature): string | undefined { const properties = feature['properties']; let name: string | undefined; if (properties) { name = properties['name'] || properties['name_en']; //|| Math.floor(Math.random()*100000000) if (name == null) { name = this._id_from_feature(feature); } } return name; } private _id_from_feature(feature: mapboxgl.MapboxGeoJSONFeature): string { const json_str = JSON.stringify(feature.geometry).replace(/{|}|"|:|\[|\]|,|\./g, ''); const json_str_elements = json_str.split(''); const letters_count = 30; const chunks = ArrayUtils.chunk(json_str_elements, json_str_elements.length / letters_count); const first_elements = chunks.map((c) => c[0]); return first_elements.join(''); } }