UNPKG

terriajs

Version:

Geospatial data visualization platform.

114 lines 4.3 kB
import BillboardGraphics from "terriajs-cesium/Source/DataSources/BillboardGraphics"; import CallbackProperty from "terriajs-cesium/Source/DataSources/CallbackProperty"; import CustomDataSource from "terriajs-cesium/Source/DataSources/CustomDataSource"; import Entity from "terriajs-cesium/Source/DataSources/Entity"; import MappableMixin from "../../../ModelMixins/MappableMixin"; import CreateModel from "../../../Models/Definition/CreateModel"; import MappableTraits from "../../../Traits/TraitsClasses/MappableTraits"; export default class Marker extends MappableMixin(CreateModel(MappableTraits)) { terria; iconUrl; dataSource; icon; currentRotation = 0; position; /** * @param terria Terria instance * @param iconUrl An HTML image element to use as the marker icon * @param position Initial position of the marker icon * @param rotation Initial rotation of the marker icon */ constructor(terria, iconUrl, position, rotation) { super(undefined, terria); this.terria = terria; this.iconUrl = iconUrl; this.position = position; this.dataSource = new CustomDataSource(); this.icon = new RotatableIcon(iconUrl, 24, 24); this.icon.loadPromise.then(() => this.icon.rotate(rotation)); const entity = new Entity({ billboard: new BillboardGraphics({ image: this.icon.canvas }), position: new CallbackProperty(() => this.position, false) }); this.dataSource.entities.add(entity); } /** * Set marker rotation in radians */ set rotation(rotation) { // round to 2 decimal places to minimize rotation updates const newRotation = Math.round(rotation * 100) / 100; if (this.currentRotation !== newRotation) { this.icon.rotate(newRotation); this.currentRotation = newRotation; } } async forceLoadMapItems() { } get mapItems() { return [this.dataSource]; } } /** * A dynamically rotatable icon * * This class provides a {@canvas} instance with the rotated image drawn on it. * The canvas instance can be used as an image parameter in Billboard graphics * or other entities drawn by Cesium/Leaflet instances. */ class RotatableIcon { image; ctx; // The canvas on which the icon is drawn and transformed canvas; // Resolves when the icon image is loaded and ready to be drawn loadPromise; /** * @param iconUrl The url of the icon image * @param width Optional icon width. * If given, the image will be scaled to this width * otherwise we use the image width. * @param height Optional icon height. * If given, the image will be scaled to this height * otherwise we use the image height. */ constructor(iconUrl, width, height) { this.image = new Image(); this.canvas = document.createElement("canvas"); this.ctx = this.canvas.getContext("2d") ?? undefined; this.image.src = iconUrl; this.loadPromise = new Promise((resolve) => { this.image.addEventListener("load", () => { this.canvas.width = width ?? this.image.width; this.canvas.height = height ?? this.image.height; resolve(); }); }); } /** * Rotate the icon by the given angle * * @param rotation Angle in radians */ rotate(rotation) { if (this.ctx === undefined || this.image.complete === false) { return; } const image = this.image; const ctx = this.ctx; const canvas = this.canvas; ctx.save(); ctx.clearRect(0, 0, canvas.width, canvas.height); // translate so that we rotate about the image center ctx.translate(canvas.width / 2, canvas.height / 2); ctx.rotate(rotation); ctx.drawImage(image, // x & y coordinate to draw the image relative to the translated center -canvas.width / 2, -canvas.height / 2, // scales the image to fit the canvas width & height canvas.width, canvas.height); ctx.restore(); } } //# sourceMappingURL=Marker.js.map