feat(foundry): render geozones as independent map layer
This commit is contained in:
@@ -26,6 +26,8 @@ import {
|
||||
Matrix4,
|
||||
Math as CesiumMath,
|
||||
PointGraphics,
|
||||
PolygonGraphics,
|
||||
PolygonHierarchy,
|
||||
PolylineGraphics,
|
||||
Resource,
|
||||
sampleTerrainMostDetailed,
|
||||
@@ -331,7 +333,7 @@ function syncRuntimeDataSources(
|
||||
presentationFilters: MapPresentationFilters,
|
||||
) {
|
||||
const activeBindings = new Map(bindings
|
||||
.filter((binding) => binding.slotId === "points")
|
||||
.filter((binding) => binding.slotId === "points" || binding.slotId === "zones")
|
||||
.map((binding) => [binding.bindingId, binding]));
|
||||
|
||||
for (const [bindingId, dataSource] of dataSources) {
|
||||
@@ -359,17 +361,48 @@ function syncRuntimeDataSources(
|
||||
!fact.geometry
|
||||
|| (profile && !mapRuntimeFactIsVisible(fact, profile, presentationFilters, binding.bindingId))
|
||||
) continue;
|
||||
const entityId = mapRuntimeEntityId(binding.bindingId, fact);
|
||||
wanted.add(entityId);
|
||||
const [longitude, latitude] = fact.geometry.coordinates;
|
||||
const resolvedStyle = profile ? resolveMapPresentationStyle(profile, presentationClass) : undefined;
|
||||
const color = resolvedStyle
|
||||
? Color.fromCssColorString(resolvedStyle.color).withAlpha(resolvedStyle.opacity)
|
||||
: runtimePointColor(fact);
|
||||
const label = mapRuntimeDisplayLabel(fact, profile);
|
||||
const entity = dataSource.entities.getById(entityId) ?? dataSource.entities.add({ id: entityId });
|
||||
entity.name = label;
|
||||
if (profile) {
|
||||
const baseEntityId = mapRuntimeEntityId(binding.bindingId, fact);
|
||||
|
||||
if (fact.geometry.type === "Point" && binding.slotId === "points") {
|
||||
if (profile && profile.target.variant !== "elevated-spike") continue;
|
||||
const entityId = baseEntityId;
|
||||
wanted.add(entityId);
|
||||
const [longitude, latitude] = fact.geometry.coordinates;
|
||||
const entity = dataSource.entities.getById(entityId) ?? dataSource.entities.add({ id: entityId });
|
||||
entity.name = label;
|
||||
entity.polygon = undefined;
|
||||
if (!profile) {
|
||||
entity.position = new ConstantPositionProperty(Cartesian3.fromDegrees(longitude, latitude, 0));
|
||||
entity.polyline = undefined;
|
||||
entity.point = new PointGraphics({
|
||||
pixelSize: 10,
|
||||
color,
|
||||
outlineColor: Color.fromCssColorString("#0c0d12").withAlpha(0.72),
|
||||
outlineWidth: 2,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
});
|
||||
entity.label = new LabelGraphics({
|
||||
text: label,
|
||||
font: "700 13px Arial",
|
||||
fillColor: Color.WHITE,
|
||||
showBackground: true,
|
||||
backgroundColor: Color.BLACK.withAlpha(0.72),
|
||||
backgroundPadding: new Cartesian2(10, 7),
|
||||
pixelOffset: new Cartesian2(10, 0),
|
||||
horizontalOrigin: HorizontalOrigin.LEFT,
|
||||
verticalOrigin: VerticalOrigin.BOTTOM,
|
||||
heightReference: HeightReference.NONE,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (profile.target.variant !== "elevated-spike") continue;
|
||||
const target = profile.target;
|
||||
const fallbackHeightMeters = typeof fact.attributes.elevation_meters === "number" && Number.isFinite(fact.attributes.elevation_meters)
|
||||
? fact.attributes.elevation_meters
|
||||
: 0;
|
||||
@@ -377,7 +410,7 @@ function syncRuntimeDataSources(
|
||||
viewer,
|
||||
longitude,
|
||||
latitude,
|
||||
profile.target.stemHeightMeters,
|
||||
target.stemHeightMeters,
|
||||
fallbackHeightMeters,
|
||||
);
|
||||
entity.polyline = new PolylineGraphics({
|
||||
@@ -385,20 +418,20 @@ function syncRuntimeDataSources(
|
||||
viewer,
|
||||
longitude,
|
||||
latitude,
|
||||
profile.target.stemHeightMeters,
|
||||
target.stemHeightMeters,
|
||||
fallbackHeightMeters,
|
||||
),
|
||||
width: profile.target.stemWidthPx,
|
||||
width: target.stemWidthPx,
|
||||
material: color,
|
||||
show: showBelowCameraHeight(viewer, profile.target.hideCameraHeightMeters),
|
||||
show: showBelowCameraHeight(viewer, target.hideCameraHeightMeters),
|
||||
});
|
||||
entity.point = new PointGraphics({
|
||||
pixelSize: profile.target.headSizePx,
|
||||
pixelSize: target.headSizePx,
|
||||
color,
|
||||
outlineColor: Color.fromCssColorString(profile.target.outlineColor).withAlpha(profile.target.outlineOpacity),
|
||||
outlineWidth: profile.target.outlineWidthPx,
|
||||
outlineColor: Color.fromCssColorString(target.outlineColor).withAlpha(target.outlineOpacity),
|
||||
outlineWidth: target.outlineWidthPx,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
show: showBelowCameraHeight(viewer, profile.target.hideCameraHeightMeters),
|
||||
show: showBelowCameraHeight(viewer, target.hideCameraHeightMeters),
|
||||
});
|
||||
entity.label = new LabelGraphics({
|
||||
text: label,
|
||||
@@ -417,28 +450,73 @@ function syncRuntimeDataSources(
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
show: profile.label.mode !== "none" && showBelowCameraHeight(viewer, profile.label.hideCameraHeightMeters),
|
||||
});
|
||||
} else {
|
||||
entity.position = new ConstantPositionProperty(Cartesian3.fromDegrees(longitude, latitude, 0));
|
||||
entity.polyline = undefined;
|
||||
entity.point = new PointGraphics({
|
||||
pixelSize: 10,
|
||||
color,
|
||||
outlineColor: Color.fromCssColorString("#0c0d12").withAlpha(0.72),
|
||||
outlineWidth: 2,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
binding.slotId !== "zones"
|
||||
|| fact.geometry.type === "Point"
|
||||
|| (profile && profile.target.variant !== "surface-fill")
|
||||
) continue;
|
||||
const polygons = fact.geometry.type === "Polygon" ? [fact.geometry.coordinates] : fact.geometry.coordinates;
|
||||
for (const [polygonIndex, polygon] of polygons.entries()) {
|
||||
const entityId = polygonIndex === 0 ? baseEntityId : `${baseEntityId}:part:${polygonIndex}`;
|
||||
wanted.add(entityId);
|
||||
const outerRing = polygon[0];
|
||||
const toCartesian = (ring: Array<[number, number]>) => ring.map(([longitude, latitude]) => (
|
||||
Cartesian3.fromDegrees(longitude, latitude, 0)
|
||||
));
|
||||
const hierarchy = new PolygonHierarchy(
|
||||
toCartesian(outerRing),
|
||||
polygon.slice(1).map((ring) => new PolygonHierarchy(toCartesian(ring))),
|
||||
);
|
||||
const labelAnchor = outerRing.slice(0, -1).reduce(
|
||||
(accumulator, [longitude, latitude]) => [accumulator[0] + longitude, accumulator[1] + latitude] as [number, number],
|
||||
[0, 0] as [number, number],
|
||||
);
|
||||
const divisor = Math.max(1, outerRing.length - 1);
|
||||
const entity = dataSource.entities.getById(entityId) ?? dataSource.entities.add({ id: entityId });
|
||||
const target = profile?.target.variant === "surface-fill" ? profile.target : null;
|
||||
const visibleBelowLod = target ? showBelowCameraHeight(viewer, target.hideCameraHeightMeters) : true;
|
||||
entity.name = label;
|
||||
entity.position = new ConstantPositionProperty(Cartesian3.fromDegrees(labelAnchor[0] / divisor, labelAnchor[1] / divisor, 0));
|
||||
entity.point = undefined;
|
||||
entity.polygon = new PolygonGraphics({
|
||||
hierarchy,
|
||||
material: color,
|
||||
height: 0,
|
||||
heightReference: HeightReference.CLAMP_TO_GROUND,
|
||||
show: visibleBelowLod,
|
||||
});
|
||||
entity.polyline = new PolylineGraphics({
|
||||
positions: toCartesian(outerRing),
|
||||
width: target?.outlineWidthPx ?? 1.5,
|
||||
material: target
|
||||
? Color.fromCssColorString(target.outlineColor).withAlpha(target.outlineOpacity)
|
||||
: Color.fromCssColorString("#c9b6ff").withAlpha(0.9),
|
||||
clampToGround: true,
|
||||
show: visibleBelowLod,
|
||||
});
|
||||
entity.label = new LabelGraphics({
|
||||
text: label,
|
||||
font: "700 13px Arial",
|
||||
fillColor: Color.WHITE,
|
||||
showBackground: true,
|
||||
backgroundColor: Color.BLACK.withAlpha(0.72),
|
||||
backgroundPadding: new Cartesian2(10, 7),
|
||||
pixelOffset: new Cartesian2(10, 0),
|
||||
font: profile ? `${profile.label.fontWeight} ${profile.label.sizePx}px Arial` : "700 13px Arial",
|
||||
fillColor: profile ? Color.fromCssColorString(profile.label.color) : Color.WHITE,
|
||||
outlineColor: profile ? Color.fromCssColorString(profile.label.outlineColor) : Color.BLACK,
|
||||
outlineWidth: profile?.label.outlineWidthPx ?? 1,
|
||||
style: 2,
|
||||
showBackground: (profile?.label.backgroundOpacity ?? 0.72) > 0,
|
||||
backgroundColor: profile
|
||||
? Color.fromCssColorString(profile.label.backgroundColor).withAlpha(profile.label.backgroundOpacity)
|
||||
: Color.BLACK.withAlpha(0.72),
|
||||
backgroundPadding: new Cartesian2(profile?.label.paddingX ?? 10, profile?.label.paddingY ?? 7),
|
||||
pixelOffset: new Cartesian2(profile?.label.offsetX ?? 10, profile?.label.offsetY ?? 0),
|
||||
horizontalOrigin: HorizontalOrigin.LEFT,
|
||||
verticalOrigin: VerticalOrigin.BOTTOM,
|
||||
heightReference: HeightReference.NONE,
|
||||
heightReference: HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
show: polygonIndex === 0
|
||||
&& (profile?.label.mode ?? "attributes") !== "none"
|
||||
&& (profile ? showBelowCameraHeight(viewer, profile.label.hideCameraHeightMeters) : true),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user