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
+48 -13
View File
@@ -1166,11 +1166,53 @@ async function inspectFoundryConsumerReaderGrant(target, { generation = 1 } = {}
return { product: planned.product, readerGrantAction: "ensure", readerGrantGeneration: generation };
}
function runtimePointGeometry(value) {
if (!isObject(value) || value.type !== "Point" || !Array.isArray(value.coordinates) || value.coordinates.length !== 2) return null;
const [longitude, latitude] = value.coordinates;
if (typeof longitude !== "number" || !Number.isFinite(longitude) || typeof latitude !== "number" || !Number.isFinite(latitude)) return null;
return { type: "Point", coordinates: [longitude, latitude] };
function runtimePosition(value) {
if (!Array.isArray(value) || value.length !== 2) return null;
const [longitude, latitude] = value;
if (
typeof longitude !== "number"
|| !Number.isFinite(longitude)
|| longitude < -180
|| longitude > 180
|| typeof latitude !== "number"
|| !Number.isFinite(latitude)
|| latitude < -90
|| latitude > 90
) return null;
return [longitude, latitude];
}
function runtimeLinearRing(value) {
if (!Array.isArray(value) || value.length < 4 || value.length > 10_000) return null;
const positions = value.map(runtimePosition);
if (positions.some((position) => !position)) return null;
const first = positions[0];
const last = positions.at(-1);
if (!last || first[0] !== last[0] || first[1] !== last[1]) return null;
return positions;
}
function runtimePolygonCoordinates(value) {
if (!Array.isArray(value) || value.length < 1 || value.length > 256) return null;
const rings = value.map(runtimeLinearRing);
return rings.some((ring) => !ring) ? null : rings;
}
function runtimeGeometry(value) {
if (!isObject(value) || JSON.stringify(value).length > 196_608) return null;
if (value.type === "Point") {
const coordinates = runtimePosition(value.coordinates);
return coordinates ? { type: "Point", coordinates } : null;
}
if (value.type === "Polygon") {
const coordinates = runtimePolygonCoordinates(value.coordinates);
return coordinates ? { type: "Polygon", coordinates } : null;
}
if (value.type === "MultiPolygon" && Array.isArray(value.coordinates) && value.coordinates.length >= 1 && value.coordinates.length <= 256) {
const coordinates = value.coordinates.map(runtimePolygonCoordinates);
return coordinates.some((polygon) => !polygon) ? null : { type: "MultiPolygon", coordinates };
}
return null;
}
const RUNTIME_SECRET_LIKE_KEY = /(token|secret|password|authorization|access[_-]?token|refresh[_-]?token|api[_-]?key)/i;
@@ -1203,7 +1245,7 @@ function sanitizeRuntimeFact(value, binding) {
observedAt: value.observedAt,
receivedAt: value.receivedAt,
attributes,
geometry: runtimePointGeometry(value.geometry),
geometry: runtimeGeometry(value.geometry),
};
}
@@ -1285,23 +1327,16 @@ function sanitizeRuntimePatch(value, binding) {
const fact = sanitizeRuntimeFact(operation.fact, binding);
return fact ? [{ op: "upsert", fact }] : [];
}
// Current EDP v1 emits only upserts. Foundry already understands the
// canonical removal shape so a future versioned product can revoke a
// subject without turning a transient stream failure into deletion.
if (
operation.op === "remove"
&& isRuntimeIdentifier(operation.sourceId)
&& isRuntimeIdentifier(operation.semanticType)
&& binding.semanticTypes.includes(operation.semanticType)
&& isRuntimeTimestamp(operation.removedAt)
&& ["tombstone", "revoked"].includes(operation.reason)
) {
return [{
op: "remove",
sourceId: operation.sourceId,
semanticType: operation.semanticType,
removedAt: operation.removedAt,
reason: operation.reason,
}];
}
return [];