UNPKG

@itwin/measure-tools-react

Version:
366 lines 15.6 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { Geometry, IModelJson, Point3d, PointString3d, } from "@itwin/core-geometry"; import { Cartographic } from "@itwin/core-common"; import { GraphicType } from "@itwin/core-frontend"; import { FormatterUtils } from "../api/FormatterUtils.js"; import { StyleSet, WellKnownGraphicStyleType, WellKnownTextStyleType, } from "../api/GraphicStyle.js"; import { Measurement, MeasurementPickContext, MeasurementSerializer, } from "../api/Measurement.js"; import { WellKnownViewType } from "../api/MeasurementEnums.js"; import { MeasurementPreferences } from "../api/MeasurementPreferences.js"; import { MeasurementPropertyHelper } from "../api/MeasurementPropertyHelper.js"; import { MeasurementSelectionSet } from "../api/MeasurementSelectionSet.js"; import { TextMarker } from "../api/TextMarker.js"; import { MeasureTools } from "../MeasureTools.js"; /** Serializer for a [[LocationMeasurement]]. */ export class LocationMeasurementSerializer extends MeasurementSerializer { get measurementName() { return LocationMeasurementSerializer.locationMeasurementName; } isValidType(measurement) { return measurement instanceof LocationMeasurement; } isValidJSON(json) { if (!super.isValidJSON(json) || !json.hasOwnProperty("location")) return false; return true; } parseSingle(data) { if (!this.isValidJSON(data)) return undefined; const props = data; return LocationMeasurement.fromJSON(props); } } LocationMeasurementSerializer.locationMeasurementName = "locationMeasurement"; /** * Location measurement. A point somewhere in the world, optionally with other values (such as slope, station, offset, etc). */ export class LocationMeasurement extends Measurement { get location() { return this._location; } set location(pt) { this._location.setFrom(pt); this.createTextMarker().catch(); // eslint-disable-line @typescript-eslint/no-floating-promises } get geoLocation() { return this._geoLocation; } set geoLocation(geoLoc) { this._geoLocation = geoLoc; } get slope() { return this._slope; } set slope(slope) { this._slope = slope; } get station() { return this._station; } set station(station) { this._station = station; } get offset() { return this._offset; } set offset(offset) { this._offset = offset; } get isDynamic() { return this._isDynamic; } set isDynamic(v) { this._isDynamic = v; if (this._textMarker) this._textMarker.pickable = !v; } constructor(props) { super(props); this._location = Point3d.createZero(); this._isDynamic = false; this.getSnapId(); // Preload transient ID"s since we normally don not have these as dynamic if (props) { this.readFromJSON(props); } else { this.createTextMarker().catch(); // eslint-disable-line @typescript-eslint/no-floating-promises } } /** Changes the location. Only possible if the measurement is dynamic. */ changeLocation(props) { if (!this.isDynamic) return false; this.readFromJSON(props); return true; } testDecorationHit(pickContext) { if (this.transientId && this.transientId === pickContext.geomId) return true; if (pickContext.buttonEvent && this._textMarker && this.displayLabels) return this._textMarker.pick(pickContext.buttonEvent.viewPoint); return false; } getDecorationGeometry(_pickContext) { return [ IModelJson.Writer.toIModelJson(PointString3d.create(this.location)), ]; } async getDecorationToolTip(_pickContext) { return MeasureTools.localization.getLocalizedString("MeasureTools:Measurements.locationMeasurement"); } getSnapId() { if (!this.transientId) this.transientId = MeasurementSelectionSet.nextTransientId; if (this.isDynamic) return undefined; return this.transientId; } onTransientIdChanged(_prevId) { if (this._textMarker) this._textMarker.transientHiliteId = this.transientId; } decorate(context) { super.decorate(context); const styleTheme = StyleSet.getOrDefault(this.activeStyle); const style = styleTheme.getGraphicStyle(WellKnownGraphicStyleType.LocationMeasurement); if (this._textMarker && this.displayLabels) this._textMarker.addDecoration(context); if (this.isDynamic) return; const xBuilder = context.createGraphicBuilder(GraphicType.WorldDecoration, undefined, this.getSnapId()); style.addStyledPointString(xBuilder, [this._location], false); context.addDecorationFromBuilder(xBuilder); } async createTextMarker() { const adjustedLocation = this.adjustPointWithSheetToWorldTransform(this.adjustPointForGlobalOrigin(this._location)); const entries = [ { label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.coordinate_x"), value: await FormatterUtils.formatLength(adjustedLocation.x), }, { label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.coordinate_y"), value: await FormatterUtils.formatLength(adjustedLocation.y), }, { label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.coordinate_z"), value: await FormatterUtils.formatLength(adjustedLocation.z), }, ]; if (this._isDynamic) { entries.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.slope"), value: this._slope === undefined ? "" : FormatterUtils.formatSlope(100 * this._slope, false), }); } if (!this._textMarker) { const styleTheme = StyleSet.getOrDefault(this.activeStyle); this._textMarker = TextMarker.createHoverBox(entries, this._location, styleTheme); this._textMarker.pickable = !this.isDynamic; this._textMarker.transientHiliteId = this.transientId; this._textMarker.setMouseButtonHandler(this.handleTextMarkerButtonEvent.bind(this)); } else { this._textMarker.pickable = !this.isDynamic; this._textMarker.worldLocation = this._location; this._textMarker.textLines = entries; } } async getDataForMeasurementWidgetInternal() { const adjustedLocation = this.adjustPointWithSheetToWorldTransform(this.adjustPointForGlobalOrigin(this._location)); const fCoordinates = await FormatterUtils.formatCoordinates(adjustedLocation); let title = MeasureTools.localization.getLocalizedString("MeasureTools:Measurements.locationMeasurement"); title += ` [${fCoordinates}]`; const data = { title, properties: [] }; MeasurementPropertyHelper.tryAddNameProperty(this, data.properties); data.properties.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.coordinates"), name: "LocationMeasurement_Location", value: fCoordinates, }); if (this._geoLocation && this.drawingMetadata?.sheetToWorldTransform === undefined) data.properties.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.latLong"), name: "LocationMeasurement_LatLong", value: await FormatterUtils.formatCartographicToLatLong(this._geoLocation), }); if (MeasurementPreferences.current.displayLocationAltitude) { data.properties.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.altitude"), name: "LocationMeasurement_Altitude", value: await FormatterUtils.formatLength(adjustedLocation.z), }); } if (this.drawingMetadata?.sheetToWorldTransform === undefined) { let slopeValue; if (undefined !== this._slope) slopeValue = FormatterUtils.formatSlope(100.0 * this._slope, true); else slopeValue = MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.slopeUnavailable"); data.properties.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.slope"), name: "LocationMeasurement_Slope", value: slopeValue, }); } if (undefined !== this._station) { data.properties.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.station"), name: "LocationMeasurement_Station", value: await FormatterUtils.formatStation(this._station), }); } if (undefined !== this._offset) { data.properties.push({ label: MeasureTools.localization.getLocalizedString("MeasureTools:tools.MeasureLocation.offset"), name: "LocationMeasurement_Offset", value: await FormatterUtils.formatLength(this._offset), }); } return data; } handleTextMarkerButtonEvent(ev) { if (this._isDynamic) return false; // eslint-disable-next-line @typescript-eslint/no-floating-promises this.onDecorationButtonEvent(MeasurementPickContext.createFromSourceId("Invalid", ev)).catch(); return true; } onStyleChanged(_isLock, _prevStyle) { this.updateMarkerStyle(); } onLockToggled() { this.updateMarkerStyle(); } onDisplayUnitsChanged() { this.createTextMarker().catch(); // eslint-disable-line @typescript-eslint/no-floating-promises } updateMarkerStyle() { if (!this._textMarker) return; const styleTheme = StyleSet.getOrDefault(this.activeStyle); const tStyle = styleTheme.getTextStyle(WellKnownTextStyleType.HoverBox); this._textMarker.applyStyle(tStyle); } /** * Tests equality with another measurement. * @param other Measurement to test equality for. * @param opts Options for equality testing. * @returns true if the other measurement is equal, false if some property is not the same or if the measurement is not of the same type. */ equals(other, opts) { if (!super.equals(other, opts)) return false; // Compare data (ignore isDynamic) const tol = opts && opts.tolerance !== undefined ? opts.tolerance : Geometry.smallMetricDistance; const otherLoc = other; if (otherLoc === undefined || !this._location.isAlmostEqual(otherLoc._location, tol) || !isNearlyEqual(this._offset, otherLoc._offset, tol) || !isNearlyEqual(this._slope, otherLoc._slope, tol) || !isNearlyEqual(this._station, otherLoc._station, tol)) return false; if (this._geoLocation !== undefined && otherLoc._geoLocation !== undefined) { if (!this._geoLocation.equalsEpsilon(otherLoc._geoLocation, tol)) return false; } else if (this._geoLocation !== otherLoc._geoLocation) { return false; } return true; } /** * Copies data from the other measurement into this instance. * @param other Measurement to copy property values from. */ copyFrom(other) { super.copyFrom(other); if (other instanceof LocationMeasurement) { this._isDynamic = other._isDynamic; this._location.setFrom(other._location); this._geoLocation = other._geoLocation ? other._geoLocation.clone() : undefined; this._slope = other._slope; this._offset = other._offset; this._station = other._station; this.createTextMarker().catch(); // eslint-disable-line @typescript-eslint/no-floating-promises } } /** * Deserializes properties (if they exist) from the JSON object. * @param json JSON object to read data from. */ readFromJSON(json) { super.readFromJSON(json); const jsonLoc = json; if (jsonLoc.location !== undefined) this._location.setFromJSON(jsonLoc.location); if (jsonLoc.geoLocation) this._geoLocation = Cartographic.fromRadians(jsonLoc.geoLocation); else this._geoLocation = undefined; this._slope = jsonLoc.slope; this._station = jsonLoc.station; this._offset = jsonLoc.offset; this.createTextMarker().catch(); // eslint-disable-line @typescript-eslint/no-floating-promises } /** * Serializes properties to a JSON object. * @param json JSON object to append data to. */ writeToJSON(json) { super.writeToJSON(json); const jsonLoc = json; jsonLoc.location = this._location.toJSON(); if (this._geoLocation) { const geoLoc = this._geoLocation; jsonLoc.geoLocation = { latitude: geoLoc.latitude, longitude: geoLoc.longitude, height: geoLoc.height, }; } else { jsonLoc.geoLocation = undefined; } jsonLoc.slope = this._slope; jsonLoc.station = this._station; jsonLoc.offset = this._offset; } static create(location, viewType) { // Don"t ned to serialize the points, will just work as is const measurement = new LocationMeasurement({ location }); if (viewType) measurement.viewTarget.include(viewType); return measurement; } static fromJSON(props) { const locMeasurement = new LocationMeasurement(props); // LEGACY - Originally location measurements were hardcoded for "MainOnly", so if no viewTarget/viewportType default to "Spatial". So reading this from old JSON // we don"t want location measurement"s set to Any...but we want new measurements created to be set to Any if no view types are given! if (props.viewTarget === undefined && props.viewportType === undefined) locMeasurement.viewTarget.include(WellKnownViewType.Spatial); return locMeasurement; } } LocationMeasurement.serializer = Measurement.registerSerializer(new LocationMeasurementSerializer()); function isNearlyEqual(a, b, tol) { if (a !== undefined && b !== undefined) { if (!Geometry.isSameCoordinate(a, b, tol)) return false; } else if (a !== b) { return false; } return true; } //# sourceMappingURL=LocationMeasurement.js.map