control/elevationProfile/ElevationProfileControl.tstypescript
import { Control, type IControlParams } from "../Control";
import { Dialog } from "../../ui/Dialog";
import { View } from "../../ui/View";
import { ToggleButton } from "../../ui/ToggleButton";
import { ElevationProfileView } from "./ElevationProfileView";
import { ElevationProfileScene } from "./ElevationProfileScene";
import { throttle } from "../../utils/shared";
import { ElevationProfileButtonsView } from "./ElevationProfileButtonsView";
import { PointListDialog } from "./PointListDialog";
import type { GroundItem, TrackItem } from "./ElevationProfile";
import { ElevationProfileLegend } from "./ElevationProfileLegend";
const TEMPLATE = `<div class="og-elevationprofile__container">
<div class="og-elevationprofile__menu"></div>
<div class="og-elevationprofile__graph"></div>
</div>`;
interface IElevationProfileGraphParams extends IControlParams {}
const ICON_BUTTON_SVG = `<svg style="width: 2em; height: 2em;" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M128 896v-158.293333l331.946667-191.573334 160.853333 93.866667L896 480V896H128M896 381.44l-275.2 159.146667-160.853333-92.586667L128 640v-94.293333l331.946667-191.573334 160.853333 93.866667L896 288v93.44z" fill="" /></svg>`;
export class ElevationProfileControl extends Control {
protected _toggleBtn: ToggleButton;
protected _dialog: Dialog<null>;
protected _graphView: View<null>;
protected _poiListDialog: PointListDialog;
protected _elevationProfileView: ElevationProfileView;
protected _elevationProfileScene: ElevationProfileScene;
protected _elevationProfileButtonsView: ElevationProfileButtonsView;
protected _elevationProfileLegend: ElevationProfileLegend;
protected _collectProfileThrottled: () => void;
constructor(options: IElevationProfileGraphParams = {}) {
super({
name: "ElevationProfileControl",
...options
});
this._elevationProfileScene = new ElevationProfileScene();
this._elevationProfileView = new ElevationProfileView();
this._elevationProfileLegend = new ElevationProfileLegend();
this._elevationProfileButtonsView = new ElevationProfileButtonsView({
model: this._elevationProfileView.model
});
this._elevationProfileView.events.on("pointer", this._onElevationProfilePointer);
this._elevationProfileView.events.on("dblclick", this._onElevationProfileDblClick);
this._elevationProfileView.events.on("mouseenter", this._onElevationProfileMouseEnter);
this._elevationProfileView.events.on("mouseleave", this._onElevationProfileMouseLeave);
this._dialog = new Dialog({
title: "Elevation Profile",
visible: false,
resizable: true,
useHide: true,
top: 175,
left: 65,
width: 400,
height: 200,
minHeight: 100,
minWidth: 100
});
this._graphView = new View({
template: TEMPLATE
});
this._poiListDialog = new PointListDialog({
model: this._elevationProfileScene
});
this._toggleBtn = new ToggleButton({
classList: ["og-map-button", "og-elevationprofile_button"],
icon: ICON_BUTTON_SVG
});
this._collectProfileThrottled = throttle(() => {
let points = this._elevationProfileScene.getPointsLonLat();
this._elevationProfileView.model.collectProfile(points);
}, 250);
}
override oninit() {
this._dialog.appendTo(this.planet!.renderer!.getUIContainer());
this._graphView.appendTo(this._dialog.container!);
this._toggleBtn.appendTo(this.renderer!.topLeftContainer());
this._dialog.events.on("visibility", (v: boolean) => {
this._toggleBtn.setActive(v);
if (v) {
this._dialog.positionNearElementOnFirstOpen(this._toggleBtn.el, this.renderer!.getUIContainer());
this.activate();
this._elevationProfileView.resize();
} else {
this.deactivate();
}
});
this._toggleBtn.events.on("change", (isActive: boolean) => {
this._dialog.setVisibility(isActive);
});
this._elevationProfileView.appendTo(this._graphView.select(".og-elevationprofile__graph")!);
this._elevationProfileView.model.bindPlanet(this.planet!);
this._elevationProfileView.model.events.on("clear", () => {
this._elevationProfileScene.clear();
this._elevationProfileLegend.clear();
});
this._elevationProfileView.model.events.on("startcollecting", () => {
this._elevationProfileScene.setPointerVisibility(false);
});
this._elevationProfileView.events.on("tracklength", (length: number) => {
this._elevationProfileLegend.setTrackLength(length);
});
this._elevationProfileView.events.on("groundlength", (length: number) => {
this._elevationProfileLegend.setGroundLength(length);
});
this._elevationProfileView.events.on("warninglength", (length: number) => {
this._elevationProfileLegend.setWarningLength(length);
});
this._elevationProfileView.events.on("collisionlength", (length: number) => {
this._elevationProfileLegend.setCollisionLength(length);
});
this._poiListDialog.appendTo(this.planet!.renderer!.getUIContainer());
this._poiListDialog.events.on("visibility", (isVisible: boolean) => {
this._elevationProfileButtonsView.pointListBtn.setActive(isVisible, true);
});
this._elevationProfileLegend.appendTo(this._graphView.select(".og-elevationprofile__menu")!);
this._elevationProfileButtonsView.appendTo(this._graphView.select(".og-elevationprofile__menu")!);
this._elevationProfileButtonsView.events.on("list", (isActive: boolean) => {
this._poiListDialog.setVisibility(isActive);
});
this._elevationProfileButtonsView.events.on("location", (isActive: boolean) => {
this._elevationProfileScene.flyExtent();
});
this._elevationProfileButtonsView.events.on("reset", (isActive: boolean) => {
this._elevationProfileScene.setPointerVisibility(false);
});
this._elevationProfileScene.events.on("change", this._onSceneChange);
}
protected _onSceneChange = () => {
this._collectProfileThrottled();
};
override onactivate() {
this.planet && this._elevationProfileScene.bindPlanet(this.planet);
this.renderer && this.renderer.addScene(this._elevationProfileScene);
}
override ondeactivate() {
this._poiListDialog.setVisibility(false);
this._elevationProfileView.model.clear();
this.renderer && this.renderer.removeNode(this._elevationProfileScene);
this._dialog.hide();
}
protected _onElevationProfilePointer = (
pointerDistance: number,
tp0: TrackItem,
tp1: TrackItem,
gp0: GroundItem,
gp1: GroundItem,
trackPoiIndex: number,
groundPoiIndex: number,
elevation: number
) => {
let lonLat0 = this._elevationProfileScene.getPointLonLat(trackPoiIndex)!;
let lonLat1 = this._elevationProfileScene.getPointLonLat(trackPoiIndex + 1)!;
let cart0 = this.planet!.ellipsoid.lonLatToCartesian(lonLat0),
cart1 = this.planet!.ellipsoid.lonLatToCartesian(lonLat1);
let d = (pointerDistance - tp0[0]) / (tp1[0] - tp0[0]);
let dir = cart1.sub(cart0);
this._elevationProfileScene.setPointerCartesian3v(cart0.add(dir.scale(d)), elevation);
};
protected _onElevationProfileDblClick = (
pointerDistance: number,
tp0: TrackItem,
tp1: TrackItem,
gp0: GroundItem,
gp1: GroundItem,
trackPoiIndex: number,
groundPoiIndex: number,
elevation: number
) => {
let lonLat0 = this._elevationProfileScene.getPointLonLat(trackPoiIndex)!;
let lonLat1 = this._elevationProfileScene.getPointLonLat(trackPoiIndex + 1)!;
let cart0 = this.planet!.ellipsoid.lonLatToCartesian(lonLat0),
cart1 = this.planet!.ellipsoid.lonLatToCartesian(lonLat1);
let d = (pointerDistance - tp0[0]) / (tp1[0] - tp0[0]);
let dir = cart1.sub(cart0);
let poi = cart0.add(dir.scale(d));
this.planet!.camera.flyDistance(poi, this.planet!.camera.eye.distance(poi));
};
protected _onElevationProfileMouseEnter = () => {
if (this._elevationProfileView.model.pointsReady) {
this._elevationProfileScene.setPointerVisibility(true);
}
};
protected _onElevationProfileMouseLeave = () => {};
}