feat(foundry): render geozones as independent map layer

This commit is contained in:
Codex
2026-07-20 21:48:27 +03:00
parent a02c3ff3dd
commit aa681ca069
12 changed files with 418 additions and 90 deletions
+109 -31
View File
@@ -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),
});
}
}