diff --git a/src/app/editor/[id]/page.tsx b/src/app/editor/[id]/page.tsx
index 9683aa1..6c28e29 100644
--- a/src/app/editor/[id]/page.tsx
+++ b/src/app/editor/[id]/page.tsx
@@ -24,7 +24,7 @@ import {
Geometry,
useEditorState,
} from "@/uhm/lib/editor/state/useEditorState";
-import { GEO_TYPE_KEYS, geoTypeCodeToTypeKey } from "@/uhm/lib/map/geo/geoTypeMap";
+import { GEO_TYPE_KEYS } from "@/uhm/lib/map/geo/geoTypeMap";
import {
BackgroundLayerId,
BackgroundLayerVisibility,
@@ -1080,7 +1080,7 @@ export default function Page() {
}
const bindingIds = normalizeGeoSearchBindingIds(geo.binding);
- const typeKey = geoTypeCodeToTypeKey(Number(geo.geo_type)) || null;
+ const typeKey = geo.type || null;
const feature: Feature = {
type: "Feature",
@@ -1537,7 +1537,7 @@ export default function Page() {
#{geo.id}
- type: {String(geo.geo_type)}{" "}
+ type: {geo.type || "unknown"}{" "}
{geo.time_start != null || geo.time_end != null
? `| time: ${geo.time_start ?? "?"} → ${geo.time_end ?? "?"}`
: ""}
diff --git a/src/uhm/api/geometries.ts b/src/uhm/api/geometries.ts
index 0ba7621..cdca56f 100644
--- a/src/uhm/api/geometries.ts
+++ b/src/uhm/api/geometries.ts
@@ -8,7 +8,7 @@ export type { GeometriesBBoxQuery } from "@/uhm/types/api";
export type EntityGeometrySearchGeo = {
id: string;
- geo_type: number;
+ type: string | null;
draw_geometry: unknown;
binding?: unknown;
time_start?: number | null;
@@ -27,6 +27,18 @@ export type SearchGeometriesByEntityNameResponse = {
next_cursor?: string;
};
+type EntityGeometrySearchGeoRow = Omit
& {
+ geo_type: number;
+};
+
+type EntityGeometriesSearchItemRow = Omit & {
+ geometries: EntityGeometrySearchGeoRow[];
+};
+
+type SearchGeometriesByEntityNameApiResponse = Omit & {
+ items: EntityGeometriesSearchItemRow[];
+};
+
function buildBBoxQueryString(params: GeometriesBBoxQuery): string {
const query = new URLSearchParams({
// API mới dùng snake_case
@@ -71,7 +83,24 @@ export async function searchGeometriesByEntityName(
params.set("limit", String(Math.trunc(options.limit)));
}
- return requestJson(`${API_ENDPOINTS.geometries}/entity?${params.toString()}`);
+ const response = await requestJson(
+ `${API_ENDPOINTS.geometries}/entity?${params.toString()}`
+ );
+
+ return {
+ ...response,
+ items: (response.items || []).map((item) => ({
+ ...item,
+ geometries: (item.geometries || []).map((geometry) => ({
+ id: geometry.id,
+ type: geoTypeCodeToTypeKey(geometry.geo_type) || null,
+ draw_geometry: geometry.draw_geometry,
+ binding: geometry.binding,
+ time_start: geometry.time_start ?? null,
+ time_end: geometry.time_end ?? null,
+ })),
+ })),
+ };
}
type GeometryRow = {
diff --git a/src/uhm/api/projects.ts b/src/uhm/api/projects.ts
index a56585f..19ebe33 100644
--- a/src/uhm/api/projects.ts
+++ b/src/uhm/api/projects.ts
@@ -1,5 +1,6 @@
import { API_BASE_URL, API_ENDPOINTS } from "@/uhm/api/config";
import { ApiError, jsonRequestInit, requestJson } from "@/uhm/api/http";
+import { toApiEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot";
import type {
CreateCommitInput,
CreateProjectInput,
@@ -76,10 +77,11 @@ export async function createProjectCommit(
input: CreateCommitInput
): Promise<{ commit: ProjectCommit; state: ProjectState }> {
// POST /projects/{id}/commits
+ const snapshot = toApiEditorSnapshot(input.snapshot);
const commit = await requestJson(
`${API_ENDPOINTS.projects}/${encodeURIComponent(projectId)}/commits`,
jsonRequestInit("POST", {
- snapshot_json: input.snapshot,
+ snapshot_json: snapshot,
edit_summary: input.edit_summary,
})
);
diff --git a/src/uhm/components/editor/SelectedGeometryPanel.tsx b/src/uhm/components/editor/SelectedGeometryPanel.tsx
index 93c3395..3e3cc68 100644
--- a/src/uhm/components/editor/SelectedGeometryPanel.tsx
+++ b/src/uhm/components/editor/SelectedGeometryPanel.tsx
@@ -9,6 +9,7 @@ import {
findGeometryTypeOption,
groupGeometryTypeOptions,
} from "@/uhm/lib/map/geo/geometryTypeOptions";
+import { normalizeGeoTypeKey } from "@/uhm/lib/map/geo/geoTypeMap";
import type { GeometryMetaFormState } from "@/uhm/lib/editor/session/sessionTypes";
type Props = {
@@ -288,9 +289,7 @@ function normalizeGeometryPreset(value: unknown): GeometryPreset | null {
}
function normalizeTypeId(value: unknown): string | null {
- if (typeof value !== "string") return null;
- const normalized = value.trim().toLowerCase();
- return normalized.length ? normalized : null;
+ return normalizeGeoTypeKey(value);
}
function mapGeometryTypeToPreset(
diff --git a/src/uhm/components/map/mapUtils.ts b/src/uhm/components/map/mapUtils.ts
index f61ae67..b272455 100644
--- a/src/uhm/components/map/mapUtils.ts
+++ b/src/uhm/components/map/mapUtils.ts
@@ -13,6 +13,7 @@ import {
import { PATH_RENDER_BY_TYPE } from "@/uhm/lib/map/styles/style";
import { getRasterTileTemplateUrl } from "@/uhm/api/tiles";
import { newId } from "@/uhm/lib/utils/id";
+import { normalizeGeoTypeKey } from "@/uhm/lib/map/geo/geoTypeMap";
type Coordinate = [number, number];
type PolygonCoordinates = Coordinate[][];
@@ -342,18 +343,22 @@ export function collectCoordinatePairs(value: unknown): Array<[number, number]>
}
export function buildPathArrowFeatureCollection(fc: FeatureCollection): FeatureCollection {
- const features = fc.features
- .map((feature) => {
- if (!isPathFeature(feature) || feature.geometry.type !== "LineString") return null;
- const geometry = buildPathArrowGeometry(feature.geometry.coordinates);
- if (!geometry) return null;
- return {
- type: "Feature" as const,
+ const features: Feature[] = [];
+
+ for (const feature of fc.features) {
+ if (!isPathFeature(feature)) continue;
+
+ const coordinateGroups = getLineCoordinateGroups(feature.geometry);
+ for (const coordinates of coordinateGroups) {
+ const geometry = buildPathArrowGeometry(coordinates);
+ if (!geometry) continue;
+ features.push({
+ type: "Feature",
properties: { ...feature.properties },
geometry,
- };
- })
- .filter((feature): feature is Feature => feature !== null);
+ });
+ }
+ }
return {
type: "FeatureCollection",
@@ -368,9 +373,7 @@ export function isPathFeature(feature: Feature): boolean {
export function getFeatureSemanticType(feature: Feature): string | null {
const value = feature.properties.type || feature.properties.entity_type_id || null;
- if (!value) return null;
- const normalized = String(value).trim().toLowerCase();
- return normalized.length ? normalized : null;
+ return normalizeGeoTypeKey(value);
}
export function buildPathArrowGeometry(coords: [number, number][]): Geometry | null {
@@ -719,6 +722,12 @@ function isLineGeometry(geometry: Geometry): boolean {
return geometry.type === "LineString" || geometry.type === "MultiLineString";
}
+function getLineCoordinateGroups(geometry: Geometry): Coordinate[][] {
+ if (geometry.type === "LineString") return [geometry.coordinates];
+ if (geometry.type === "MultiLineString") return geometry.coordinates;
+ return [];
+}
+
function getPolygonLabelPoint(geometry: Geometry): Coordinate | null {
if (geometry.type === "Polygon") {
return getPolygonLabelCandidate(geometry.coordinates)?.point || null;
diff --git a/src/uhm/components/map/useMapLayers.ts b/src/uhm/components/map/useMapLayers.ts
index 16bdede..82f4652 100644
--- a/src/uhm/components/map/useMapLayers.ts
+++ b/src/uhm/components/map/useMapLayers.ts
@@ -384,7 +384,11 @@ export function setupMapLayers(
id: "entity-focus-fill",
type: "fill",
source: "entity-focus",
- filter: ["==", ["geometry-type"], "Polygon"],
+ filter: [
+ "any",
+ ["==", ["geometry-type"], "Polygon"],
+ ["==", ["geometry-type"], "MultiPolygon"],
+ ],
paint: {
"fill-color": "#fde047",
"fill-opacity": 0.2,
@@ -413,7 +417,11 @@ export function setupMapLayers(
id: "entity-focus-points",
type: "circle",
source: "entity-focus",
- filter: ["==", ["geometry-type"], "Point"],
+ filter: [
+ "any",
+ ["==", ["geometry-type"], "Point"],
+ ["==", ["geometry-type"], "MultiPoint"],
+ ],
paint: {
"circle-color": "#f8fafc",
"circle-radius": 8,
diff --git a/src/uhm/lib/editor/project/useProjectCommands.ts b/src/uhm/lib/editor/project/useProjectCommands.ts
index 3d03f47..d0b3dda 100644
--- a/src/uhm/lib/editor/project/useProjectCommands.ts
+++ b/src/uhm/lib/editor/project/useProjectCommands.ts
@@ -9,7 +9,7 @@ import {
openSectionEditor,
submitSection,
} from "@/uhm/api/projects";
-import { buildEditorSnapshot, normalizeEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot";
+import { buildEditorSnapshot, normalizeEditorSnapshot, toApiEditorSnapshot } from "@/uhm/lib/editor/snapshot/editorSnapshot";
import type { Change } from "@/uhm/lib/editor/draft/editorTypes";
import type { Feature, FeatureCollection, FeatureId, GeometryEntitySnapshot, GeometrySnapshot } from "@/uhm/types/geo";
import type { EditorSnapshot, Project, ProjectCommit, ProjectState, EntityWikiLinkSnapshot } from "@/uhm/types/projects";
@@ -110,7 +110,7 @@ export function useProjectCommands(options: Options) {
// Guardrail: commit payload can get large and some deployments reject/close connections for big bodies.
// When that happens, browsers often surface it as "TypeError: Failed to fetch".
try {
- const payloadText = JSON.stringify({ snapshot_json: snapshot, edit_summary: editSummary });
+ const payloadText = JSON.stringify({ snapshot_json: toApiEditorSnapshot(snapshot), edit_summary: editSummary });
const bytes = typeof Blob !== "undefined" ? new Blob([payloadText]).size : payloadText.length;
const limitBytes = 3_500_000; // ~3.5MB (conservative vs common default body limits)
if (bytes > limitBytes) {
diff --git a/src/uhm/lib/editor/snapshot/editorSnapshot.ts b/src/uhm/lib/editor/snapshot/editorSnapshot.ts
index a6db1d7..d142388 100644
--- a/src/uhm/lib/editor/snapshot/editorSnapshot.ts
+++ b/src/uhm/lib/editor/snapshot/editorSnapshot.ts
@@ -1,5 +1,5 @@
import { DEFAULT_GEOMETRY_TYPE_ID } from "@/uhm/lib/map/geo/geometryTypeOptions";
-import { geoTypeCodeToTypeKey, typeKeyToGeoTypeCode } from "@/uhm/lib/map/geo/geoTypeMap";
+import { normalizeGeoTypeKey, typeKeyToGeoTypeCode } from "@/uhm/lib/map/geo/geoTypeMap";
import type { Change } from "@/uhm/lib/editor/draft/editorTypes";
import type { EntitySnapshot } from "@/uhm/types/entities";
import type { EntitySnapshotOperation } from "@/uhm/types/entities";
@@ -86,12 +86,15 @@ export function normalizeEditorSnapshot(raw: unknown): EditorSnapshot | null {
const source: "inline" | "ref" = existingSource || (refId || !hasInlineGeometry ? "ref" : "inline");
const rest: UnknownRecord = { ...g };
delete rest.ref;
+ const typeKey = normalizeGeoTypeKey(rest.type) || normalizeGeoTypeKey(rest.geo_type);
+ delete rest.geo_type;
return {
...(rest as unknown as Omit),
id,
source,
operation,
+ type: typeKey,
};
})
: undefined;
@@ -210,30 +213,39 @@ export function normalizeEditorSnapshot(raw: unknown): EditorSnapshot | null {
for (const feature of cloned.features) {
const gid = String(feature.properties.id);
const entity_ids = byGeom.get(gid) || [];
+ const p = feature.properties as unknown as UnknownRecord;
+
+ const existingTypeKey = normalizeGeoTypeKey(p.type) || normalizeGeoTypeKey(p.entity_type_id);
+ const fallbackTypeKey = getDefaultTypeIdForFeature(feature);
+ if (existingTypeKey) p.type = existingTypeKey;
+
if (entity_ids.length || hasLinks) {
- const props = feature.properties as unknown as UnknownRecord;
- props.entity_ids = entity_ids;
- props.entity_id = entity_ids[0] || null;
+ p.entity_ids = entity_ids;
+ p.entity_id = entity_ids[0] || null;
// Generate denormalized names for UI/map usage.
const primaryId = entity_ids[0] || null;
const primaryName = primaryId ? (entityNameById.get(primaryId) || "") : "";
const names = entity_ids.map((id) => entityNameById.get(id) || "").filter((n) => n.length > 0);
- props.entity_name = primaryName || null;
- props.entity_names = names;
+ p.entity_name = primaryName || null;
+ p.entity_names = names;
}
// Generate geometry metadata onto feature properties (optional in persisted snapshot).
const geo = geometryById.get(gid) || null;
if (geo) {
- const p = feature.properties as unknown as UnknownRecord;
- // type (semantic key) is derived from geometries[].type (numeric code in string form).
- const typeCode = typeof geo.type === "string" && geo.type.trim().length ? Number(geo.type) : NaN;
- const typeKey = geoTypeCodeToTypeKey(Number.isFinite(typeCode) ? typeCode : null);
+ const geoRecord = geo as unknown as UnknownRecord;
+ // type can arrive as numeric geo_type, numeric string, or semantic key depending on backend version.
+ const typeKey = normalizeGeoTypeKey(geoRecord.type)
+ || normalizeGeoTypeKey(geoRecord.geo_type)
+ || existingTypeKey
+ || fallbackTypeKey;
if (typeKey) p.type = typeKey;
if (Array.isArray(geo.binding) && geo.binding.length) p.binding = geo.binding;
if (typeof geo.time_start === "number") p.time_start = geo.time_start;
if (typeof geo.time_end === "number") p.time_end = geo.time_end;
+ } else if (!existingTypeKey) {
+ p.type = fallbackTypeKey;
}
}
return cloned;
@@ -375,14 +387,12 @@ export function buildEditorSnapshot(options: {
? "update"
: "reference";
const bbox = getFeatureBBox(feature);
- const typeKey = feature.properties.type || getDefaultTypeIdForFeature(feature);
- const typeCode = typeKeyToGeoTypeCode(typeKey);
+ const typeKey = normalizeGeoTypeKey(feature.properties.type) || getDefaultTypeIdForFeature(feature);
return {
id,
operation,
source: "inline",
- // BE currently expects geometries[].type as a string. We send the geo_type SMALLINT code as a string.
- type: String(typeCode ?? 0),
+ type: typeKey,
draw_geometry: feature.geometry,
binding: normalizeFeatureBindingIds(feature),
time_start: feature.properties.time_start ?? null,
@@ -600,6 +610,29 @@ export function buildEditorSnapshot(options: {
};
}
+export function toApiEditorSnapshot(snapshot: EditorSnapshot): EditorSnapshot {
+ const cloned = JSON.parse(JSON.stringify(snapshot)) as EditorSnapshot;
+
+ if (Array.isArray(cloned.geometries)) {
+ cloned.geometries = cloned.geometries.map((geometry) => {
+ const row = { ...(geometry as unknown as UnknownRecord) };
+ const typeKey = normalizeGeoTypeKey(row.type) || normalizeGeoTypeKey(row.geo_type);
+ delete row.geo_type;
+
+ if (typeKey) {
+ const typeCode = typeKeyToGeoTypeCode(typeKey);
+ row.type = typeCode == null ? null : String(typeCode);
+ } else if ("type" in row) {
+ row.type = null;
+ }
+
+ return row as unknown as GeometrySnapshot;
+ });
+ }
+
+ return cloned;
+}
+
function dedupeAndSortGeometryEntity(rows: GeometryEntitySnapshot[]): GeometryEntitySnapshot[] {
const seen = new Set();
const deduped: GeometryEntitySnapshot[] = [];
diff --git a/src/uhm/lib/map/geo/geoTypeMap.ts b/src/uhm/lib/map/geo/geoTypeMap.ts
index e6f2c42..da849e7 100644
--- a/src/uhm/lib/map/geo/geoTypeMap.ts
+++ b/src/uhm/lib/map/geo/geoTypeMap.ts
@@ -39,3 +39,20 @@ export function geoTypeCodeToTypeKey(code: number | null | undefined): string |
if (!Number.isFinite(code)) return null;
return KEY_BY_CODE.get(Math.trunc(code)) ?? null;
}
+
+export function normalizeGeoTypeKey(value: unknown): string | null {
+ if (typeof value === "number") {
+ return geoTypeCodeToTypeKey(value);
+ }
+
+ if (typeof value !== "string") return null;
+
+ const normalized = value.trim().toLowerCase();
+ if (!normalized.length) return null;
+
+ if (/^-?\d+$/.test(normalized)) {
+ return geoTypeCodeToTypeKey(Number(normalized));
+ }
+
+ return normalized;
+}
diff --git a/src/uhm/lib/map/styles/geotypes/attack_route.ts b/src/uhm/lib/map/styles/geotypes/attack_route.ts
index a62c02b..0afb1ba 100644
--- a/src/uhm/lib/map/styles/geotypes/attack_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/attack_route.ts
@@ -1,68 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getAttackRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "attack_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "attack_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#ef4444"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "attack_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "attack_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "attack_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "attack_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#ef4444"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "attack_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "attack_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "attack_route",
+ color: "#ef4444",
+ strokeColor: "#7f1d1d",
+ width: { z1: 2.6, z4: 3.8, z6: 5 },
+ arrowOpacity: 0.9,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/battle.ts b/src/uhm/lib/map/styles/geotypes/battle.ts
index 121e9ff..4eaf64a 100644
--- a/src/uhm/lib/map/styles/geotypes/battle.ts
+++ b/src/uhm/lib/map/styles/geotypes/battle.ts
@@ -1,40 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getBattleLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "battle-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "battle"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#f43f5e"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.34
- ]
- }
- },
- {
- id: "battle-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "battle"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#9f1239"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "battle",
+ fillColor: "#f43f5e",
+ strokeColor: "#9f1239",
+ fillOpacity: 0.3,
+ strokeWidth: { z1: 1.5, z4: 2.2, z6: 3 },
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/civilization.ts b/src/uhm/lib/map/styles/geotypes/civilization.ts
index beda40b..8eca272 100644
--- a/src/uhm/lib/map/styles/geotypes/civilization.ts
+++ b/src/uhm/lib/map/styles/geotypes/civilization.ts
@@ -1,40 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getCivilizationLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "civilization-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "civilization"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#14b8a6"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.38
- ]
- }
- },
- {
- id: "civilization-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "civilization"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#134e4a"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "civilization",
+ fillColor: "#14b8a6",
+ strokeColor: "#134e4a",
+ fillOpacity: 0.34,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/country.ts b/src/uhm/lib/map/styles/geotypes/country.ts
index ca3ad34..11de408 100644
--- a/src/uhm/lib/map/styles/geotypes/country.ts
+++ b/src/uhm/lib/map/styles/geotypes/country.ts
@@ -1,40 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getCountryLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "country-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "country"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#2563eb"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.5
- ]
- }
- },
- {
- id: "country-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "country"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#1e3a8a"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "country",
+ fillColor: "#2563eb",
+ strokeColor: "#1e40af",
+ fillOpacity: 0.34,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/defense_line.ts b/src/uhm/lib/map/styles/geotypes/defense_line.ts
index c8e9068..cb8e7b9 100644
--- a/src/uhm/lib/map/styles/geotypes/defense_line.ts
+++ b/src/uhm/lib/map/styles/geotypes/defense_line.ts
@@ -1,35 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getDefenseLineLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "defense_line-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "defense_line"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#f97316"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-dasharray": [3, 2],
- "line-opacity": 0.9
- }
- },
- {
- id: "defense_line-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "defense_line"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "defense_line",
+ color: "#38bdf8",
+ strokeColor: "#075985",
+ dasharray: [3, 2],
+ arrow: false,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/empire.ts b/src/uhm/lib/map/styles/geotypes/empire.ts
index 4870683..3379345 100644
--- a/src/uhm/lib/map/styles/geotypes/empire.ts
+++ b/src/uhm/lib/map/styles/geotypes/empire.ts
@@ -1,40 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getEmpireLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "empire-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "empire"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#f59e0b"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.5
- ]
- }
- },
- {
- id: "empire-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "empire"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#7c2d12"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "empire",
+ fillColor: "#f59e0b",
+ strokeColor: "#92400e",
+ fillOpacity: 0.36,
+ strokeWidth: { z1: 1.8, z4: 2.6, z6: 3.4 },
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/invasion_route.ts b/src/uhm/lib/map/styles/geotypes/invasion_route.ts
index 50c996b..985446f 100644
--- a/src/uhm/lib/map/styles/geotypes/invasion_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/invasion_route.ts
@@ -1,68 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getInvasionRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "invasion_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "invasion_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#b91c1c"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "invasion_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "invasion_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "invasion_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "invasion_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#b91c1c"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "invasion_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "invasion_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "invasion_route",
+ color: "#be123c",
+ strokeColor: "#4c0519",
+ width: { z1: 2.8, z4: 4.1, z6: 5.4 },
+ arrowOpacity: 0.9,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/kingdom.ts b/src/uhm/lib/map/styles/geotypes/kingdom.ts
index f6bae6f..01fd523 100644
--- a/src/uhm/lib/map/styles/geotypes/kingdom.ts
+++ b/src/uhm/lib/map/styles/geotypes/kingdom.ts
@@ -1,40 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getKingdomLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "kingdom-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "kingdom"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#d97706"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.5
- ]
- }
- },
- {
- id: "kingdom-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "kingdom"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#9a3412"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "kingdom",
+ fillColor: "#8b5cf6",
+ strokeColor: "#6d28d9",
+ fillOpacity: 0.34,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/migration_route.ts b/src/uhm/lib/map/styles/geotypes/migration_route.ts
index 113b3f7..85eef60 100644
--- a/src/uhm/lib/map/styles/geotypes/migration_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/migration_route.ts
@@ -1,68 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getMigrationRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "migration_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "migration_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#0ea5e9"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "migration_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "migration_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "migration_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "migration_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#0ea5e9"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "migration_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "migration_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "migration_route",
+ color: "#10b981",
+ strokeColor: "#065f46",
+ dasharray: [4, 3],
+ arrowOpacity: 0.76,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/rebellion_zone.ts b/src/uhm/lib/map/styles/geotypes/rebellion_zone.ts
index 590f669..9e29e15 100644
--- a/src/uhm/lib/map/styles/geotypes/rebellion_zone.ts
+++ b/src/uhm/lib/map/styles/geotypes/rebellion_zone.ts
@@ -1,40 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getRebellionZoneLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "rebellion_zone-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "rebellion_zone"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#7c3aed"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.32
- ]
- }
- },
- {
- id: "rebellion_zone-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "rebellion_zone"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#4c1d95"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "rebellion_zone",
+ fillColor: "#a21caf",
+ strokeColor: "#701a75",
+ fillOpacity: 0.26,
+ dasharray: [3, 2],
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/refugee_route.ts b/src/uhm/lib/map/styles/geotypes/refugee_route.ts
index 668c164..5a023f8 100644
--- a/src/uhm/lib/map/styles/geotypes/refugee_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/refugee_route.ts
@@ -1,68 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getRefugeeRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "refugee_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "refugee_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#06b6d4"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "refugee_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "refugee_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "refugee_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "refugee_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#06b6d4"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "refugee_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "refugee_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "refugee_route",
+ color: "#f97316",
+ strokeColor: "#9a3412",
+ dasharray: [1, 2],
+ opacity: 0.84,
+ arrowOpacity: 0.72,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/retreat_route.ts b/src/uhm/lib/map/styles/geotypes/retreat_route.ts
index 55f697e..1701538 100644
--- a/src/uhm/lib/map/styles/geotypes/retreat_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/retreat_route.ts
@@ -1,68 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getRetreatRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "retreat_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "retreat_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#94a3b8"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "retreat_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "retreat_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "retreat_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "retreat_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#94a3b8"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "retreat_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "retreat_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "retreat_route",
+ color: "#94a3b8",
+ strokeColor: "#475569",
+ dasharray: [6, 3],
+ opacity: 0.82,
+ arrowOpacity: 0.68,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/shipping_route.ts b/src/uhm/lib/map/styles/geotypes/shipping_route.ts
index 3b8ebc7..2c1b246 100644
--- a/src/uhm/lib/map/styles/geotypes/shipping_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/shipping_route.ts
@@ -1,68 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getShippingRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "shipping_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "shipping_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#2563eb"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "shipping_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "shipping_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "shipping_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "shipping_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#2563eb"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "shipping_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "shipping_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "shipping_route",
+ color: "#0ea5e9",
+ strokeColor: "#075985",
+ width: { z1: 2.4, z4: 3.5, z6: 4.7 },
+ dasharray: [7, 4],
+ arrowOpacity: 0.8,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/state.ts b/src/uhm/lib/map/styles/geotypes/state.ts
index 1989cd7..7981db7 100644
--- a/src/uhm/lib/map/styles/geotypes/state.ts
+++ b/src/uhm/lib/map/styles/geotypes/state.ts
@@ -1,40 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getStateLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "state-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "state"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#0ea5e9"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.5
- ]
- }
- },
- {
- id: "state-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "state"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0c4a6e"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "state",
+ fillColor: "#0891b2",
+ strokeColor: "#0e7490",
+ fillOpacity: 0.28,
+ strokeWidth: { z1: 1.1, z4: 1.7, z6: 2.4 },
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/trade_route.ts b/src/uhm/lib/map/styles/geotypes/trade_route.ts
index 77bd73d..983ced0 100644
--- a/src/uhm/lib/map/styles/geotypes/trade_route.ts
+++ b/src/uhm/lib/map/styles/geotypes/trade_route.ts
@@ -1,68 +1,13 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildLineGeotypeLayers } from "./styleBuilders";
export function getTradeRouteLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "trade_route-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "trade_route"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#eab308"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 2.2, 4, 3.2, 6, 4.2],
- "line-opacity": 0.9
- }
- },
- {
- id: "trade_route-hit",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "LineString"], ["==", TYPE_MATCH_EXPR, "trade_route"]],
- paint: {
- "line-color": "#ffffff",
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 12, 4, 18, 6, 24],
- "line-opacity": 0
- }
- },
- {
- id: "trade_route-path-arrow-fill",
- type: "fill",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "trade_route"],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#eab308"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.92,
- 0.82
- ]
- }
- },
- {
- id: "trade_route-path-arrow-line",
- type: "line",
- source: pathArrowSourceId!,
- filter: ["==", TYPE_MATCH_EXPR, "trade_route"],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#0f172a"
- ],
- "line-width": ["interpolate", ["linear"], ["zoom"], 1, 0.45, 4, 0.8, 6, 1.2],
- "line-opacity": 0.9
- }
- }
- ];
+ void pointSourceId;
+ return buildLineGeotypeLayers(sourceId, pathArrowSourceId, {
+ typeId: "trade_route",
+ color: "#eab308",
+ strokeColor: "#854d0e",
+ dasharray: [5, 3],
+ arrowOpacity: 0.78,
+ });
}
diff --git a/src/uhm/lib/map/styles/geotypes/war.ts b/src/uhm/lib/map/styles/geotypes/war.ts
index a261e5f..1e2f0fc 100644
--- a/src/uhm/lib/map/styles/geotypes/war.ts
+++ b/src/uhm/lib/map/styles/geotypes/war.ts
@@ -1,40 +1,14 @@
import { LayerSpecification } from "maplibre-gl";
-import { TYPE_MATCH_EXPR } from "./index";
+import { buildPolygonGeotypeLayers } from "./styleBuilders";
export function getWarLayers(sourceId: string, pathArrowSourceId?: string, pointSourceId?: string): LayerSpecification[] {
- return [
- {
- id: "war-fill",
- type: "fill",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "war"]],
- paint: {
- "fill-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#22c55e",
- ["==", ["coalesce", ["get", "entity_id"], ""], ""], "#ef4444",
- "#dc2626"
- ],
- "fill-opacity": [
- "case",
- ["boolean", ["feature-state", "selected"], false], 0.6,
- 0.3
- ]
- }
- },
- {
- id: "war-line",
- type: "line",
- source: sourceId,
- filter: ["all", ["==", ["geometry-type"], "Polygon"], ["==", TYPE_MATCH_EXPR, "war"]],
- paint: {
- "line-color": [
- "case",
- ["boolean", ["feature-state", "selected"], false], "#14532d",
- "#7f1d1d"
- ],
- "line-width": 2
- }
- }
- ];
+ void pathArrowSourceId;
+ void pointSourceId;
+ return buildPolygonGeotypeLayers(sourceId, {
+ typeId: "war",
+ fillColor: "#dc2626",
+ strokeColor: "#7f1d1d",
+ fillOpacity: 0.26,
+ dasharray: [5, 2],
+ });
}