import { HTMLWidget } from "@hpcc-js/common";
import { Deck, mapboxgl } from "@hpcc-js/deck-shim";
import { hashSum } from "@hpcc-js/util";
import "d3-transition";
declare const window: any;
export interface ViewState {
width?: number;
height?: number;
latitude?: number;
longitude?: number;
zoom?: number;
bearing?: number;
pitch?: number;
}
export class Common extends HTMLWidget {
protected _mapgl: mapboxgl.Map;
protected _deckgl: Deck;
protected _div: any;
protected _divMapbox: any;
protected _canvasDeck: any;
protected _layers: any[] = [];
private _prevShowBuildings: boolean;
constructor() {
super();
}
viewState(): ViewState;
viewState(_: ViewState): this;
viewState(_?: ViewState): ViewState | this {
if (_ === void 0) return {
latitude: this.latitude(),
longitude: this.longitude(),
zoom: this.zoom(),
bearing: this.bearing(),
pitch: this.pitch()
};
// Dont set width and height ---
this.latitude(_.latitude);
this.longitude(_.longitude);
this.zoom(_.zoom);
this.bearing(_.bearing);
this.pitch(_.pitch);
return this;
}
getBounds() {
return this._mapgl.getBounds();
}
syncMapbox(viewState: ViewState) {
this._mapgl.jumpTo({
center: [viewState.longitude, viewState.latitude],
zoom: viewState.zoom,
bearing: viewState.bearing,
pitch: viewState.pitch
});
}
layers(): any[] {
return [];
}
enter(domNode, element) {
super.enter(domNode, element);
const context = this;
this._div = element.append("div")
.style("width", this.width() + "px")
.style("height", this.height() + "px")
.style("opacity", 0)
;
this._divMapbox = this._div.append("div")
.style("position", "absolute")
.style("top", 0)
.style("left", 0)
.style("width", "100%")
.style("height", "100%")
;
this._canvasDeck = this._div.append("canvas")
.attr("id", "deck-canvas")
.style("position", "absolute")
.style("top", 0)
.style("left", 0)
.style("width", "100%")
.style("height", "100%")
;
// Default key should be in sync with packages/map/src/leaflet/MapBox.ts
if (!window.__hpcc_mapbox_apikey) {
console.warn("__hpcc_mapbox_apikey does not contain a valid API key, reverting to developers key (expect limited performance)");
}
mapboxgl.accessToken = mapboxgl.accessToken || window.__hpcc_mapbox_apikey || "pk.eyJ1IjoibGVzY2htb28iLCJhIjoiY2psY2FqY3l3MDhqNDN3cDl1MzFmZnkwcCJ9.HRoFwmz1j80gyz18ruggqw";
this._mapgl = new mapboxgl.Map({
container: this._divMapbox.node(),
interactive: false,
center: [this.longitude(), this.latitude()],
zoom: this.zoom(),
bearing: this.bearing(),
pitch: this.pitch()
});
this._mapgl.on("load", () => {
this._div.transition().style("opacity", 1);
});
this._deckgl = new Deck({
canvas: "deck-canvas",
width: "100%",
height: "100%",
initialViewState: { ...this.viewState() },
controller: true,
onLoad: () => {
},
onViewStateChange: ({ viewState }) => {
this._prevViewStateHash = hashSum(viewState);
this.viewState(viewState);
this.syncMapbox(viewState);
}
});
this._prevShowBuildings = this.showBuildings();
this._mapgl.on("load", function () {
if (context.showBuildings()) {
context.add3dBuildingLayer();
}
});
}
add3dBuildingLayer() {
const layers = this._mapgl.getStyle().layers;
let labelLayerId;
for (let i = 0; i < layers.length; i++) {
if (layers[i].type === "symbol" && layers[i].layout["text-field"]) {
labelLayerId = layers[i].id;
break;
}
}
this._mapgl.addLayer({
"id": "3d-buildings",
"source": "composite",
"source-layer": "building",
"filter": ["==", "extrude", "true"],
"type": "fill-extrusion",
"minzoom": 15,
"paint": {
"fill-extrusion-color": "#aaa",
"fill-extrusion-height": [
"interpolate", ["linear"], ["zoom"],
15, 0,
15.05, ["get", "height"]
],
"fill-extrusion-base": [
"interpolate", ["linear"], ["zoom"],
15, 0,
15.05, ["get", "min_height"]
],
"fill-extrusion-opacity": .6
}
}, labelLayerId);
}
private _prevStyle;
private _prevViewStateHash;
update(domNode, element) {
super.update(domNode, element);
this._div
.style("width", this.width() + "px")
.style("height", this.height() + "px")
;
this._mapgl.resize();
if (this._prevStyle !== this.style()) {
this._prevStyle = this.style();
this._mapgl.setStyle(`mapbox://styles/mapbox/${this._prevStyle}?optimize=true`);
}
if (this._prevShowBuildings !== this.showBuildings()) {
if (this.showBuildings()) {
this.add3dBuildingLayer();
} else {
this._mapgl.removeLayer("3d-buildings");
}
this._prevShowBuildings = this.showBuildings();
}
const viewState = this.viewState();
const viewStateHash = hashSum(viewState);
if (this._prevViewStateHash !== viewStateHash) {
this._prevViewStateHash = viewStateHash;
this._deckgl.setProps({
viewState: { ...viewState }
});
this.syncMapbox(viewState);
}
this._deckgl.setProps({
layers: this.layers()
});
}
exit(domNode, element) {
super.exit(domNode, element);
if (this._deckgl) {
this._deckgl.finalize();
}
}
}
Common.prototype._class += " map-deck_Common";
export interface Common {
style(): string;
style(_: string): this;
latitude(): number;
latitude(_: number): this;
longitude(): number;
longitude(_: number): this;
zoom(): number;
zoom(_: number): this;
bearing(): number;
bearing(_: number): this;
pitch(): number;
pitch(_: number): this;
showBuildings(): boolean;
showBuildings(_: boolean): this;
}
Common.prototype.publish("style", "light-v9", "set", "Map Style", ["streets-v10", "outdoors-v10", "light-v9", "dark-v9", "satellite-v9", "satellite-streets-v10", "navigation-preview-day-v2", "navigation-preview-night-v2", "navigation-guidance-day-v2", "navigation-guidance-night-v2"]);
Common.prototype.publish("latitude", 37.1, "number", "Latitude");
Common.prototype.publish("longitude", -95.7, "number", "Latitude");
Common.prototype.publish("zoom", 2, "number", "Latitude");
Common.prototype.publish("bearing", 0, "number", "Latitude");
Common.prototype.publish("pitch", 30, "number", "Latitude");
Common.prototype.publish("showBuildings", true, "boolean", "If true, 3d buildings will display when zoomed in");