diff --git a/package-lock.json b/package-lock.json index 53616e24..25a3c267 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1353,7 +1353,6 @@ "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1394,7 +1393,6 @@ "integrity": "sha512-gfajTHVCiwpxRj1qh0Sh/5bbGLG4F/ZH/V9xvFVoFddpITfMta9YGow0W6ZpTTORv2vdJuz9TnrNSmjKvpOf4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/browser": "4.0.18", "@vitest/mocker": "4.0.18", @@ -1876,7 +1874,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -2163,7 +2160,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2475,7 +2471,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -2551,7 +2546,6 @@ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", diff --git a/public/modules/io/export.js b/public/modules/io/export.js index 51164c73..e2ab8263 100644 --- a/public/modules/io/export.js +++ b/public/modules/io/export.js @@ -574,3 +574,121 @@ function saveGeoJsonMarkers() { const fileName = getFileName("Markers") + ".geojson"; downloadFile(JSON.stringify(json), fileName, "application/json"); } + +function saveGeoJsonZones() { + const {zones, cells, vertices} = pack; + const json = {type: "FeatureCollection", features: []}; + + // Helper function to convert zone cells to polygon coordinates + // Handles multiple disconnected components and holes properly + function getZonePolygonCoordinates(zoneCells) { + const cellsInZone = new Set(zoneCells); + const ofSameType = (cellId) => cellsInZone.has(cellId); + const ofDifferentType = (cellId) => !cellsInZone.has(cellId); + + const checkedCells = new Set(); + const rings = []; // Array of LinearRings (each ring is an array of coordinates) + + // Find all boundary components by tracing each connected region + for (const cellId of zoneCells) { + if (checkedCells.has(cellId)) continue; + + // Check if this cell is on the boundary (has a neighbor outside the zone) + const neighbors = cells.c[cellId]; + const onBorder = neighbors.some(ofDifferentType); + if (!onBorder) continue; + + // Check if this is an inner lake (hole) - skip if so + const feature = pack.features[cells.f[cellId]]; + if (feature.type === "lake" && feature.shoreline) { + if (feature.shoreline.every(ofSameType)) continue; + } + + // Find a starting vertex that's on the boundary + const cellVertices = cells.v[cellId]; + let startingVertex = null; + + for (const vertexId of cellVertices) { + const vertexCells = vertices.c[vertexId]; + if (vertexCells.some(ofDifferentType)) { + startingVertex = vertexId; + break; + } + } + + if (startingVertex === null) continue; + + // Use connectVertices to trace the boundary (reusing existing logic) + const vertexChain = connectVertices({ + vertices, + startingVertex, + ofSameType, + addToChecked: (cellId) => checkedCells.add(cellId), + closeRing: false, // We'll close it manually after converting to coordinates + }); + + if (vertexChain.length < 3) continue; + + // Convert vertex chain to coordinates + const coordinates = []; + for (const vertexId of vertexChain) { + const [x, y] = vertices.p[vertexId]; + coordinates.push(getCoordinates(x, y, 4)); + } + + // Close the ring (first coordinate = last coordinate) + if (coordinates.length > 0) { + coordinates.push(coordinates[0]); + } + + // Only add ring if it has at least 4 positions (minimum for valid LinearRing) + if (coordinates.length >= 4) { + rings.push(coordinates); + } + } + + return rings; + } + + // Filter and process zones + zones.forEach(zone => { + // Exclude hidden zones and zones with no cells + if (zone.hidden || !zone.cells || zone.cells.length === 0) return; + + const rings = getZonePolygonCoordinates(zone.cells); + + // Skip if no valid rings were generated + if (rings.length === 0) return; + + const properties = { + id: zone.i, + name: zone.name, + type: zone.type, + color: zone.color, + cells: zone.cells + }; + + // If there's only one ring, use Polygon geometry + if (rings.length === 1) { + const feature = { + type: "Feature", + geometry: {type: "Polygon", coordinates: rings}, + properties + }; + json.features.push(feature); + } else { + // Multiple disconnected components: use MultiPolygon + // Each component is wrapped in its own array + const multiPolygonCoordinates = rings.map(ring => [ring]); + const feature = { + type: "Feature", + geometry: {type: "MultiPolygon", coordinates: multiPolygonCoordinates}, + properties + }; + json.features.push(feature); + } + }); + + const fileName = getFileName("Zones") + ".geojson"; + downloadFile(JSON.stringify(json), fileName, "application/json"); +} diff --git a/public/versioning.js b/public/versioning.js index 99f6a730..7861158d 100644 --- a/public/versioning.js +++ b/public/versioning.js @@ -13,7 +13,7 @@ * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.112.3"; +const VERSION = "1.112.4"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); { @@ -49,6 +49,7 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o
Join our Discord server and Reddit community to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.
diff --git a/src/index.html b/src/index.html index 8e73451d..9414a182 100644 --- a/src/index.html +++ b/src/index.html @@ -6152,6 +6152,7 @@ +GeoJSON format is used in GIS tools such as QGIS. Check out @@ -8553,6 +8554,6 @@ - +