diff --git a/.gitignore b/.gitignore index 455a8bc..c3a4534 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,6 @@ app/release/ captures/ local.properties keystore.properties -key.jks \ No newline at end of file +key.jks +# generated map intermediates; the packed .bmap in assets is what ships +mapdata/*.geojson diff --git a/app/src/main/assets/aeqd01.bmap b/app/src/main/assets/aeqd01.bmap new file mode 100644 index 0000000..f3c5c56 Binary files /dev/null and b/app/src/main/assets/aeqd01.bmap differ diff --git a/app/src/main/assets/aeqd01.svg b/app/src/main/assets/aeqd01.svg deleted file mode 100644 index a7dc0e7..0000000 --- a/app/src/main/assets/aeqd01.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/src/main/assets/loxim01.bmap b/app/src/main/assets/loxim01.bmap new file mode 100644 index 0000000..0dfc2ef Binary files /dev/null and b/app/src/main/assets/loxim01.bmap differ diff --git a/app/src/main/assets/loxim01.svg b/app/src/main/assets/loxim01.svg deleted file mode 100644 index d3940f9..0000000 --- a/app/src/main/assets/loxim01.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/src/main/assets/webmercator01.bmap b/app/src/main/assets/webmercator01.bmap new file mode 100644 index 0000000..e629a63 Binary files /dev/null and b/app/src/main/assets/webmercator01.bmap differ diff --git a/app/src/main/assets/webmercator01.svg b/app/src/main/assets/webmercator01.svg deleted file mode 100644 index d10cb44..0000000 --- a/app/src/main/assets/webmercator01.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/genenum.js b/genenum.js index 4bf8a1b..d52988d 100644 --- a/genenum.js +++ b/genenum.js @@ -1,10 +1,8 @@ #!/bin/node -import {readFileSync,writeFileSync,existsSync} from 'fs' +import {readFileSync,existsSync} from 'fs' import area from '@turf/area' import * as turf from '@turf/turf' -import * as path from 'path'; -import { JSDOM } from 'jsdom'; const countries = @@ -162,52 +160,4 @@ async function run(){ } } - -function fixSvg(svgPath) { - const countryToRegion = {}; - for (const [region, countries] of Object.entries(groups)) { - countries.forEach(country => countryToRegion[country] = region); - } - const absoluteInputPath = path.resolve(svgPath); - if (!existsSync(absoluteInputPath)) { - throw new Error(`Input file not found at: ${absoluteInputPath}`); - } - const svgContent = readFileSync(absoluteInputPath, 'utf8'); - const dom = new JSDOM(svgContent, { contentType: 'image/svg+xml' }); - const document = dom.window.document; - const svgRoot = document.querySelector('svg'); - if (!svgRoot) { - throw new Error("Invalid or empty SVG structure encountered."); - } - if (svgRoot.getAttribute('data-processed') === 'true') { - console.log(`Skipping: File at "${svgPath}" has already been processed.`); - return; - } - - const elementGroups = Array.from(svgRoot.querySelectorAll('g')); - elementGroups.forEach(group => { - const currentId = group.getAttribute('id') || ''; - const baseIsoCode = currentId.replace(/\d+$/, ''); - const regionKey = countryToRegion[baseIsoCode] || 'XXXX'; - let regionGroup = svgRoot.querySelector(`g[id="${regionKey}"]`); - if (!regionGroup) { - regionGroup = document.createElementNS('http://w3.org', 'g'); - regionGroup.setAttribute('id', regionKey); - svgRoot.appendChild(regionGroup); - } - regionGroup.appendChild(group); - }); - svgRoot.setAttribute('data-processed', 'true'); - const absoluteOutputPath = path.resolve(svgPath); - let cleanXmlString = svgRoot.outerHTML; - cleanXmlString = cleanXmlString.replace(/[\r\n]+/g, ''); - cleanXmlString = cleanXmlString.replace(/xmlns="http:\/\/w3\.org"\s?/g, ''); - cleanXmlString = cleanXmlString.replace(/xmlns="http:\/\/www\.w3\.org\/2000\/svg"\s?/g, ''); - cleanXmlString = cleanXmlString.replace(' { - if (err) { - console.error('Error reading SVG file:', err); - return; - } - - // Create a virtual DOM with JSDOM - const dom = new JSDOM(data); - const document = dom.window.document; - - // Select the root element - const svgElement = document.querySelector('svg'); - - // Group elements representing countries by continent - const world = {}; // Renamed continents object to 'world' - svgElement.querySelectorAll('g').forEach(countryGroup => { - const countryId = countryGroup.getAttribute('id'); - if (countryId!= null){ - const continentId = continents[countryId.replace(/\d/g, "")] || "XXX"; // Remove numbers from country ID to get continent ID - if (!world[continentId]) { - world[continentId] = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - world[continentId].setAttribute('id', continentId); - } - world[continentId].innerHTML += countryGroup.outerHTML; - }else{ - - console.log(countryId,countryGroup.outerHTML) - } - }); - - // Create new elements for each continent - svgElement.innerHTML = ''; - for (const continentId in world) { - svgElement.appendChild(world[continentId]); - } - - // Output the modified SVG - fs.writeFile(svgFilePath, svgElement.outerHTML, 'utf-8', err => { - if (err) { - console.error('Error writing to SVG file:', err); - return; - } - console.log('SVG file updated successfully.'); - }); - }); -} - - -// Example usage: -const svgFileBasePath = "./app/src/main/assets/" ; -groupByContinent(svgFileBasePath+ "webmercator01.svg"); -groupByContinent(svgFileBasePath+ "aeqd01.svg"); -groupByContinent(svgFileBasePath+ "loxim01.svg"); \ No newline at end of file diff --git a/package.json b/package.json index 409c9e2..cd61055 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { + "scripts": { + "pack": "node packmap.js mapdata/loxim01.geojson mapdata/webmercator01.geojson mapdata/aeqd01.geojson" + }, "dependencies": { "@turf/area": "^7.3.5", "@turf/turf": "^7.3.5", - "jsdom": "^30.0.0", "mapshaper": "^0.7.22" }, "type": "module" diff --git a/packmap.js b/packmap.js new file mode 100644 index 0000000..d3b0d13 --- /dev/null +++ b/packmap.js @@ -0,0 +1,232 @@ +#!/bin/node + +/** + * Packs the generated map data into the compact binary the app reads. + * + * The app used to ship SVG, which spends about four megabytes per projection + * spelling coordinates out in decimal ASCII inside XML, and needed the map + * parsed as a document at startup. Nothing needs a document: the app wants a + * list of places and the outlines that belong to them, which is what this + * writes. + * + * Input is mapshaper's projected GeoJSON, where each feature carries: + * CODE the place it belongs to, e.g. "FRA" or "FRA_CE" + * LEVEL 0 for a country outline, 1 for one of its regions + * + * Output format. All integers are LEB128 varints and coordinates are in grid + * steps, on a canvas MAP_WIDTH user units across: + * + * "BMAP" 0x02 + * precision uvarint, grid steps per user unit + * width, height uvarint, in grid steps + * shapeCount uvarint + * per shape: + * codeLength, code uvarint + UTF-8 bytes + * flags 1 byte, bit 0 = is a region + * ringCount uvarint + * per ring: + * pointCount uvarint + * flags 1 byte, bit 0 = even-odd fill + * pointCount pairs of dx, dy zigzag varint, from the previous point, + * starting from the origin on each ring + */ + +import { readFileSync, writeFileSync } from 'fs' + +/** Canvas width in user units. Stroke widths in the app are relative to it. */ +const MAP_WIDTH = 800 + +/** Blank edge left around the map, as mapshaper's own SVG output used to. */ +const MARGIN = 1 + +/** + * Grid steps per user unit. Coordinates are snapped to this, so it sets how + * fine the map can be; it is written into the header rather than agreed by + * convention, so raising it only means regenerating the assets. + */ +const PRECISION = 20 + +function uvarint(bytes, value) { + while (value >= 0x80) { + bytes.push((value & 0x7f) | 0x80) + value >>>= 7 + } + bytes.push(value) +} + +function svarint(bytes, value) { + uvarint(bytes, value < 0 ? -value * 2 - 1 : value * 2) +} + +/** Every polygon of a feature, outer rings and holes alike. */ +function ringsOf(geometry) { + if (!geometry) return [] + if (geometry.type === 'Polygon') return [geometry.coordinates] + if (geometry.type === 'MultiPolygon') return geometry.coordinates + return [] +} + +/** + * Groups features by the place they belong to. A country outline and its + * regions arrive as separate features; the code is what ties them together. + */ +function collect(features) { + const shapes = [] + const byCode = new Map() + + for (const feature of features) { + const code = feature.properties?.CODE + if (!code) continue + const polygons = ringsOf(feature.geometry) + if (polygons.length === 0) continue + + let shape = byCode.get(code) + if (!shape) { + shape = { code, isState: feature.properties.LEVEL === 1, rings: [], seen: new Set() } + byCode.set(code, shape) + shapes.push(shape) + } + // A polygon with holes only reads correctly under an even-odd fill, which + // is the rule mapshaper's SVG output used to pick for the same reason. + const evenOdd = polygons.some((polygon) => polygon.length > 1) + + for (const polygon of polygons) { + for (const ring of polygon) { + if (ring.length >= 3) shape.rings.push({ ring, evenOdd }) + } + } + } + return shapes +} + +/** Twice the signed area of a closed ring of `x, y` pairs. */ +function shoelace(points) { + let sum = 0 + const count = points.length / 2 + for (let i = 0, j = count - 1; i < count; j = i++) { + sum += points[j * 2] * points[i * 2 + 1] - points[i * 2] * points[j * 2 + 1] + } + return sum +} + +/** + * Snaps every ring to the output grid and throws away what that makes + * redundant: points that land on the one before them, and rings left too small + * to enclose anything. The simplification upstream works to about this + * precision, so a good share of the raw points say nothing once rounded. + */ +function quantize(shapes, canvas) { + const kept = [] + for (const shape of shapes) { + const seen = new Set() + const rings = [] + for (const { ring, evenOdd } of shape.rings) { + const points = [] + let px = null + let py = null + for (const [rx, ry] of ring) { + const x = canvas.x(rx) + const y = canvas.y(ry) + if (x === px && y === py) continue + points.push(x, y) + px = x + py = y + } + if (points.length < 6) continue + // Coordinates are integers now, so any ring that still encloses area + // encloses at least half a grid square. One that comes out at exactly + // zero is a sliver the rounding flattened, and it would draw nothing. + if (shoelace(points) === 0) continue + // Antarctica arrives twice over, once per admin level, because its source + // carries no country code and both levels land on ATA. A ring counted + // twice cancels itself out in the even-odd test the app hit tests with, + // which would leave the shape impossible to tap. + const key = points.join(',') + if (seen.has(key)) continue + seen.add(key) + rings.push({ points, evenOdd }) + } + if (rings.length > 0) kept.push({ code: shape.code, isState: shape.isState, rings }) + } + // By code, which puts a country next to its own regions. They are neighbours + // on the map, so their coordinates are similar, and the compressor in the APK + // gets noticeably more out of the file for it. + return kept.sort((a, b) => (a.code < b.code ? -1 : a.code > b.code ? 1 : 0)) +} + +/** Projected metres in, user units on a MAP_WIDTH canvas out, y flipped. */ +function fitToCanvas(shapes) { + let minX = Infinity + let minY = Infinity + let maxX = -Infinity + let maxY = -Infinity + for (const shape of shapes) { + for (const { ring } of shape.rings) { + for (const [x, y] of ring) { + if (x < minX) minX = x + if (x > maxX) maxX = x + if (y < minY) minY = y + if (y > maxY) maxY = y + } + } + } + const scale = (MAP_WIDTH - 2 * MARGIN) / (maxX - minX) + return { + width: MAP_WIDTH, + height: 2 * MARGIN + (maxY - minY) * scale, + x: (v) => Math.round((MARGIN + (v - minX) * scale) * PRECISION), + y: (v) => Math.round((MARGIN + (maxY - v) * scale) * PRECISION), + } +} + +function pack(path) { + const geojson = JSON.parse(readFileSync(path, 'utf8')) + const collected = collect(geojson.features ?? []) + const canvas = fitToCanvas(collected) + const shapes = quantize(collected, canvas) + + const bytes = [0x42, 0x4d, 0x41, 0x50, 0x02] // "BMAP" v2 + uvarint(bytes, PRECISION) + uvarint(bytes, Math.round(canvas.width * PRECISION)) + uvarint(bytes, Math.round(canvas.height * PRECISION)) + uvarint(bytes, shapes.length) + + let rings = 0 + let points = 0 + for (const shape of shapes) { + const code = Buffer.from(shape.code, 'utf8') + uvarint(bytes, code.length) + for (const byte of code) bytes.push(byte) + bytes.push(shape.isState ? 1 : 0) + uvarint(bytes, shape.rings.length) + + for (const { points: ring, evenOdd } of shape.rings) { + uvarint(bytes, ring.length / 2) + bytes.push(evenOdd ? 1 : 0) + let px = 0 + let py = 0 + for (let i = 0; i < ring.length; i += 2) { + svarint(bytes, ring[i] - px) + svarint(bytes, ring[i + 1] - py) + px = ring[i] + py = ring[i + 1] + } + rings++ + points += ring.length / 2 + } + } + + const out = path.replace(/^.*\//, './app/src/main/assets/').replace(/\.geojson$/, '.bmap') + writeFileSync(out, Buffer.from(bytes)) + console.log( + `${out}: ${shapes.length} shapes, ${rings} rings, ${points} points, ` + + `${canvas.width}x${canvas.height.toFixed(1)}, ${bytes.length} bytes`, + ) +} + +const inputs = process.argv.slice(2) +if (inputs.length === 0) { + console.error('usage: node packmap.js .geojson ...') + process.exit(1) +} +inputs.forEach(pack) diff --git a/yarn.lock b/yarn.lock index 36750fe..112cac7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,77 +2,11 @@ # yarn lockfile v1 -"@asamuzakjp/css-color@^6.0.5": - version "6.0.7" - resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-6.0.7.tgz#8f9f67452e6636930949abe047dd553b13276939" - integrity sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw== - dependencies: - "@csstools/css-calc" "^3.3.0" - "@csstools/css-color-parser" "^4.1.10" - "@csstools/css-parser-algorithms" "^4.0.0" - "@csstools/css-tokenizer" "^4.0.0" - lru-cache "^11.5.2" - -"@asamuzakjp/dom-selector@^8.3.0": - version "8.3.2" - resolved "https://registry.yarnpkg.com/@asamuzakjp/dom-selector/-/dom-selector-8.3.2.tgz#1e84c1ea1e12a921c1aa94da4b1f657168f09596" - integrity sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q== - dependencies: - bidi-js "^1.0.3" - css-tree "^3.2.1" - is-potential-custom-element-name "^1.0.1" - lru-cache "^11.5.2" - "@bokuweb/zstd-wasm@^0.0.27": version "0.0.27" resolved "https://registry.yarnpkg.com/@bokuweb/zstd-wasm/-/zstd-wasm-0.0.27.tgz#89d8ed8c37528c4b9d11f47826b9f603346dfc4c" integrity sha512-GDm2uOTK3ESjnYmSeLQifJnBsRCWajKLvN32D2ZcQaaCIJI/Hse9s74f7APXjHit95S10UImsRGkTsbwHmrtmg== -"@bramus/specificity@^2.4.2": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@bramus/specificity/-/specificity-2.4.2.tgz#aa8db8eb173fdee7324f82284833106adeecc648" - integrity sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw== - dependencies: - css-tree "^3.0.0" - -"@csstools/color-helpers@^6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-6.1.1.tgz#1890f29a4347486b54048d24c6ab8fbef039ee61" - integrity sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w== - -"@csstools/css-calc@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-3.3.0.tgz#33cc4bdbab8edf1b6e3ab8c1eba849c41a324f1a" - integrity sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ== - -"@csstools/css-color-parser@^4.1.10": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-4.2.2.tgz#f590bc710a26914dbe64c4656813eafc93f4e45e" - integrity sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg== - dependencies: - "@csstools/color-helpers" "^6.1.1" - "@csstools/css-calc" "^3.3.0" - -"@csstools/css-parser-algorithms@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz#e1c65dc09378b42f26a111fca7f7075fc2c26164" - integrity sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w== - -"@csstools/css-syntax-patches-for-csstree@^1.1.7": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.12.tgz#34e7156b352b71a482884d659bbd1bf2f738e0c9" - integrity sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw== - -"@csstools/css-tokenizer@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz#798a33950d11226a0ebb6acafa60f5594424967f" - integrity sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA== - -"@exodus/bytes@^1.11.0", "@exodus/bytes@^1.15.1", "@exodus/bytes@^1.6.0": - version "1.15.1" - resolved "https://registry.yarnpkg.com/@exodus/bytes/-/bytes-1.15.1.tgz#b13bc464ca162c17abf0837fb3a11aeab79e45d1" - integrity sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q== - "@inquirer/ansi@^2.0.8": version "2.0.8" resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-2.0.8.tgz#0308f3ed790dfa960f0f1f60045fa67e804de38e" @@ -2060,13 +1994,6 @@ better-sqlite3@^12.10.0: bindings "^1.5.0" prebuild-install "^7.1.1" -bidi-js@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.3.tgz#6f8bcf3c877c4d9220ddf49b9bb6930c88f877d2" - integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw== - dependencies: - require-from-string "^2.0.2" - bignumber.js@^9.1.0: version "9.3.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.1.tgz#759c5aaddf2ffdc4f154f7b493e1c8770f88c4d7" @@ -2189,14 +2116,6 @@ cookies@^0.8.0: depd "~2.0.0" keygrip "~1.1.0" -css-tree@^3.0.0, css-tree@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.2.1.tgz#86cac7011561272b30e6b1e042ba6ce047aa7518" - integrity sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA== - dependencies: - mdn-data "2.27.1" - source-map-js "^1.2.1" - d3-array@^2.5.0: version "2.12.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" @@ -2236,19 +2155,6 @@ d3-voronoi@1.1.2: resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" integrity sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw== -data-urls@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-7.0.0.tgz#6dce8b63226a1ecfdd907ce18a8ccfb1eee506d3" - integrity sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA== - dependencies: - whatwg-mimetype "^5.0.0" - whatwg-url "^16.0.0" - -decimal.js@^10.6.0: - version "10.6.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" - integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -2318,11 +2224,6 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -entities@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-8.0.0.tgz#c1df5fe3602429747fa233d0dd26f142f0ce4743" - integrity sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA== - event-stream@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-4.0.1.tgz#4092808ec995d0dd75ea4580c1df6a74db2cde65" @@ -2528,13 +2429,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -html-encoding-sniffer@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz#f8d9390b3b348b50d4f61c16dd2ef5c05980a882" - integrity sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg== - dependencies: - "@exodus/bytes" "^1.6.0" - hyparquet-compressors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/hyparquet-compressors/-/hyparquet-compressors-1.1.1.tgz#35ab916d4ee3306000279126a937e9a1633725a6" @@ -2640,11 +2534,6 @@ is-inside-container@^1.0.0: dependencies: is-docker "^3.0.0" -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - is-wsl@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.1.tgz#327897b26832a3eb117da6c27492d04ca132594f" @@ -2657,33 +2546,6 @@ jpeg-js@^0.4.4: resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa" integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg== -jsdom@^30.0.0: - version "30.0.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-30.0.1.tgz#1b0751cbd0abce86762c48697583b5e91433f6e4" - integrity sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA== - dependencies: - "@asamuzakjp/css-color" "^6.0.5" - "@asamuzakjp/dom-selector" "^8.3.0" - "@bramus/specificity" "^2.4.2" - "@csstools/css-syntax-patches-for-csstree" "^1.1.7" - "@exodus/bytes" "^1.15.1" - css-tree "^3.2.1" - data-urls "^7.0.0" - decimal.js "^10.6.0" - html-encoding-sniffer "^6.0.0" - is-potential-custom-element-name "^1.0.1" - lru-cache "^11.5.2" - parse5 "^8.0.1" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^6.0.2" - undici "^8.9.0" - w3c-xmlserializer "^5.0.0" - webidl-conversions "^8.0.1" - whatwg-mimetype "^5.0.0" - whatwg-url "^17.1.0" - xml-name-validator "^5.0.0" - jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -2716,11 +2578,6 @@ lodash@4.18.1: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== -lru-cache@^11.5.2: - version "11.5.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.2.tgz#00e16665c90c620fba14a3c368732a976493f760" - integrity sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g== - map-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" @@ -2766,11 +2623,6 @@ mapshaper@^0.7.22: "@rollup/rollup-linux-x64-gnu" "^4.44.1" better-sqlite3 "^12.10.0" -mdn-data@2.27.1: - version "2.27.1" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.27.1.tgz#e37b9c50880b75366c4d40ac63d9bbcacdb61f0e" - integrity sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ== - mgrs@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mgrs/-/mgrs-1.0.0.tgz#fb91588e78c90025672395cb40b25f7cd6ad1829" @@ -2897,13 +2749,6 @@ parse-headers@^2.0.2: resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.6.tgz#7940f0abe5fe65df2dd25d4ce8800cb35b49d01c" integrity sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A== -parse5@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-8.0.1.tgz#f43bcd2cd683efe084075333e9ce0da7d06da31e" - integrity sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw== - dependencies: - entities "^8.0.0" - pause-stream@^0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -3018,11 +2863,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - queue@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.1.tgz#abd5a5b0376912f070a25729e0b6a7d565683791" @@ -3124,11 +2964,6 @@ reproject@1.2.5: minimist "^1.2.0" proj4 "^2.4.4" -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - resolve-protobuf-schema@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758" @@ -3176,13 +3011,6 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saxes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - semver@^7.3.5: version "7.8.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69" @@ -3222,11 +3050,6 @@ slice-source@0.4.1: resolved "https://registry.yarnpkg.com/slice-source/-/slice-source-0.4.1.tgz#40a57ac03c6668b5da200e05378e000bf2a61d79" integrity sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg== -source-map-js@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" - integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== - splaytree-ts@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/splaytree-ts/-/splaytree-ts-1.0.2.tgz#34963704587aff45eaa09c24713f552bbf56e8f0" @@ -3279,11 +3102,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - tar-fs@^2.0.0: version "2.1.5" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.5.tgz#33e9c29413dce0c58ada7ff77db4e5a30afffe70" @@ -3315,18 +3133,6 @@ tinyqueue@^2.0.3: resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== -tldts-core@^7.4.11: - version "7.4.11" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.4.11.tgz#a02fba29af72cbf9e658cb3a335f857efc9475f7" - integrity sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg== - -tldts@^7.0.5: - version "7.4.11" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.4.11.tgz#51d5a3feeb473e592edf671491a24fb23789eed2" - integrity sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw== - dependencies: - tldts-core "^7.4.11" - token-types@^4.1.1: version "4.2.1" resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.1.tgz#0f897f03665846982806e138977dbe72d44df753" @@ -3349,20 +3155,6 @@ topojson-server@3.x: dependencies: commander "2" -tough-cookie@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.2.tgz#7b1f22fcf2daf06c4ff9d53ec1845f44c6627062" - integrity sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA== - dependencies: - tldts "^7.0.5" - -tr46@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-6.0.0.tgz#f5a1ae546a0adb32a277a2278d0d17fa2f9093e6" - integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw== - dependencies: - punycode "^2.3.1" - tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" @@ -3390,11 +3182,6 @@ undici-types@~8.3.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-8.3.0.tgz#44e9fc9f3244648cdea35e4f9bb2d681e9410809" integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== -undici@^8.9.0: - version "8.10.2" - resolved "https://registry.yarnpkg.com/undici/-/undici-8.10.2.tgz#960bcb0b43c267f86910ea7ca408ada843a44bc7" - integrity sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ== - unzipit@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unzipit/-/unzipit-2.0.0.tgz#9461b6a9b067ed9b13f9eda1bc5b93bc448307fa" @@ -3405,51 +3192,16 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -w3c-xmlserializer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" - integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== - dependencies: - xml-name-validator "^5.0.0" - web-worker@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.5.0.tgz#71b2b0fbcc4293e8f0aa4f6b8a3ffebff733dcc5" integrity sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw== -webidl-conversions@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz#0657e571fe6f06fcb15ca50ed1fdbcb495cd1686" - integrity sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ== - webworkify@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/webworkify/-/webworkify-1.5.0.tgz#734ad87a774de6ebdd546e1d3e027da5b8f4a42c" integrity sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g== -whatwg-mimetype@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz#d8232895dbd527ceaee74efd4162008fb8a8cf48" - integrity sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw== - -whatwg-url@^16.0.0: - version "16.0.1" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-16.0.1.tgz#047f7f4bd36ef76b7198c172d1b1cebc66f764dd" - integrity sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw== - dependencies: - "@exodus/bytes" "^1.11.0" - tr46 "^6.0.0" - webidl-conversions "^8.0.1" - -whatwg-url@^17.1.0: - version "17.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-17.1.0.tgz#693144cd2060d324e73b15a717d1f38ecfb3f02e" - integrity sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw== - dependencies: - "@exodus/bytes" "^1.15.1" - tr46 "^6.0.0" - webidl-conversions "^8.0.1" - wkt-parser@^1.3.1, wkt-parser@^1.5.5: version "1.5.6" resolved "https://registry.yarnpkg.com/wkt-parser/-/wkt-parser-1.5.6.tgz#3a19521914b2a21b7a8203590f5b180b9dba8dfa" @@ -3475,21 +3227,11 @@ wsl-utils@^1.0.0: is-wsl "^3.1.0" powershell-utils "^0.1.0" -xml-name-validator@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" - integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== - xml-utils@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.10.2.tgz#436b39ccc25a663ce367ea21abb717afdea5d6b1" integrity sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA== -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - zarrita@^0.7.1: version "0.7.5" resolved "https://registry.yarnpkg.com/zarrita/-/zarrita-0.7.5.tgz#74a4d5d5047cd1e930bffaed9234b62437a3fe13"