import { BBox, Feature, FeatureCollection, featureCollection } from "@turf/turf"; import { GeoJsonLayer } from "@deck.gl/layers"; import { PathStyleExtension } from "@deck.gl/extensions"; import { FlyToInterpolator } from "@deck.gl/core"; import EventEmitter from "eventemitter3"; import IHPaaSScene, { IHPaaSEventsMap, IHPaaSLayer, IHPaaSLayerEventKeys, IHPaaSLayerOptions, IHPaaSLayerType, MouseHandler, } from "./IScene"; import { Style } from "../styles"; import convert from "color-convert"; import HPaaSDeckGL from "../HPaaSDeckGL"; import { HpaasEventKey } from "../types"; let layerIndex = 100; const lastinteractionState = { isZooming: false, isPanning: false, }; export default class SceneDeckGL implements IHPaaSScene { hpaas: HPaaSDeckGL; layers: any[] = []; tooltip: { show: boolean; text?: string; html?: string } = { show: false, text: "", html: "", }; constructor(hpaas: HPaaSDeckGL) { this.hpaas = hpaas; this._bindSceneEvent(); } private lastEventTimer: any = {}; private triggerEventDebouncing(eventKey: "zoomend" | "moveend") { clearTimeout(this.lastEventTimer[eventKey]); this.lastEventTimer[eventKey] = setTimeout(() => { const viewport = this.hpaas.deckgl.getViewports()[0]; this.sceneEvent.emit(eventKey, { zoom: viewport.zoom, center: { lng: viewport.longitude, lat: viewport.latitude, }, }); }, 100); } private _bindSceneEvent() { this.hpaas.deckglContainer.addEventListener("contextmenu", (e) => { e.preventDefault(); this.sceneEvent.emit("contextmenu", { lnglat: { lng: 0, lat: 0 }, }); }); this.hpaas.deckgl.setProps({ getTooltip: () => { return this.tooltip.show ? this.tooltip : null; }, onClick: (e: any) => { if (e.coordinate) { const lng = e.coordinate[0]; const lat = e.coordinate[1]; this.sceneEvent.emit("click", { lnglat: { lng, lat, }, feature: e.object, layer: e.layer, }); } }, onHover: (e: any) => { if (e.coordinate) { const lng = e.coordinate[0]; const lat = e.coordinate[1]; this.sceneEvent.emit("mousemove", { lnglat: { lng, lat, }, feature: e.object, layer: e.layer, }); } }, onInteractionStateChange: (e: { isZooming: boolean; isPanning: boolean }) => { if ( lastinteractionState.isZooming == true && e.isZooming == false // interactionState.inTransition == false ) { this.triggerEventDebouncing("zoomend"); } if (lastinteractionState.isPanning == true && e.isPanning == false) { this.triggerEventDebouncing("moveend"); } Object.assign(lastinteractionState, e); }, }); } createLayer(type: IHPaaSLayerType, style: Style, options?: IHPaaSLayerOptions) { // if (!this.hpaas.sceneLoaded) { // this.hpaas.events.on(HpaasEventKey.SCENE_LOADED, () => { // this.createLayer(type, style, options); // }); // return; // } console.log(style); const layer: any = new GeoJsonLayer({ id: type + layerIndex++, data: featureCollection([]), // pickable: true, stroked: !!style.stroke, filled: !!style.fill, // extruded: true, getFillColor: () => { if (style.fill) { let opacity: number[] = []; if (style.fill.opacity) { opacity = [style.fill.opacity * 255]; } return convert.hex.rgb(style.fill.color).concat(opacity); } }, getLineColor: () => { if (style.stroke) { let opacity: number[] = []; if (style.stroke.opacity) { opacity = [style.stroke.opacity * 255]; } return convert.hex.rgb(style.stroke.color).concat(opacity); } }, getLineWidth: () => { if (style.stroke && style.stroke.size) { return style.stroke.size; } }, // getWidth: style.size, lineWidthUnits: "pixels", getDashArray: () => { if (style.stroke) { return style.stroke.dashArray; } }, getPointRadius: () => { if (style.size) { return style.size; } }, pointRadiusUnits: "pixels", // dashJustified: true, extensions: [new PathStyleExtension({ dash: style.stroke?.lineType === "dash" })], }); return layer; } addLayer(layer: IHPaaSLayer) { this.layers.push(layer); this.hpaas.deckgl.setProps({ layers: this.layers }); } setData(layer: IHPaaSLayer, data: FeatureCollection | Feature[]) { layer && layer.updateState({ props: { data: data }, changeFlags: { dataChanged: true } }); } removeLayer(layer: IHPaaSLayer) { this.layers.forEach((l, i) => { if (l.id == layer.id) { this.layers.splice(i, 1); } }); this.hpaas.deckgl.setProps({ layers: this.layers }); } sceneEvent = new EventEmitter(); layerEvent = { on: (layer: IHPaaSLayer, eventKey: IHPaaSLayerEventKeys, handler: (e: MouseHandler) => void) => { if (!this.hpaas.sceneLoaded) { this.hpaas.events.on(HpaasEventKey.SCENE_LOADED, () => { this.layerEvent.on(layer, eventKey, handler); }); return; } if (eventKey == "click") { this.sceneEvent.on("click", (e) => { if (e.layer.id == layer.id) { handler(e); } }); // layer.setState({ // onClick: (e: any) => { // alert(e); // if (e.coordinate) { // const lng = e.coordinate[0]; // const lat = e.coordinate[1]; // handler({ // lnglat: { lng, lat }, // feature: e.object, // }); // } // }, // }); } else if (eventKey == "mousemove") { layer.setState({ onHover: (e: any) => { if (e.coordinate) { const lng = e.coordinate[0]; const lat = e.coordinate[1]; handler({ lnglat: { lng, lat }, feature: e.object, }); } }, }); } else if (eventKey == "dbclick") { // layer.updateState({ // onClick: (e: any) => { // if (e.coordinate) { // const lng = e.coordinate[0]; // const lat = e.coordinate[1]; // handler({ // lnglat: { lng, lat }, // feature: e.object, // }); // } // }, // }); } }, off: (layer: IHPaaSLayer, eventKey: IHPaaSLayerEventKeys, handler: (e: MouseHandler) => void) => {}, }; fitBounds(b: BBox, animate?: boolean) { const viewport = this.hpaas.deckgl.getViewports()[0]; const { longitude, latitude, zoom } = viewport.fitBounds([ [b[0], b[1]], [b[2], b[3]], ]); // Zoom to the object this.hpaas.deckgl.setProps({ initialViewState: { longitude, latitude, zoom, transitionDuration: 500, transitionInterpolator: new FlyToInterpolator(), }, }); } popup = { html: (html: string) => { this.tooltip.html = html; }, pos: (pos: { lng: number; lat: number }) => {}, show: (html?: string, pos?: { lng: number; lat: number }) => { html && (this.tooltip.html = html); this.tooltip.show = true; }, hide: () => { this.tooltip.show = false; }, }; }