first commit
This commit is contained in:
commit
711fa06faa
24 files changed
+3016
No files matched your search
+312
@@ -0,0 +1,312 @@
|
||||
import {
|
||||
Cartesian3,
|
||||
Cartographic,
|
||||
Camera,
|
||||
Color,
|
||||
CallbackProperty,
|
||||
HeightReference,
|
||||
LabelStyle,
|
||||
VerticalOrigin,
|
||||
PerspectiveFrustum,
|
||||
DebugCameraPrimitive,
|
||||
Entity,
|
||||
Cartesian2,
|
||||
Math as cesiumMath
|
||||
} from 'cesium'
|
||||
|
||||
import { Wayline } from './wayline'
|
||||
import { viewer } from './basemap'
|
||||
import * as egm96 from 'egm96-universal'
|
||||
|
||||
export class WayPoint {
|
||||
position: Cartesian3
|
||||
wayline: Wayline
|
||||
index: number
|
||||
asl: number
|
||||
alt: number
|
||||
fov: number
|
||||
selected!: boolean
|
||||
heightLabel!: Entity
|
||||
wayPointEntity!: Entity
|
||||
orientation: { heading: number; pitch: number; roll: number }
|
||||
debugCameraPrimitive: any
|
||||
distanceLabel1: any
|
||||
distanceLabel2: any
|
||||
|
||||
constructor(position: Cartesian3, wayline: Wayline, index: number) {
|
||||
this.position = position
|
||||
this.wayline = wayline
|
||||
this.index = index
|
||||
|
||||
const cartographic = Cartographic.fromCartesian(position)
|
||||
this.alt = cartographic.height
|
||||
const height = egm96.meanSeaLevel(cesiumMath.toDegrees(cartographic.latitude), cesiumMath.toDegrees(cartographic.longitude))
|
||||
this.asl = this.alt - height
|
||||
|
||||
this.createWaypointEntity()
|
||||
|
||||
const minimapViewer = this.wayline.minimapViewer
|
||||
this.fov = minimapViewer.camera.frustum.fov
|
||||
this.orientation = {
|
||||
heading: minimapViewer.camera.heading,
|
||||
pitch: minimapViewer.camera.pitch,
|
||||
roll: minimapViewer.camera.roll
|
||||
}
|
||||
}
|
||||
|
||||
private createWaypointEntity(): void {
|
||||
const { position, index } = this
|
||||
|
||||
const url = generateNumberSvgDataUrl(index + 1)
|
||||
|
||||
this.wayPointEntity = viewer.entities.add({
|
||||
position,
|
||||
wayPoint: this,
|
||||
point: {
|
||||
pixelSize: 10,
|
||||
color: Color.RED,
|
||||
heightReference: HeightReference.NONE
|
||||
},
|
||||
polyline: {
|
||||
positions: new CallbackProperty(() => {
|
||||
const position = this.position
|
||||
const cartographic = Cartographic.fromCartesian(position)
|
||||
const bottom = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0)
|
||||
|
||||
return [position, bottom]
|
||||
}, false),
|
||||
width: 2,
|
||||
material: Color.WHITE
|
||||
},
|
||||
billboard: {
|
||||
image: url,
|
||||
scale: 1.0,
|
||||
verticalOrigin: VerticalOrigin.BOTTOM,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
updatePosition(position: Cartesian3): void {
|
||||
const pointList = this.wayline.pointList
|
||||
this.position = position
|
||||
this.wayPointEntity.position = position
|
||||
this.heightLabel.position = position
|
||||
this.debugCameraPrimitive._camera.position = position
|
||||
|
||||
const text = `ASL: ${this.asl.toFixed(1)} m\nHAE: ${this.alt.toFixed(1)} m`
|
||||
this.heightLabel.label.text = text
|
||||
|
||||
const index = this.index
|
||||
const prev = pointList[index - 1]
|
||||
const next = pointList[index + 1]
|
||||
|
||||
if (prev) {
|
||||
const distance = Cartesian3.distance(
|
||||
prev.position,
|
||||
position
|
||||
)
|
||||
const midpoint = Cartesian3.lerp(prev.position, position, 0.5, new Cartesian3())
|
||||
this.distanceLabel1.position = midpoint
|
||||
this.distanceLabel1.label.text = distance.toFixed(1) + 'm'
|
||||
}
|
||||
|
||||
if (next) {
|
||||
const distance = Cartesian3.distance(
|
||||
position,
|
||||
next.position
|
||||
)
|
||||
const midpoint = Cartesian3.lerp(position, next.position, 0.5, new Cartesian3())
|
||||
this.distanceLabel2.position = midpoint
|
||||
this.distanceLabel2.label.text = distance.toFixed(1) + 'm'
|
||||
}
|
||||
}
|
||||
|
||||
select(): void {
|
||||
this.selected = true
|
||||
this.wayPointEntity.billboard.image = generateHighlightedNumberSvgDataUrl(this.index + 1)
|
||||
|
||||
this.showDistanceLabels()
|
||||
this.showHeightLabel()
|
||||
|
||||
const camera = new Camera(viewer.scene)
|
||||
camera.frustum = new PerspectiveFrustum({
|
||||
fov: this.fov,
|
||||
aspectRatio: 4 / 3,
|
||||
near: 1,
|
||||
far: 100
|
||||
})
|
||||
|
||||
camera.setView({
|
||||
destination: this.position,
|
||||
orientation: this.orientation
|
||||
})
|
||||
|
||||
const minimapViewer = this.wayline.minimapViewer
|
||||
minimapViewer.camera.setView({
|
||||
destination: this.position,
|
||||
orientation: this.orientation
|
||||
})
|
||||
|
||||
minimapViewer.camera.frustum.fov = this.fov
|
||||
this.debugCameraPrimitive = new DebugCameraPrimitive({
|
||||
camera,
|
||||
color: Color.YELLOW
|
||||
})
|
||||
|
||||
viewer.scene.primitives.add(this.debugCameraPrimitive)
|
||||
}
|
||||
|
||||
unselect(): void {
|
||||
this.selected = false
|
||||
this.wayPointEntity.billboard.image = generateNumberSvgDataUrl(this.index + 1)
|
||||
|
||||
viewer.entities.remove(this.distanceLabel0)
|
||||
viewer.entities.remove(this.distanceLabel1)
|
||||
viewer.entities.remove(this.distanceLabel2)
|
||||
viewer.entities.remove(this.heightLabel)
|
||||
viewer.scene.primitives.remove(this.debugCameraPrimitive)
|
||||
}
|
||||
distanceLabel0(distanceLabel0: any) {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
delete(): void {
|
||||
const pointList = this.wayline.pointList
|
||||
|
||||
viewer.entities.remove(this.wayPointEntity)
|
||||
viewer.scene.primitives.remove(this.debugCameraPrimitive)
|
||||
|
||||
pointList.splice(this.index, 1)
|
||||
for (let i = this.index; i < pointList.length; i++) {
|
||||
pointList[i].wayPointEntity.billboard.image = generateNumberSvgDataUrl(i + 1)
|
||||
pointList[i].index = i
|
||||
}
|
||||
}
|
||||
|
||||
private showHeightLabel(): void {
|
||||
const { position, alt, asl } = this
|
||||
const text = `ASL: ${asl.toFixed(1)} m\nHAE: ${alt.toFixed(1)} m`
|
||||
|
||||
this.heightLabel = viewer.entities.add({
|
||||
position,
|
||||
label: {
|
||||
text,
|
||||
font: "14px sans-serif",
|
||||
fillColor: Color.WHITE,
|
||||
outlineColor: Color.BLACK,
|
||||
outlineWidth: 2,
|
||||
style: LabelStyle.FILL_AND_OUTLINE,
|
||||
verticalOrigin: VerticalOrigin.BOTTOM,
|
||||
heightReference: HeightReference.NONE,
|
||||
showBackground: true,
|
||||
backgroundColor: Color.BLACK.withAlpha(0.5),
|
||||
pixelOffset: new Cartesian2(0, -30)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private showDistanceLabels(): void {
|
||||
const index = this.index
|
||||
const pointList = this.wayline.pointList
|
||||
|
||||
const current = pointList[index]
|
||||
const prev = pointList[index - 1]
|
||||
const next = pointList[index + 1]
|
||||
|
||||
if (prev) {
|
||||
this.distanceLabel1 = this.createMidpointLabel(prev.position, current.position)
|
||||
} else {
|
||||
this.distanceLabel0 = this.createMidpointLabel(this.wayline.takeOffPoint, this.wayline.top)
|
||||
this.distanceLabel1 = this.createMidpointLabel(this.wayline.top, current.position)
|
||||
}
|
||||
|
||||
if (next) {
|
||||
this.distanceLabel2 = this.createMidpointLabel(current.position, next.position)
|
||||
}
|
||||
}
|
||||
|
||||
private createMidpointLabel(point1: Cartesian3, point2: Cartesian3): Entity {
|
||||
const midpoint = Cartesian3.lerp(point1, point2, 0.5, new Cartesian3())
|
||||
const distance = Cartesian3.distance(
|
||||
point1,
|
||||
point2
|
||||
)
|
||||
|
||||
return viewer.entities.add({
|
||||
position: midpoint,
|
||||
label: {
|
||||
text: `${distance.toFixed(1)}m`,
|
||||
font: "14px sans-serif",
|
||||
fillColor: Color.WHITE,
|
||||
outlineColor: Color.BLACK,
|
||||
outlineWidth: 2,
|
||||
style: LabelStyle.FILL_AND_OUTLINE,
|
||||
verticalOrigin: VerticalOrigin.BOTTOM,
|
||||
heightReference: HeightReference.NONE,
|
||||
showBackground: true,
|
||||
backgroundColor: Color.BLACK.withAlpha(0.5),
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
eyeOffset: new Cartesian3(0.0, 0.0, -10.0),
|
||||
pixelOffset: new Cartesian2(0, -10)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
viewer.entities.remove(this.wayPointEntity)
|
||||
}
|
||||
}
|
||||
|
||||
function generateNumberSvgDataUrl(number: number): string {
|
||||
const svg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 26" width="32" height="26">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#00D690" d="M16.8320503 24.75192456L30.9635332 3.5547002c.3063525-.4595287.1821786-1.080398-.2773501-1.3867505C30.5219156 2.058438 30.3289079 2 30.1314829 2H1.86851709c-.55228475 0-1 .4477153-1 1 0 .197425.05843803.3904327.16794971.5547002l14.1314829 21.19722436c.3063525.45952869.9272218.58370256 1.3867505.2773501.1098523-.07323486.2041152-.16749781.2773501-.2773501z"/>
|
||||
<text fill="#FFF" font-size="16" font-weight="500">
|
||||
<tspan x="50%" y="50%" dy=".25em" text-anchor="middle">${number}</tspan>
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
`
|
||||
|
||||
const encoded = encodeURIComponent(svg)
|
||||
.replace(/'/g, '%27')
|
||||
.replace(/"/g, '%22')
|
||||
|
||||
return `data:image/svg+xml,${encoded}`
|
||||
}
|
||||
|
||||
function generateHighlightedNumberSvgDataUrl(number: number): string {
|
||||
const svg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 42 35" width="42" height="35">
|
||||
<defs>
|
||||
<filter id="a" width="150.8%" height="171.7%" x="-25.4%" y="-34.2%" filterUnits="objectBoundingBox">
|
||||
<feMorphology in="SourceAlpha" operator="dilate" radius="1" result="shadowSpreadOuter1"/>
|
||||
<feOffset in="shadowSpreadOuter1" result="shadowOffsetOuter1"/>
|
||||
<feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="2"/>
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"/>
|
||||
<feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0.039215686 0 0 0 0 0.933333333 0 0 0 0 0.545098039 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<path id="b" d="M16.832 3.248l14.132 21.197A1 1 0 0130.13 26H1.87a1 1 0 01-.833-1.555L15.168 3.248a1 1 0 011.664 0z"/>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g transform="matrix(1 0 0 -1 5 32)">
|
||||
<use fill="#000" filter="url(#a)" xlink:href="#b"/>
|
||||
<path fill="#00D690" stroke="#FFF" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15.955 3.871L30.13 25H1.87L15.955 3.871z"/>
|
||||
</g>
|
||||
<text fill="#FFF" font-size="14" font-weight="500">
|
||||
<tspan x="50%" y="50%" dy=".1em" text-anchor="middle">${number}</tspan>
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
`
|
||||
|
||||
const encoded = encodeURIComponent(svg)
|
||||
.replace(/'/g, '%27')
|
||||
.replace(/"/g, '%22')
|
||||
|
||||
return `data:image/svg+xml,${encoded}`
|
||||
}
|
||||
Reference in new issue
Block a user