mirror of
https://github.com/SigNoz/signoz.git
synced 2026-10-16 07:01:11 +01:00
Compare commits
56 Commits
feat/scatt
...
feat/geo-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f26c4a5892 | ||
|
|
8f978e8033 | ||
|
|
252ac15309 | ||
|
|
77650c4494 | ||
|
|
a4a67abd6b | ||
|
|
6cd80789e6 | ||
|
|
55d2afaab2 | ||
|
|
6966d30981 | ||
|
|
a0192fa740 | ||
|
|
940b49385f | ||
|
|
cb2b45ce6e | ||
|
|
0a63c42a2f | ||
|
|
a3c5dbde7a | ||
|
|
5892afe538 | ||
|
|
6be0608e08 | ||
|
|
5c6b38c364 | ||
|
|
112a0a5c9f | ||
|
|
87b33bf9cc | ||
|
|
c2c97067be | ||
|
|
d09517b66c | ||
|
|
413c08d965 | ||
|
|
942716d773 | ||
|
|
3acf5221af | ||
|
|
2f350f5f19 | ||
|
|
b49209e811 | ||
|
|
17b740015a | ||
|
|
38d1b69fd4 | ||
|
|
cd3ae16360 | ||
|
|
a82c241ea2 | ||
|
|
9b40cf8b5c | ||
|
|
e9235a10fd | ||
|
|
68476e25f7 | ||
|
|
5ba7a4c86c | ||
|
|
b7b3e48a5d | ||
|
|
ab4bb5a6a5 | ||
|
|
ab5d035022 | ||
|
|
87ec6e9cf8 | ||
|
|
8d93e46b2a | ||
|
|
af55cc43cc | ||
|
|
11d91eb9eb | ||
|
|
917e5da2b7 | ||
|
|
de3d950716 | ||
|
|
bdf7e1708b | ||
|
|
76489f4b43 | ||
|
|
6bf9153612 | ||
|
|
2962c900cc | ||
|
|
b1bcca1332 | ||
|
|
e3f53736ed | ||
|
|
7b05bc3ed4 | ||
|
|
38fe40bbec | ||
|
|
d4fb1bfcfc | ||
|
|
f56517abd4 | ||
|
|
135097d609 | ||
|
|
230d24416f | ||
|
|
652e765f88 | ||
|
|
7ee496c537 |
@@ -3739,6 +3739,11 @@ components:
|
||||
timePreference:
|
||||
$ref: '#/components/schemas/DashboardtypesTimePreference'
|
||||
type: object
|
||||
DashboardtypesBubbleRadius:
|
||||
maximum: 64
|
||||
minimum: 1
|
||||
nullable: true
|
||||
type: integer
|
||||
DashboardtypesBuilderQuerySpec:
|
||||
discriminator:
|
||||
mapping:
|
||||
@@ -3911,6 +3916,149 @@ components:
|
||||
minimum: 0
|
||||
nullable: true
|
||||
type: number
|
||||
DashboardtypesGeoMapBubbles:
|
||||
properties:
|
||||
maxRadius:
|
||||
$ref: '#/components/schemas/DashboardtypesBubbleRadius'
|
||||
minRadius:
|
||||
$ref: '#/components/schemas/DashboardtypesBubbleRadius'
|
||||
type: object
|
||||
DashboardtypesGeoMapChartAppearance:
|
||||
properties:
|
||||
bubbles:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapBubbles'
|
||||
colors:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapColors'
|
||||
labels:
|
||||
description: Shows country and state names once each fits inside its shape.
|
||||
nullable: true
|
||||
type: boolean
|
||||
type: object
|
||||
DashboardtypesGeoMapColors:
|
||||
properties:
|
||||
fill:
|
||||
type: string
|
||||
max:
|
||||
nullable: true
|
||||
type: number
|
||||
min:
|
||||
nullable: true
|
||||
type: number
|
||||
mode:
|
||||
$ref: '#/components/schemas/DashboardtypesHeatmapColorMode'
|
||||
palette:
|
||||
$ref: '#/components/schemas/DashboardtypesHeatmapPalette'
|
||||
scale:
|
||||
$ref: '#/components/schemas/DashboardtypesHeatmapColorScale'
|
||||
steps:
|
||||
type: integer
|
||||
type: object
|
||||
DashboardtypesGeoMapCountryFormat:
|
||||
enum:
|
||||
- auto
|
||||
- alpha2
|
||||
- alpha3
|
||||
- name
|
||||
type: string
|
||||
DashboardtypesGeoMapLayer:
|
||||
enum:
|
||||
- regions
|
||||
- bubbles
|
||||
- points
|
||||
type: string
|
||||
DashboardtypesGeoMapLocation:
|
||||
properties:
|
||||
country:
|
||||
description: Group-by key holding the country, for the country and subdivision
|
||||
modes.
|
||||
type: string
|
||||
countryFormat:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapCountryFormat'
|
||||
lat:
|
||||
description: Group-by key holding the latitude, for the coordinates mode.
|
||||
type: string
|
||||
lon:
|
||||
description: Group-by key holding the longitude, for the coordinates mode.
|
||||
type: string
|
||||
mode:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapLocationMode'
|
||||
region:
|
||||
description: Group-by key holding the ISO 3166-2 region code (e.g. geo.region.iso_code),
|
||||
for the subdivision mode.
|
||||
type: string
|
||||
type: object
|
||||
DashboardtypesGeoMapLocationMode:
|
||||
enum:
|
||||
- auto
|
||||
- country
|
||||
- subdivision
|
||||
- coordinates
|
||||
type: string
|
||||
DashboardtypesGeoMapPanelSpec:
|
||||
properties:
|
||||
chartAppearance:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapChartAppearance'
|
||||
formatting:
|
||||
$ref: '#/components/schemas/DashboardtypesTableFormatting'
|
||||
legend:
|
||||
$ref: '#/components/schemas/DashboardtypesLegend'
|
||||
location:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapLocation'
|
||||
map:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapSettings'
|
||||
thresholds:
|
||||
items:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapThreshold'
|
||||
nullable: true
|
||||
type: array
|
||||
visualization:
|
||||
$ref: '#/components/schemas/DashboardtypesBasicVisualization'
|
||||
type: object
|
||||
DashboardtypesGeoMapSettings:
|
||||
properties:
|
||||
colorBy:
|
||||
description: Value column key that sets the colour. Empty uses the first
|
||||
value column.
|
||||
type: string
|
||||
layer:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapLayer'
|
||||
sizeBy:
|
||||
description: Value column key that sets the bubble or point size. Empty
|
||||
uses colorBy.
|
||||
type: string
|
||||
view:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapView'
|
||||
viewCountry:
|
||||
description: ISO 3166-1 alpha-2 code of the country the country view shows.
|
||||
type: string
|
||||
type: object
|
||||
DashboardtypesGeoMapThreshold:
|
||||
properties:
|
||||
color:
|
||||
type: string
|
||||
operator:
|
||||
$ref: '#/components/schemas/DashboardtypesComparisonOperator'
|
||||
unit:
|
||||
type: string
|
||||
value:
|
||||
format: double
|
||||
type: number
|
||||
required:
|
||||
- value
|
||||
- color
|
||||
type: object
|
||||
DashboardtypesGeoMapView:
|
||||
enum:
|
||||
- world
|
||||
- fit
|
||||
- africa
|
||||
- asia
|
||||
- europe
|
||||
- north_america
|
||||
- south_america
|
||||
- oceania
|
||||
- country
|
||||
type: string
|
||||
DashboardtypesGettableDashboardV2:
|
||||
properties:
|
||||
createdAt:
|
||||
@@ -4446,6 +4594,7 @@ components:
|
||||
mapping:
|
||||
signoz/AreaChartPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAreaChartPanelSpec'
|
||||
signoz/BarChartPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBarChartPanelSpec'
|
||||
signoz/GeoMapPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpec'
|
||||
signoz/HeatmapPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHeatmapPanelSpec'
|
||||
signoz/HistogramPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHistogramPanelSpec'
|
||||
signoz/ListPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesListPanelSpec'
|
||||
@@ -4455,6 +4604,7 @@ components:
|
||||
signoz/TablePanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTablePanelSpec'
|
||||
signoz/TextPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTextPanelSpec'
|
||||
signoz/TimeSeriesPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpec'
|
||||
signoz/TopListPanel: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpec'
|
||||
propertyName: kind
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpec'
|
||||
@@ -4468,6 +4618,8 @@ components:
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTextPanelSpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHeatmapPanelSpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesScatterPlotPanelSpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpec'
|
||||
type: object
|
||||
DashboardtypesPanelPluginKind:
|
||||
enum:
|
||||
@@ -4482,6 +4634,8 @@ components:
|
||||
- signoz/TextPanel
|
||||
- signoz/HeatmapPanel
|
||||
- signoz/ScatterPlotPanel
|
||||
- signoz/TopListPanel
|
||||
- signoz/GeoMapPanel
|
||||
type: string
|
||||
DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAreaChartPanelSpec:
|
||||
properties:
|
||||
@@ -4507,6 +4661,18 @@ components:
|
||||
- kind
|
||||
- spec
|
||||
type: object
|
||||
DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpec:
|
||||
properties:
|
||||
kind:
|
||||
enum:
|
||||
- signoz/GeoMapPanel
|
||||
type: string
|
||||
spec:
|
||||
$ref: '#/components/schemas/DashboardtypesGeoMapPanelSpec'
|
||||
required:
|
||||
- kind
|
||||
- spec
|
||||
type: object
|
||||
DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHeatmapPanelSpec:
|
||||
properties:
|
||||
kind:
|
||||
@@ -4615,6 +4781,18 @@ components:
|
||||
- kind
|
||||
- spec
|
||||
type: object
|
||||
DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpec:
|
||||
properties:
|
||||
kind:
|
||||
enum:
|
||||
- signoz/TopListPanel
|
||||
type: string
|
||||
spec:
|
||||
$ref: '#/components/schemas/DashboardtypesTopListPanelSpec'
|
||||
required:
|
||||
- kind
|
||||
- spec
|
||||
type: object
|
||||
DashboardtypesPanelSpec:
|
||||
properties:
|
||||
display:
|
||||
@@ -5143,6 +5321,35 @@ components:
|
||||
timePreference:
|
||||
$ref: '#/components/schemas/DashboardtypesTimePreference'
|
||||
type: object
|
||||
DashboardtypesTopListAppearance:
|
||||
properties:
|
||||
barColor:
|
||||
description: Hex color of every bar; a matching background threshold overrides
|
||||
it.
|
||||
type: string
|
||||
showRank:
|
||||
description: Numbers each row by its position in the list.
|
||||
type: boolean
|
||||
showShare:
|
||||
description: Shows each row's share of the listed rows' total.
|
||||
type: boolean
|
||||
required:
|
||||
- barColor
|
||||
type: object
|
||||
DashboardtypesTopListPanelSpec:
|
||||
properties:
|
||||
appearance:
|
||||
$ref: '#/components/schemas/DashboardtypesTopListAppearance'
|
||||
formatting:
|
||||
$ref: '#/components/schemas/DashboardtypesPanelFormatting'
|
||||
thresholds:
|
||||
items:
|
||||
$ref: '#/components/schemas/DashboardtypesComparisonThreshold'
|
||||
nullable: true
|
||||
type: array
|
||||
visualization:
|
||||
$ref: '#/components/schemas/DashboardtypesBasicVisualization'
|
||||
type: object
|
||||
DashboardtypesUpdatableDashboardV2:
|
||||
properties:
|
||||
image:
|
||||
|
||||
@@ -56,10 +56,10 @@ const config: Config.InitialOptions = {
|
||||
transformIgnorePatterns: [
|
||||
// @chenglou/pretext is ESM-only; @signozhq/ui pulls it in via text-ellipsis.
|
||||
// Pattern 1: allow .pnpm virtual store through (handled by pattern 2), plus root-level ESM packages.
|
||||
'node_modules/(?!(\\.pnpm|react-json-tree|react-base16-styling|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou/pretext|@signozhq/design-tokens|@signozhq|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|remark-gfm|mdast-util-gfm|mdast-util-gfm-autolink-literal|mdast-util-gfm-footnote|mdast-util-gfm-strikethrough|mdast-util-gfm-table|mdast-util-gfm-task-list-item|mdast-util-find-and-replace|mdast-util-phrasing|mdast-util-to-markdown|markdown-table|longest-streak|ccount|escape-string-regexp|mdast-util-from-markdown|mdast-util-to-string|micromark|micromark-core-commonmark|micromark-extension-gfm|micromark-extension-gfm-autolink-literal|micromark-extension-gfm-footnote|micromark-extension-gfm-strikethrough|micromark-extension-gfm-table|micromark-extension-gfm-tagfilter|micromark-extension-gfm-task-list-item|micromark-factory-destination|micromark-factory-label|micromark-factory-space|micromark-factory-title|micromark-factory-whitespace|micromark-util-character|micromark-util-chunked|micromark-util-classify-character|micromark-util-combine-extensions|micromark-util-decode-numeric-character-reference|micromark-util-decode-string|micromark-util-encode|micromark-util-html-tag-name|micromark-util-normalize-identifier|micromark-util-resolve-all|micromark-util-sanitize-uri|micromark-util-subtokenize|micromark-util-symbol|micromark-util-types|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)/)',
|
||||
'node_modules/(?!(\\.pnpm|react-json-tree|react-base16-styling|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou/pretext|@signozhq/design-tokens|@signozhq|date-fns|d3-interpolate|d3-color|ol|rbush|quickselect|earcut|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|remark-gfm|mdast-util-gfm|mdast-util-gfm-autolink-literal|mdast-util-gfm-footnote|mdast-util-gfm-strikethrough|mdast-util-gfm-table|mdast-util-gfm-task-list-item|mdast-util-find-and-replace|mdast-util-phrasing|mdast-util-to-markdown|markdown-table|longest-streak|ccount|escape-string-regexp|mdast-util-from-markdown|mdast-util-to-string|micromark|micromark-core-commonmark|micromark-extension-gfm|micromark-extension-gfm-autolink-literal|micromark-extension-gfm-footnote|micromark-extension-gfm-strikethrough|micromark-extension-gfm-table|micromark-extension-gfm-tagfilter|micromark-extension-gfm-task-list-item|micromark-factory-destination|micromark-factory-label|micromark-factory-space|micromark-factory-title|micromark-factory-whitespace|micromark-util-character|micromark-util-chunked|micromark-util-classify-character|micromark-util-combine-extensions|micromark-util-decode-numeric-character-reference|micromark-util-decode-string|micromark-util-encode|micromark-util-html-tag-name|micromark-util-normalize-identifier|micromark-util-resolve-all|micromark-util-sanitize-uri|micromark-util-subtokenize|micromark-util-symbol|micromark-util-types|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)/)',
|
||||
// Pattern 2: pnpm virtual store — ignore everything except ESM-only packages.
|
||||
// pnpm encodes scoped packages as @scope+name@version, so match on scope prefix.
|
||||
'node_modules/\\.pnpm/(?!(react-json-tree|react-base16-styling|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou|@signozhq|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|remark-gfm|mdast-util-gfm|mdast-util-gfm-autolink-literal|mdast-util-gfm-footnote|mdast-util-gfm-strikethrough|mdast-util-gfm-table|mdast-util-gfm-task-list-item|mdast-util-find-and-replace|mdast-util-phrasing|mdast-util-to-markdown|markdown-table|longest-streak|ccount|escape-string-regexp|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)[^/]*/node_modules)',
|
||||
'node_modules/\\.pnpm/(?!(react-json-tree|react-base16-styling|lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou|@signozhq|date-fns|d3-interpolate|d3-color|ol|rbush|quickselect|earcut|api|@codemirror|@lezer|@marijn|@grafana|nuqs|uuid|copy-text-to-clipboard|react-markdown|vfile|vfile-message|unist-util-stringify-position|unified|bail|is-plain-obj|trough|remark-parse|remark-gfm|mdast-util-gfm|mdast-util-gfm-autolink-literal|mdast-util-gfm-footnote|mdast-util-gfm-strikethrough|mdast-util-gfm-table|mdast-util-gfm-task-list-item|mdast-util-find-and-replace|mdast-util-phrasing|mdast-util-to-markdown|markdown-table|longest-streak|ccount|escape-string-regexp|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|remark-rehype|mdast-util-to-hast|unist-util-position|trim-lines|unist-util-visit|unist-util-visit-parents|unist-util-is|unist-util-generated|mdast-util-definitions|property-information|hast-util-whitespace|space-separated-tokens|comma-separated-tokens|rehype-raw|hast-util-raw|hast-util-from-parse5|devlop|hastscript|hast-util-parse-selector|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)[^/]*/node_modules)',
|
||||
],
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"test": "jest",
|
||||
"test:changedsince": "jest --changedSince=main --coverage --silent",
|
||||
"generate:api": "orval --config ./orval.config.ts && sh scripts/post-types-generation.sh",
|
||||
"generate:config:web-settings": "json2ts ./src/schemas/generated/webSettings.schema.json -o src/types/generated/webSettings.ts --style.useTabs --style.tabWidth=1 --style.singleQuote --bannerComment '/* AUTO GENERATED FILE - DO NOT EDIT - GENERATED FROM frontend/src/schemas/generated/webSettings.schema.json */'"
|
||||
"generate:config:web-settings": "json2ts ./src/schemas/generated/webSettings.schema.json -o src/types/generated/webSettings.ts --style.useTabs --style.tabWidth=1 --style.singleQuote --bannerComment '/* AUTO GENERATED FILE - DO NOT EDIT - GENERATED FROM frontend/src/schemas/generated/webSettings.schema.json */'",
|
||||
"build:geo-outlines": "node scripts/geo-outlines/build.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
@@ -96,6 +97,7 @@
|
||||
"monaco-editor": "0.55.1",
|
||||
"motion": "12.4.13",
|
||||
"nuqs": "2.8.8",
|
||||
"ol": "10.11.0",
|
||||
"overlayscrollbars": "^2.16.0",
|
||||
"overlayscrollbars-react": "^0.5.6",
|
||||
"papaparse": "5.4.1",
|
||||
@@ -132,6 +134,7 @@
|
||||
"rrule": "2.8.1",
|
||||
"styled-components": "^5.3.11",
|
||||
"timestamp-nano": "^1.0.0",
|
||||
"topojson-client": "3.1.0",
|
||||
"translation-resilience": "^0.2.0",
|
||||
"typescript": "5.9.3",
|
||||
"uplot": "1.6.31",
|
||||
@@ -175,6 +178,7 @@
|
||||
"@types/crypto-js": "4.2.2",
|
||||
"@types/d3-hierarchy": "1.1.11",
|
||||
"@types/event-source-polyfill": "^1.0.0",
|
||||
"@types/geojson": "7946.0.16",
|
||||
"@types/history": "4.7.11",
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
@@ -192,8 +196,11 @@
|
||||
"@types/redux-mock-store": "1.0.4",
|
||||
"@types/styled-components": "^5.1.4",
|
||||
"@types/testing-library__jest-dom": "^5.14.5",
|
||||
"@types/topojson-client": "3.1.5",
|
||||
"@types/topojson-specification": "1.0.5",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260430.1",
|
||||
"d3-geo": "3.1.1",
|
||||
"eslint-plugin-sonarjs": "4.0.2",
|
||||
"glob": "^13.0.6",
|
||||
"husky": "^7.0.4",
|
||||
@@ -216,6 +223,8 @@
|
||||
"storybook": "10.5.9",
|
||||
"stylelint": "17.15.0",
|
||||
"svgo": "4.1.0",
|
||||
"topojson-server": "3.0.1",
|
||||
"topojson-simplify": "3.0.3",
|
||||
"ts-jest": "29.4.9",
|
||||
"typescript-plugin-css-modules": "5.2.0",
|
||||
"use-sync-external-store": "1.6.0",
|
||||
|
||||
191
frontend/pnpm-lock.yaml
generated
191
frontend/pnpm-lock.yaml
generated
@@ -203,6 +203,9 @@ importers:
|
||||
nuqs:
|
||||
specifier: 2.8.8
|
||||
version: 2.8.8(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
|
||||
ol:
|
||||
specifier: 10.11.0
|
||||
version: 10.11.0
|
||||
overlayscrollbars:
|
||||
specifier: ^2.16.0
|
||||
version: 2.16.0
|
||||
@@ -311,6 +314,9 @@ importers:
|
||||
timestamp-nano:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.1
|
||||
topojson-client:
|
||||
specifier: 3.1.0
|
||||
version: 3.1.0
|
||||
translation-resilience:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
@@ -399,6 +405,9 @@ importers:
|
||||
'@types/event-source-polyfill':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.1
|
||||
'@types/geojson':
|
||||
specifier: 7946.0.16
|
||||
version: 7946.0.16
|
||||
'@types/history':
|
||||
specifier: 4.7.11
|
||||
version: 4.7.11
|
||||
@@ -450,12 +459,21 @@ importers:
|
||||
'@types/testing-library__jest-dom':
|
||||
specifier: ^5.14.5
|
||||
version: 5.14.5
|
||||
'@types/topojson-client':
|
||||
specifier: 3.1.5
|
||||
version: 3.1.5
|
||||
'@types/topojson-specification':
|
||||
specifier: 1.0.5
|
||||
version: 1.0.5
|
||||
'@types/uuid':
|
||||
specifier: ^8.3.1
|
||||
version: 8.3.4
|
||||
'@typescript/native-preview':
|
||||
specifier: 7.0.0-dev.20260430.1
|
||||
version: 7.0.0-dev.20260430.1
|
||||
d3-geo:
|
||||
specifier: 3.1.1
|
||||
version: 3.1.1
|
||||
eslint-plugin-sonarjs:
|
||||
specifier: 4.0.2
|
||||
version: 4.0.2(eslint@10.2.1(jiti@2.6.1))
|
||||
@@ -522,6 +540,12 @@ importers:
|
||||
svgo:
|
||||
specifier: 4.1.0
|
||||
version: 4.1.0
|
||||
topojson-server:
|
||||
specifier: 3.0.1
|
||||
version: 3.0.1
|
||||
topojson-simplify:
|
||||
specifier: 3.0.3
|
||||
version: 3.0.3
|
||||
ts-jest:
|
||||
specifier: 29.4.9
|
||||
version: 29.4.9(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.2.0)(babel-jest@29.6.4(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.4.1)(jest@30.2.0(@types/node@16.18.25)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.16.2)(@types/node@16.18.25)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
@@ -2920,6 +2944,9 @@ packages:
|
||||
'@petamoriken/float16@3.8.0':
|
||||
resolution: {integrity: sha512-AhVAm6SQ+zgxIiOzwVdUcDmKlu/qU39FiYD2UD6kQQaVenrn0dGZewIghWAENGQsvC+1avLCuT+T2/3Gsp/W3w==}
|
||||
|
||||
'@petamoriken/float16@3.9.3':
|
||||
resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -4040,8 +4067,8 @@ packages:
|
||||
'@types/file-saver@2.0.5':
|
||||
resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==}
|
||||
|
||||
'@types/geojson@7946.0.14':
|
||||
resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==}
|
||||
'@types/geojson@7946.0.16':
|
||||
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
||||
|
||||
'@types/graceful-fs@4.1.6':
|
||||
resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
|
||||
@@ -4124,6 +4151,9 @@ packages:
|
||||
'@types/prop-types@15.7.5':
|
||||
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
|
||||
|
||||
'@types/rbush@4.0.0':
|
||||
resolution: {integrity: sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==}
|
||||
|
||||
'@types/react-addons-update@0.14.21':
|
||||
resolution: {integrity: sha512-HOxr0Hd8C1L4uw8DHyv2etqMVIj78oLEpe567/HgjoE+1Lc+PUsTGXTrkr1BDvFqsu5r49mSlgI5evwrk9eutA==}
|
||||
|
||||
@@ -4178,6 +4208,12 @@ packages:
|
||||
'@types/testing-library__jest-dom@5.14.5':
|
||||
resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==}
|
||||
|
||||
'@types/topojson-client@3.1.5':
|
||||
resolution: {integrity: sha512-C79rySTyPxnQNNguTZNI1Ct4D7IXgvyAs3p9HPecnl6mNrJ5+UhvGNYcZfpROYV2lMHI48kJPxwR+F9C6c7nmw==}
|
||||
|
||||
'@types/topojson-specification@1.0.5':
|
||||
resolution: {integrity: sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ==}
|
||||
|
||||
'@types/tough-cookie@4.0.5':
|
||||
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
|
||||
|
||||
@@ -4466,6 +4502,9 @@ packages:
|
||||
'@xobotyi/scrollbar-width@1.9.5':
|
||||
resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
|
||||
|
||||
'@zarrita/storage@0.2.0':
|
||||
resolution: {integrity: sha512-855ZXqtnds7spnT8vNvD+MXa3QExP1m2GqShe8yt7uZXHnQLgJHgkpVwFjE1B0KDDRO0ki09hmk6OboTaIfPsQ==}
|
||||
|
||||
'@zxing/text-encoding@0.9.0':
|
||||
resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==}
|
||||
|
||||
@@ -5299,6 +5338,10 @@ packages:
|
||||
resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-geo@3.1.1:
|
||||
resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
d3-hierarchy@1.1.9:
|
||||
resolution: {integrity: sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==}
|
||||
|
||||
@@ -5591,6 +5634,9 @@ packages:
|
||||
earcut@2.2.4:
|
||||
resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
|
||||
|
||||
earcut@3.2.4:
|
||||
resolution: {integrity: sha512-FHc6yNpnTORB0tM5kmZ03iD2mDuNakDE2lvf4Hw1XkfxtEHIGVi01B/GqUSwx+NcC1WGjidAfpTXzeALtPtfpg==}
|
||||
|
||||
eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
|
||||
@@ -5863,6 +5909,9 @@ packages:
|
||||
fflate@0.4.9:
|
||||
resolution: {integrity: sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==}
|
||||
|
||||
fflate@0.8.3:
|
||||
resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==}
|
||||
|
||||
figures@3.2.0:
|
||||
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -6043,6 +6092,10 @@ packages:
|
||||
resolution: {integrity: sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==}
|
||||
engines: {node: '>=10.19'}
|
||||
|
||||
geotiff@3.1.0-beta.0:
|
||||
resolution: {integrity: sha512-wbVwOaQYuN+ywly5NoMXn37nHslwkQg5EFHqMeUTtjDACrSag0EEVlFsc+2DK3Pzmmp6XzJW2RTZOamPsBM6ng==}
|
||||
engines: {node: '>=10.19'}
|
||||
|
||||
get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
@@ -7646,6 +7699,9 @@ packages:
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
numcodecs@0.3.2:
|
||||
resolution: {integrity: sha512-6YSPnmZgg0P87jnNhi3s+FVLOcIn3y+1CTIgUulA3IdASzK9fJM87sUFkpyA+be9GibGRaST2wCgkD+6U+fWKw==}
|
||||
|
||||
nuqs@2.8.8:
|
||||
resolution: {integrity: sha512-LF5sw9nWpHyPWzMMu9oho3r9C5DvkpmBIg4LQN78sexIzGaeRx8DWr0uy3YiFx5i2QGZN1Qqcb+OAtEVRa2bnA==}
|
||||
peerDependencies:
|
||||
@@ -7722,6 +7778,9 @@ packages:
|
||||
ol-mapbox-style@10.7.0:
|
||||
resolution: {integrity: sha512-S/UdYBuOjrotcR95Iq9AejGYbifKeZE85D9VtH11ryJLQPTZXZSW1J5bIXcr4AlAH6tyjPPHTK34AdkwB32Myw==}
|
||||
|
||||
ol@10.11.0:
|
||||
resolution: {integrity: sha512-OJ+lrx7kx/1O7Xx18Ff9QkVnwZIEL0OuhJQSy2sYgzzDvyZ2qHvuZEGAajbSstUyBwaS1OtQ4RWnY84tRMDdqg==}
|
||||
|
||||
ol@7.4.0:
|
||||
resolution: {integrity: sha512-bgBbiah694HhC0jt8ptEFNRXwgO8d6xWH3G97PCg4bmn9Li5nLLbi59oSrvqUI6VPVwonPQF1YcqJymxxyMC6A==}
|
||||
|
||||
@@ -7952,6 +8011,10 @@ packages:
|
||||
resolution: {integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==}
|
||||
hasBin: true
|
||||
|
||||
pbf@5.1.2:
|
||||
resolution: {integrity: sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==}
|
||||
hasBin: true
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
@@ -8157,12 +8220,18 @@ packages:
|
||||
quickselect@2.0.0:
|
||||
resolution: {integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==}
|
||||
|
||||
quickselect@3.0.0:
|
||||
resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==}
|
||||
|
||||
raf-schd@4.0.3:
|
||||
resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
|
||||
|
||||
rbush@3.0.1:
|
||||
resolution: {integrity: sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==}
|
||||
|
||||
rbush@4.0.1:
|
||||
resolution: {integrity: sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==}
|
||||
|
||||
rc-cascader@3.20.0:
|
||||
resolution: {integrity: sha512-lkT9EEwOcYdjZ/jvhLoXGzprK1sijT3/Tp4BLxQQcHDZkkOzzwYQC9HgmKoJz0K7CukMfgvO9KqHeBdgE+pELw==}
|
||||
peerDependencies:
|
||||
@@ -8730,6 +8799,9 @@ packages:
|
||||
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
reference-spec-reader@0.2.0:
|
||||
resolution: {integrity: sha512-q0mfCi5yZSSHXpCyxjgQeaORq3tvDsxDyzaadA/5+AbAUwRyRuuTh0aRQuE/vAOt/qzzxidJ5iDeu1cLHaNBlQ==}
|
||||
|
||||
refractor@3.6.0:
|
||||
resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==}
|
||||
|
||||
@@ -9425,6 +9497,18 @@ packages:
|
||||
toggle-selection@1.0.6:
|
||||
resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
|
||||
|
||||
topojson-client@3.1.0:
|
||||
resolution: {integrity: sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==}
|
||||
hasBin: true
|
||||
|
||||
topojson-server@3.0.1:
|
||||
resolution: {integrity: sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==}
|
||||
hasBin: true
|
||||
|
||||
topojson-simplify@3.0.3:
|
||||
resolution: {integrity: sha512-V+pBjLVzSQ3+hSOxBiV01OVXgFiCmMO8ia3huxKEyIMTC1ApQHBcdXdOqcQ6U2JJJD31TZduwY6KyF15R8sUgg==}
|
||||
hasBin: true
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -9670,6 +9754,10 @@ packages:
|
||||
unrs-resolver@1.11.1:
|
||||
resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
|
||||
|
||||
unzipit@2.0.0:
|
||||
resolution: {integrity: sha512-DVeVIWUZCAQPNzm5sB0hpsG1GygTTdBnzNtYYEpInkttx5evkyqRgZi6rTczoySqp8hO5jHVKzrH0f23X8FZLg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
update-browserslist-db@1.3.3:
|
||||
resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==}
|
||||
hasBin: true
|
||||
@@ -9998,6 +10086,9 @@ packages:
|
||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
xml-utils@1.10.2:
|
||||
resolution: {integrity: sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==}
|
||||
|
||||
xml-utils@1.7.0:
|
||||
resolution: {integrity: sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==}
|
||||
|
||||
@@ -10080,12 +10171,18 @@ packages:
|
||||
resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
zarrita@0.7.5:
|
||||
resolution: {integrity: sha512-ZriOSEne+Wf2iuM0yjDZvUKpHNlug3dO7qJ6mAxXoOgsYV18DPOkAkwB9e0rf21bHcKcOffFuwoZwn5g6pvUeA==}
|
||||
|
||||
zod@4.3.6:
|
||||
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
|
||||
|
||||
zstddec@0.1.0:
|
||||
resolution: {integrity: sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==}
|
||||
|
||||
zstddec@0.2.0:
|
||||
resolution: {integrity: sha512-oyPnDa1X5c13+Y7mA/FDMNJrn4S8UNBe0KCqtDmor40Re7ALrPN6npFwyYVRRh+PqozZQdeg23QtbcamZnG5rA==}
|
||||
|
||||
zustand@5.0.11:
|
||||
resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
@@ -12664,6 +12761,8 @@ snapshots:
|
||||
|
||||
'@petamoriken/float16@3.8.0': {}
|
||||
|
||||
'@petamoriken/float16@3.9.3': {}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
optional: true
|
||||
|
||||
@@ -13775,7 +13874,7 @@ snapshots:
|
||||
|
||||
'@types/d3-geo@3.1.0':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.14
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/d3-hierarchy@1.1.11': {}
|
||||
|
||||
@@ -13815,7 +13914,7 @@ snapshots:
|
||||
|
||||
'@types/file-saver@2.0.5': {}
|
||||
|
||||
'@types/geojson@7946.0.14': {}
|
||||
'@types/geojson@7946.0.16': {}
|
||||
|
||||
'@types/graceful-fs@4.1.6':
|
||||
dependencies:
|
||||
@@ -13904,6 +14003,8 @@ snapshots:
|
||||
|
||||
'@types/prop-types@15.7.5': {}
|
||||
|
||||
'@types/rbush@4.0.0': {}
|
||||
|
||||
'@types/react-addons-update@0.14.21':
|
||||
dependencies:
|
||||
'@types/react': 18.0.26
|
||||
@@ -13978,6 +14079,15 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/jest': 30.0.0
|
||||
|
||||
'@types/topojson-client@3.1.5':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
'@types/topojson-specification': 1.0.5
|
||||
|
||||
'@types/topojson-specification@1.0.5':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/tough-cookie@4.0.5': {}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
@@ -14275,6 +14385,11 @@ snapshots:
|
||||
|
||||
'@xobotyi/scrollbar-width@1.9.5': {}
|
||||
|
||||
'@zarrita/storage@0.2.0':
|
||||
dependencies:
|
||||
reference-spec-reader: 0.2.0
|
||||
unzipit: 2.0.0
|
||||
|
||||
'@zxing/text-encoding@0.9.0':
|
||||
optional: true
|
||||
|
||||
@@ -15196,6 +15311,10 @@ snapshots:
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
|
||||
d3-geo@3.1.1:
|
||||
dependencies:
|
||||
d3-array: 3.2.4
|
||||
|
||||
d3-hierarchy@1.1.9: {}
|
||||
|
||||
d3-hierarchy@3.1.2: {}
|
||||
@@ -15485,6 +15604,8 @@ snapshots:
|
||||
|
||||
earcut@2.2.4: {}
|
||||
|
||||
earcut@3.2.4: {}
|
||||
|
||||
eastasianwidth@0.2.0: {}
|
||||
|
||||
ejs@3.1.10:
|
||||
@@ -15805,6 +15926,8 @@ snapshots:
|
||||
|
||||
fflate@0.4.9: {}
|
||||
|
||||
fflate@0.8.3: {}
|
||||
|
||||
figures@3.2.0:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
@@ -15997,6 +16120,17 @@ snapshots:
|
||||
xml-utils: 1.7.0
|
||||
zstddec: 0.1.0
|
||||
|
||||
geotiff@3.1.0-beta.0:
|
||||
dependencies:
|
||||
'@petamoriken/float16': 3.9.3
|
||||
lerc: 3.0.0
|
||||
pako: 2.1.0
|
||||
parse-headers: 2.0.5
|
||||
quick-lru: 6.1.1
|
||||
web-worker: 1.5.0
|
||||
xml-utils: 1.10.2
|
||||
zstddec: 0.2.0
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
get-east-asian-width@1.5.0: {}
|
||||
@@ -18092,6 +18226,10 @@ snapshots:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
numcodecs@0.3.2:
|
||||
dependencies:
|
||||
fflate: 0.8.3
|
||||
|
||||
nuqs@2.8.8(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.0.0
|
||||
@@ -18170,6 +18308,15 @@ snapshots:
|
||||
mapbox-to-css-font: 2.4.5
|
||||
ol: 7.4.0
|
||||
|
||||
ol@10.11.0:
|
||||
dependencies:
|
||||
'@types/rbush': 4.0.0
|
||||
earcut: 3.2.4
|
||||
geotiff: 3.1.0-beta.0
|
||||
pbf: 5.1.2
|
||||
rbush: 4.0.1
|
||||
zarrita: 0.7.5
|
||||
|
||||
ol@7.4.0:
|
||||
dependencies:
|
||||
earcut: 2.2.4
|
||||
@@ -18508,6 +18655,10 @@ snapshots:
|
||||
ieee754: 1.2.1
|
||||
resolve-protobuf-schema: 2.1.0
|
||||
|
||||
pbf@5.1.2:
|
||||
dependencies:
|
||||
resolve-protobuf-schema: 2.1.0
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.2: {}
|
||||
@@ -18691,12 +18842,18 @@ snapshots:
|
||||
|
||||
quickselect@2.0.0: {}
|
||||
|
||||
quickselect@3.0.0: {}
|
||||
|
||||
raf-schd@4.0.3: {}
|
||||
|
||||
rbush@3.0.1:
|
||||
dependencies:
|
||||
quickselect: 2.0.0
|
||||
|
||||
rbush@4.0.1:
|
||||
dependencies:
|
||||
quickselect: 3.0.0
|
||||
|
||||
rc-cascader@3.20.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
@@ -19444,6 +19601,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
|
||||
reference-spec-reader@0.2.0: {}
|
||||
|
||||
refractor@3.6.0:
|
||||
dependencies:
|
||||
hastscript: 6.0.0
|
||||
@@ -20222,6 +20381,19 @@ snapshots:
|
||||
|
||||
toggle-selection@1.0.6: {}
|
||||
|
||||
topojson-client@3.1.0:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
|
||||
topojson-server@3.0.1:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
|
||||
topojson-simplify@3.0.3:
|
||||
dependencies:
|
||||
commander: 2.20.3
|
||||
topojson-client: 3.1.0
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
dependencies:
|
||||
psl: 1.15.0
|
||||
@@ -20488,6 +20660,8 @@ snapshots:
|
||||
'@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
|
||||
'@unrs/resolver-binding-win32-x64-msvc': 1.11.1
|
||||
|
||||
unzipit@2.0.0: {}
|
||||
|
||||
update-browserslist-db@1.3.3(browserslist@4.28.9):
|
||||
dependencies:
|
||||
browserslist: 4.28.9
|
||||
@@ -20829,6 +21003,8 @@ snapshots:
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
||||
xml-utils@1.10.2: {}
|
||||
|
||||
xml-utils@1.7.0: {}
|
||||
|
||||
xml@1.0.1: {}
|
||||
@@ -20905,10 +21081,17 @@ snapshots:
|
||||
|
||||
yoctocolors@2.1.2: {}
|
||||
|
||||
zarrita@0.7.5:
|
||||
dependencies:
|
||||
'@zarrita/storage': 0.2.0
|
||||
numcodecs: 0.3.2
|
||||
|
||||
zod@4.3.6: {}
|
||||
|
||||
zstddec@0.1.0: {}
|
||||
|
||||
zstddec@0.2.0: {}
|
||||
|
||||
zustand@5.0.11(@types/react@18.0.26)(immer@11.1.3)(react@18.2.0)(use-sync-external-store@1.6.0(react@18.2.0)):
|
||||
optionalDependencies:
|
||||
'@types/react': 18.0.26
|
||||
|
||||
208
frontend/scripts/geo-outlines/build.mjs
Normal file
208
frontend/scripts/geo-outlines/build.mjs
Normal file
@@ -0,0 +1,208 @@
|
||||
// Builds the Geo Map outlines assets: Natural Earth 50m admin-0 countries, and
|
||||
// 10m admin-1 states/provinces for every country with at least two of them.
|
||||
// Run by hand when bumping Natural Earth or editing regions/aliases; the output is committed.
|
||||
// Usage: pnpm build:geo-outlines
|
||||
//
|
||||
// Nothing is written until every invariant in validate.mjs holds. The assets
|
||||
// are built in a staging folder and swapped in whole, so a failed run leaves
|
||||
// the committed ones as they were.
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
renameSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { setTimeout as sleep } from 'node:timers/promises';
|
||||
import { gzipSync } from 'node:zlib';
|
||||
|
||||
import {
|
||||
ASSETS_DIR,
|
||||
COUNTRIES_URL,
|
||||
SUBDIVISION_LIST_FILE,
|
||||
SUBDIVISIONS_URL,
|
||||
} from './config.mjs';
|
||||
import { buildCountries } from './countries.mjs';
|
||||
import { buildSubdivisions } from './subdivisions.mjs';
|
||||
import { validate } from './validate.mjs';
|
||||
|
||||
const DOWNLOAD_TIMEOUT_MS = 120_000;
|
||||
const DOWNLOAD_ATTEMPTS = 3;
|
||||
|
||||
const STAGING_DIR = `${ASSETS_DIR}.staging`;
|
||||
const PREVIOUS_DIR = `${ASSETS_DIR}.previous`;
|
||||
// Everything in the assets folder is generated; anything else would be lost
|
||||
// in the swap, so its presence stops the build.
|
||||
const GENERATED_ENTRIES = new Set([
|
||||
'countries-50m.json',
|
||||
'lookups.json',
|
||||
'subdivisions',
|
||||
]);
|
||||
|
||||
const kb = (bytes) => `${(bytes / 1024).toFixed(0)} KB`;
|
||||
const sizeOf = (json) =>
|
||||
`${kb(Buffer.byteLength(json))} raw / ${kb(gzipSync(json).length)} gzip`;
|
||||
|
||||
async function downloadFeatures(url) {
|
||||
for (let attempt = 1; ; attempt += 1) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
const body = await response.json();
|
||||
if (
|
||||
body?.type !== 'FeatureCollection' ||
|
||||
!Array.isArray(body.features) ||
|
||||
body.features.length === 0
|
||||
) {
|
||||
throw new Error('response is not a non-empty GeoJSON FeatureCollection');
|
||||
}
|
||||
return body;
|
||||
} catch (error) {
|
||||
if (attempt === DOWNLOAD_ATTEMPTS) {
|
||||
throw new Error(
|
||||
`download failed after ${attempt} attempts: ${url}\n ${error.message}`,
|
||||
);
|
||||
}
|
||||
await sleep(attempt * 2_000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function subdivisionListSource(countries) {
|
||||
return `// Generated by scripts/geo-outlines/build.mjs; do not edit.
|
||||
export const SUBDIVISION_COUNTRY_CODES = [
|
||||
${countries.map((country) => `\t'${country}',`).join('\n')}
|
||||
] as const;
|
||||
`;
|
||||
}
|
||||
|
||||
// A run that died mid-swap leaves the assets in the previous folder.
|
||||
function recoverInterruptedSwap() {
|
||||
if (!existsSync(ASSETS_DIR) && existsSync(PREVIOUS_DIR)) {
|
||||
renameSync(PREVIOUS_DIR, ASSETS_DIR);
|
||||
}
|
||||
rmSync(STAGING_DIR, { recursive: true, force: true });
|
||||
rmSync(PREVIOUS_DIR, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function assertOnlyGeneratedEntries() {
|
||||
if (!existsSync(ASSETS_DIR)) {
|
||||
return;
|
||||
}
|
||||
const foreign = readdirSync(ASSETS_DIR).filter(
|
||||
(entry) => !GENERATED_ENTRIES.has(entry),
|
||||
);
|
||||
if (foreign.length > 0) {
|
||||
throw new Error(
|
||||
`${ASSETS_DIR} holds files this build doesn't generate: ${foreign.join(', ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes every output under the staging folder; returns the sizes report. */
|
||||
function stage(outputs) {
|
||||
const subdivisionDir = join(STAGING_DIR, 'subdivisions');
|
||||
mkdirSync(subdivisionDir, { recursive: true });
|
||||
writeFileSync(join(STAGING_DIR, 'countries-50m.json'), outputs.countriesJson);
|
||||
writeFileSync(join(STAGING_DIR, 'lookups.json'), outputs.lookupsJson);
|
||||
const sizes = [];
|
||||
for (const [country, file] of outputs.subdivisionFiles) {
|
||||
const json = JSON.stringify(file);
|
||||
writeFileSync(join(subdivisionDir, `${country}.json`), json);
|
||||
sizes.push({
|
||||
country,
|
||||
shapes: file.objects.subdivisions.geometries.length,
|
||||
gzip: gzipSync(json).length,
|
||||
});
|
||||
}
|
||||
writeFileSync(`${SUBDIVISION_LIST_FILE}.staging`, outputs.subdivisionList);
|
||||
return sizes;
|
||||
}
|
||||
|
||||
/** Swaps the staged assets in; puts the previous ones back if that fails. */
|
||||
function commitStaged() {
|
||||
const hadAssets = existsSync(ASSETS_DIR);
|
||||
if (hadAssets) {
|
||||
renameSync(ASSETS_DIR, PREVIOUS_DIR);
|
||||
}
|
||||
try {
|
||||
renameSync(STAGING_DIR, ASSETS_DIR);
|
||||
} catch (error) {
|
||||
if (hadAssets) {
|
||||
renameSync(PREVIOUS_DIR, ASSETS_DIR);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
renameSync(`${SUBDIVISION_LIST_FILE}.staging`, SUBDIVISION_LIST_FILE);
|
||||
rmSync(PREVIOUS_DIR, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function cleanUpStaging() {
|
||||
rmSync(STAGING_DIR, { recursive: true, force: true });
|
||||
rmSync(`${SUBDIVISION_LIST_FILE}.staging`, { force: true });
|
||||
}
|
||||
|
||||
function report(countries, subdivisions, outputs, sizes) {
|
||||
const totalGzip = sizes.reduce((sum, { gzip }) => sum + gzip, 0);
|
||||
return {
|
||||
countries: {
|
||||
...countries.report,
|
||||
sizes: {
|
||||
'countries-50m.json': sizeOf(outputs.countriesJson),
|
||||
'lookups.json': sizeOf(outputs.lookupsJson),
|
||||
},
|
||||
},
|
||||
subdivisions: {
|
||||
...subdivisions.report,
|
||||
countries: sizes.length,
|
||||
totalGzip: kb(totalGzip),
|
||||
largest: [...sizes]
|
||||
.sort((a, b) => b.gzip - a.gzip)
|
||||
.slice(0, 10)
|
||||
.map(
|
||||
({ country, shapes, gzip }) =>
|
||||
`${country}: ${shapes} shapes, ${kb(gzip)} gzip`,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
recoverInterruptedSwap();
|
||||
assertOnlyGeneratedEntries();
|
||||
|
||||
const [countrySource, subdivisionSource] = await Promise.all([
|
||||
downloadFeatures(COUNTRIES_URL),
|
||||
downloadFeatures(SUBDIVISIONS_URL),
|
||||
]);
|
||||
const countries = buildCountries(countrySource);
|
||||
const subdivisions = buildSubdivisions(subdivisionSource);
|
||||
validate(countries, subdivisions);
|
||||
|
||||
const outputs = {
|
||||
countriesJson: JSON.stringify(countries.topology),
|
||||
lookupsJson: JSON.stringify(countries.lookups),
|
||||
subdivisionFiles: subdivisions.files,
|
||||
subdivisionList: subdivisionListSource([...subdivisions.files.keys()]),
|
||||
};
|
||||
try {
|
||||
const sizes = stage(outputs);
|
||||
commitStaged();
|
||||
process.stdout.write(
|
||||
`${JSON.stringify(report(countries, subdivisions, outputs, sizes), null, 2)}\n`,
|
||||
);
|
||||
} finally {
|
||||
cleanUpStaging();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
process.stderr.write(`Geo outlines build failed: ${error.message}\n`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
176
frontend/scripts/geo-outlines/config.mjs
Normal file
176
frontend/scripts/geo-outlines/config.mjs
Normal file
@@ -0,0 +1,176 @@
|
||||
// Inputs and tuning for the Geo Map outlines build. Tables only; no logic.
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const NATURAL_EARTH_BASE =
|
||||
'https://raw.githubusercontent.com/nvkelso/natural-earth-vector/v5.1.2/geojson';
|
||||
export const COUNTRIES_URL = `${NATURAL_EARTH_BASE}/ne_50m_admin_0_countries.geojson`;
|
||||
export const SUBDIVISIONS_URL = `${NATURAL_EARTH_BASE}/ne_10m_admin_1_states_provinces.geojson`;
|
||||
|
||||
const GEO_MAP_DIR = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
'../../src/lib/visualization/charts/GeoMap',
|
||||
);
|
||||
export const ASSETS_DIR = join(GEO_MAP_DIR, 'assets');
|
||||
export const SUBDIVISION_LIST_FILE = join(
|
||||
GEO_MAP_DIR,
|
||||
'geo/subdivisionCountries.ts',
|
||||
);
|
||||
|
||||
export const COUNTRY_QUANTIZATION = 1e5;
|
||||
// Simplification drops every point whose triangle with its neighbours is
|
||||
// smaller than this, in square degrees. A fixed size, unlike a share of all
|
||||
// points, still leaves a small island a few points of its own.
|
||||
export const COUNTRY_MIN_WEIGHT = 3e-3;
|
||||
// 10m is street-level detail; a country view needs far less.
|
||||
export const SUBDIVISION_QUANTIZATION = 1e4;
|
||||
export const SUBDIVISION_MIN_WEIGHT = 1e-3;
|
||||
// A shape this many times the minimum weight or smaller could lose every polygon.
|
||||
export const PROTECTED_SHAPE_FACTOR = 10;
|
||||
|
||||
// A country view needs at least this many coded states to be worth drawing.
|
||||
export const MIN_SUBDIVISIONS = 2;
|
||||
|
||||
// ISO 3166-2 codes that changed after Natural Earth v5.1.2, or older spellings
|
||||
// still in GeoIP data, mapped to the code the shape carries.
|
||||
export const SUBDIVISION_ALIASES = {
|
||||
IN: {
|
||||
'IN-CG': 'IN-CT',
|
||||
'IN-UK': 'IN-UT',
|
||||
'IN-TS': 'IN-TG',
|
||||
'IN-OD': 'IN-OR',
|
||||
'IN-DD': 'IN-DH',
|
||||
'IN-DN': 'IN-DH',
|
||||
},
|
||||
// Natural Earth carries Metro Manila under its retired code.
|
||||
PH: { 'PH-00': 'PH-MNL' },
|
||||
};
|
||||
|
||||
// Shapes Natural Earth still splits along retired lines, merged into the codes
|
||||
// GeoIP reports now; the retired codes become aliases. Northern Ireland's 26
|
||||
// districts became 11 councils in 2015, each a union of old districts.
|
||||
// Current code → [current name, retired codes].
|
||||
export const SUBDIVISION_MERGES = {
|
||||
GB: {
|
||||
'GB-ANN': ['Antrim and Newtownabbey', ['GB-ANT', 'GB-NTA']],
|
||||
'GB-AND': ['Ards and North Down', ['GB-ARD', 'GB-NDN']],
|
||||
'GB-ABC': [
|
||||
'Armagh City, Banbridge and Craigavon',
|
||||
['GB-ARM', 'GB-BNB', 'GB-CGV'],
|
||||
],
|
||||
'GB-BFS': ['Belfast', ['GB-BFS']],
|
||||
'GB-CCG': [
|
||||
'Causeway Coast and Glens',
|
||||
['GB-BLY', 'GB-CLR', 'GB-LMV', 'GB-MYL'],
|
||||
],
|
||||
'GB-DRS': ['Derry City and Strabane', ['GB-DRY', 'GB-STB']],
|
||||
'GB-FMO': ['Fermanagh and Omagh', ['GB-FER', 'GB-OMH']],
|
||||
'GB-LBC': ['Lisburn and Castlereagh', ['GB-LSB', 'GB-CSR']],
|
||||
'GB-MEA': ['Mid and East Antrim', ['GB-BLA', 'GB-CKF', 'GB-LRN']],
|
||||
'GB-MUL': ['Mid Ulster', ['GB-CKT', 'GB-DGN', 'GB-MFT']],
|
||||
'GB-NMD': ['Newry, Mourne and Down', ['GB-NYM', 'GB-DOW']],
|
||||
},
|
||||
};
|
||||
|
||||
// Natural Earth puts these islands in "Seven seas (open ocean)".
|
||||
export const OPEN_OCEAN_REGION = {
|
||||
Africa: 'africa',
|
||||
Asia: 'asia',
|
||||
Americas: 'south_america',
|
||||
};
|
||||
export const CONTINENT_REGION = {
|
||||
Africa: 'africa',
|
||||
Asia: 'asia',
|
||||
Europe: 'europe',
|
||||
'North America': 'north_america',
|
||||
'South America': 'south_america',
|
||||
Oceania: 'oceania',
|
||||
};
|
||||
// Transcontinental countries belong to every region they span.
|
||||
export const EXTRA_REGIONS = {
|
||||
RU: ['asia'],
|
||||
TR: ['europe'],
|
||||
KZ: ['europe'],
|
||||
GE: ['europe'],
|
||||
AM: ['europe'],
|
||||
AZ: ['europe'],
|
||||
CY: ['europe'],
|
||||
EG: ['asia'],
|
||||
};
|
||||
|
||||
// ISO codes GeoIP emits for places Natural Earth draws inside another country.
|
||||
export const COUNTRY_ALIASES = {
|
||||
GF: 'FR',
|
||||
GP: 'FR',
|
||||
MQ: 'FR',
|
||||
RE: 'FR',
|
||||
YT: 'FR',
|
||||
BQ: 'NL',
|
||||
SJ: 'NO',
|
||||
BV: 'NO',
|
||||
CX: 'AU',
|
||||
CC: 'AU',
|
||||
HM: 'AU',
|
||||
UM: 'US',
|
||||
TK: 'NZ',
|
||||
AC: 'SH',
|
||||
TA: 'SH',
|
||||
DG: 'IO',
|
||||
CP: 'FR',
|
||||
FX: 'FR',
|
||||
EA: 'ES',
|
||||
IC: 'ES',
|
||||
CQ: 'GB',
|
||||
GI: 'GB',
|
||||
// Retired or reserved codes still seen in older data.
|
||||
UK: 'GB',
|
||||
SU: 'RU',
|
||||
YU: 'RS',
|
||||
CS: 'RS',
|
||||
TP: 'TL',
|
||||
BU: 'MM',
|
||||
ZR: 'CD',
|
||||
DD: 'DE',
|
||||
VD: 'VN',
|
||||
YD: 'YE',
|
||||
RH: 'ZW',
|
||||
NH: 'VU',
|
||||
HV: 'BF',
|
||||
DY: 'BJ',
|
||||
AN: 'CW',
|
||||
};
|
||||
export const COUNTRY_NAME_ALIASES = {
|
||||
usa: 'US',
|
||||
'united states of america': 'US',
|
||||
america: 'US',
|
||||
uk: 'GB',
|
||||
'great britain': 'GB',
|
||||
britain: 'GB',
|
||||
england: 'GB',
|
||||
russia: 'RU',
|
||||
'south korea': 'KR',
|
||||
'north korea': 'KP',
|
||||
czechia: 'CZ',
|
||||
'czech republic': 'CZ',
|
||||
'ivory coast': 'CI',
|
||||
turkiye: 'TR',
|
||||
türkiye: 'TR',
|
||||
burma: 'MM',
|
||||
'cape verde': 'CV',
|
||||
swaziland: 'SZ',
|
||||
macedonia: 'MK',
|
||||
'east timor': 'TL',
|
||||
vatican: 'VA',
|
||||
};
|
||||
|
||||
// Alpha-2 codes Intl names that aren't countries, so none needs a shape.
|
||||
export const NON_COUNTRY_CODES = new Set([
|
||||
'EU',
|
||||
'EZ',
|
||||
'UN',
|
||||
'ZZ',
|
||||
'QO',
|
||||
'XA',
|
||||
'XB',
|
||||
'AQ',
|
||||
]);
|
||||
185
frontend/scripts/geo-outlines/countries.mjs
Normal file
185
frontend/scripts/geo-outlines/countries.mjs
Normal file
@@ -0,0 +1,185 @@
|
||||
// Natural Earth admin-0 → the countries topology and the lookup tables.
|
||||
import { geoArea, geoBounds } from 'd3-geo';
|
||||
import { feature } from 'topojson-client';
|
||||
|
||||
import {
|
||||
CONTINENT_REGION,
|
||||
COUNTRY_ALIASES,
|
||||
COUNTRY_MIN_WEIGHT,
|
||||
COUNTRY_NAME_ALIASES,
|
||||
COUNTRY_QUANTIZATION,
|
||||
EXTRA_REGIONS,
|
||||
NON_COUNTRY_CODES,
|
||||
OPEN_OCEAN_REGION,
|
||||
} from './config.mjs';
|
||||
import {
|
||||
polygonsOf,
|
||||
round,
|
||||
roundBounds,
|
||||
simplifyAndQuantize,
|
||||
} from './geometry.mjs';
|
||||
|
||||
export const isCountryCode = (value) => /^[A-Z]{2}$/.test(value ?? '');
|
||||
|
||||
function regionsOf(props, iso2) {
|
||||
const base = props.CONTINENT.startsWith('Seven seas')
|
||||
? OPEN_OCEAN_REGION[props.REGION_UN]
|
||||
: CONTINENT_REGION[props.CONTINENT];
|
||||
return [...new Set([base, ...(EXTRA_REGIONS[iso2] ?? [])].filter(Boolean))];
|
||||
}
|
||||
|
||||
// Group features by code so a country's overseas parts (Australia's Indian
|
||||
// Ocean territories) become one shape that one data row colours.
|
||||
function groupByCode(sourceFeatures, report) {
|
||||
const groups = new Map();
|
||||
for (const f of sourceFeatures) {
|
||||
const p = f.properties;
|
||||
if (p.CONTINENT === 'Antarctica') {
|
||||
report.dropped.push(p.NAME);
|
||||
continue;
|
||||
}
|
||||
const iso2 = isCountryCode(p.ISO_A2_EH) ? p.ISO_A2_EH : null;
|
||||
const key = iso2 ?? `uncoded:${p.NAME}`;
|
||||
if (!iso2) {
|
||||
report.uncoded.push(p.NAME);
|
||||
}
|
||||
if (!groups.has(key)) {
|
||||
groups.set(key, []);
|
||||
}
|
||||
groups.get(key).push(f);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
function toCountryFeature(key, members, report) {
|
||||
// The sovereign part carries the name, label point and region.
|
||||
const primary =
|
||||
members.find((f) => f.properties.ISO_A2 === f.properties.ISO_A2_EH) ??
|
||||
members.reduce((a, b) => (geoArea(a) >= geoArea(b) ? a : b));
|
||||
const p = primary.properties;
|
||||
const iso2 = key.startsWith('uncoded:') ? null : key;
|
||||
if (members.length > 1) {
|
||||
report.merged.push(
|
||||
`${iso2}: ${members.map((f) => f.properties.NAME).join(' + ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
const polygons = members.flatMap((f) => polygonsOf(f.geometry));
|
||||
const geometry = { type: 'MultiPolygon', coordinates: polygons };
|
||||
const mainland = polygons
|
||||
.map((coordinates) => ({ type: 'Polygon', coordinates }))
|
||||
.reduce((a, b) => (geoArea(a) >= geoArea(b) ? a : b));
|
||||
|
||||
return {
|
||||
type: 'Feature',
|
||||
id: iso2 ?? p.NAME,
|
||||
properties: {
|
||||
code: iso2,
|
||||
iso3: /^[A-Z]{3}$/.test(p.ISO_A3_EH) ? p.ISO_A3_EH : null,
|
||||
name: p.NAME,
|
||||
label: [round(p.LABEL_X), round(p.LABEL_Y)],
|
||||
mainland: roundBounds(geoBounds(mainland)),
|
||||
bounds: roundBounds(geoBounds(geometry)),
|
||||
regions: regionsOf(p, iso2 ?? ''),
|
||||
},
|
||||
geometry,
|
||||
};
|
||||
}
|
||||
|
||||
/** Alpha-3 codes and English names → alpha-2, for normalising data. */
|
||||
function buildLookups(sourceFeatures) {
|
||||
const alpha3 = {};
|
||||
const names = { ...COUNTRY_NAME_ALIASES };
|
||||
for (const { properties: p } of sourceFeatures) {
|
||||
if (!isCountryCode(p.ISO_A2_EH) || p.CONTINENT === 'Antarctica') {
|
||||
continue;
|
||||
}
|
||||
if (/^[A-Z]{3}$/.test(p.ISO_A3_EH)) {
|
||||
alpha3[p.ISO_A3_EH] = p.ISO_A2_EH;
|
||||
}
|
||||
for (const name of [
|
||||
p.NAME,
|
||||
p.NAME_LONG,
|
||||
p.ADMIN,
|
||||
p.NAME_EN,
|
||||
p.BRK_NAME,
|
||||
p.NAME_SORT,
|
||||
p.NAME_CIAWF,
|
||||
]) {
|
||||
if (name) {
|
||||
names[name.toLowerCase()] ??= p.ISO_A2_EH;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { alpha3, names, aliases: COUNTRY_ALIASES };
|
||||
}
|
||||
|
||||
/** ISO alpha-2 codes Intl knows that neither a shape nor an alias covers. */
|
||||
function findUncoveredCodes(drawn) {
|
||||
const display = new Intl.DisplayNames(['en'], {
|
||||
type: 'region',
|
||||
fallback: 'none',
|
||||
});
|
||||
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const uncovered = [];
|
||||
for (const a of letters) {
|
||||
for (const b of letters) {
|
||||
const code = a + b;
|
||||
const name = display.of(code);
|
||||
if (
|
||||
name &&
|
||||
!NON_COUNTRY_CODES.has(code) &&
|
||||
!drawn.has(code) &&
|
||||
!COUNTRY_ALIASES[code]
|
||||
) {
|
||||
uncovered.push(`${code} ${name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return uncovered;
|
||||
}
|
||||
|
||||
/** Natural Earth admin-0 GeoJSON → `{ topology, lookups, report }`. */
|
||||
export function buildCountries(source) {
|
||||
const report = { dropped: [], uncoded: [], merged: [] };
|
||||
const features = [...groupByCode(source.features, report)]
|
||||
.map(([key, members]) => toCountryFeature(key, members, report))
|
||||
.sort((a, b) => String(a.id).localeCompare(String(b.id)));
|
||||
|
||||
const { topo, flipped, collapsed, emptied } = simplifyAndQuantize(
|
||||
'countries',
|
||||
features,
|
||||
COUNTRY_MIN_WEIGHT,
|
||||
COUNTRY_QUANTIZATION,
|
||||
);
|
||||
const drawn = new Set(features.map((f) => f.properties.code).filter(Boolean));
|
||||
// Lets data be checked against the drawn codes without decoding the outlines.
|
||||
const lookups = { codes: [...drawn].sort(), ...buildLookups(source.features) };
|
||||
|
||||
const regionCounts = {};
|
||||
for (const f of features) {
|
||||
for (const region of f.properties.regions) {
|
||||
regionCounts[region] = (regionCounts[region] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
topology: topo,
|
||||
lookups,
|
||||
report: {
|
||||
features: features.length,
|
||||
decodedFeatures: feature(topo, topo.objects.countries).features.length,
|
||||
droppedFlippedIslands: flipped,
|
||||
droppedCollapsedPolygons: collapsed,
|
||||
emptied: emptied.join(' '),
|
||||
regionCounts,
|
||||
...report,
|
||||
lookupCounts: {
|
||||
alpha3: Object.keys(lookups.alpha3).length,
|
||||
names: Object.keys(lookups.names).length,
|
||||
aliases: Object.keys(lookups.aliases).length,
|
||||
},
|
||||
uncoveredIsoCodes: findUncoveredCodes(drawn),
|
||||
},
|
||||
};
|
||||
}
|
||||
114
frontend/scripts/geo-outlines/geometry.mjs
Normal file
114
frontend/scripts/geo-outlines/geometry.mjs
Normal file
@@ -0,0 +1,114 @@
|
||||
// Geometry helpers and the simplification shared by countries and states.
|
||||
import { geoArea } from 'd3-geo';
|
||||
import { topology } from 'topojson-server';
|
||||
import { presimplify, simplify } from 'topojson-simplify';
|
||||
import { feature, quantize } from 'topojson-client';
|
||||
|
||||
import { PROTECTED_SHAPE_FACTOR } from './config.mjs';
|
||||
|
||||
export const round = (n) => Math.round(n * 1e3) / 1e3;
|
||||
export const roundBounds = (bounds) =>
|
||||
bounds.map(([x, y]) => [round(x), round(y)]);
|
||||
|
||||
export function polygonsOf(geometry) {
|
||||
if (geometry.type === 'Polygon') {
|
||||
return [geometry.coordinates];
|
||||
}
|
||||
if (geometry.type === 'MultiPolygon') {
|
||||
return geometry.coordinates;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/** Area of a lon/lat ring in square degrees, the unit of the simplify weights. */
|
||||
function planarRingArea(ring) {
|
||||
let twice = 0;
|
||||
ring.forEach(([x, y], i) => {
|
||||
const [nx, ny] = ring[(i + 1) % ring.length];
|
||||
twice += x * ny - nx * y;
|
||||
});
|
||||
return Math.abs(twice) / 2;
|
||||
}
|
||||
|
||||
// Weights every point of a small shape's largest outline as Infinity, which
|
||||
// simplify never removes, so a city-state or city-sized province (Monaco, a
|
||||
// London borough) still has an outline to colour. Neighbours share those arcs,
|
||||
// so their common border stays in step.
|
||||
function protectSmallShapes(topo, objectName, features, minWeight) {
|
||||
topo.objects[objectName].geometries.forEach((geometry, i) => {
|
||||
const polygons = polygonsOf(features[i].geometry);
|
||||
const areas = polygons.map(([outer]) => planarRingArea(outer));
|
||||
const total = areas.reduce((sum, area) => sum + area, 0);
|
||||
if (polygons.length === 0 || total > minWeight * PROTECTED_SHAPE_FACTOR) {
|
||||
return;
|
||||
}
|
||||
const largest = areas.indexOf(Math.max(...areas));
|
||||
const rings =
|
||||
geometry.type === 'Polygon' ? geometry.arcs : geometry.arcs[largest];
|
||||
rings[0].forEach((index) => {
|
||||
topo.arcs[index < 0 ? ~index : index].forEach((point) => {
|
||||
point[2] = Infinity;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const isCollapsed = ([outer]) =>
|
||||
new Set(outer.map((point) => point.join())).size < 3;
|
||||
|
||||
// Simplifying can collapse a tiny island until its ring runs the other way; d3
|
||||
// then reads it as "the whole globe except this island" and floods the map.
|
||||
const isInverted = (coordinates) =>
|
||||
geoArea({ type: 'Polygon', coordinates }) > 2 * Math.PI;
|
||||
|
||||
/**
|
||||
* One feature collection to a simplified, quantized topology under
|
||||
* `objectName`. Polygons that collapse to a sliver or flip inside out are
|
||||
* dropped and the topology rebuilt without them.
|
||||
*/
|
||||
export function simplifyAndQuantize(
|
||||
objectName,
|
||||
features,
|
||||
minWeight,
|
||||
quantization,
|
||||
) {
|
||||
let topo = presimplify(
|
||||
topology({ [objectName]: { type: 'FeatureCollection', features } }),
|
||||
);
|
||||
protectSmallShapes(topo, objectName, features, minWeight);
|
||||
topo = simplify(topo, minWeight);
|
||||
// presimplify stores each point's weight as a third coordinate and simplify
|
||||
// keeps it, so it is dropped before quantizing (which would carry it through).
|
||||
topo = { ...topo, arcs: topo.arcs.map((arc) => arc.map(([x, y]) => [x, y])) };
|
||||
topo = quantize(topo, quantization);
|
||||
|
||||
let flipped = 0;
|
||||
let collapsed = 0;
|
||||
const cleaned = feature(topo, topo.objects[objectName]).features.map((f) => {
|
||||
const kept = polygonsOf(f.geometry).filter((coordinates) => {
|
||||
if (isCollapsed(coordinates)) {
|
||||
collapsed += 1;
|
||||
return false;
|
||||
}
|
||||
if (isInverted(coordinates)) {
|
||||
flipped += 1;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return { ...f, geometry: { type: 'MultiPolygon', coordinates: kept } };
|
||||
});
|
||||
const emptied = cleaned
|
||||
.filter((f) => f.geometry.coordinates.length === 0)
|
||||
.map((f) => f.properties.code ?? f.properties.name);
|
||||
|
||||
if (flipped > 0 || collapsed > 0) {
|
||||
topo = quantize(
|
||||
topology({
|
||||
[objectName]: { type: 'FeatureCollection', features: cleaned },
|
||||
}),
|
||||
quantization,
|
||||
);
|
||||
}
|
||||
return { topo, flipped, collapsed, emptied };
|
||||
}
|
||||
188
frontend/scripts/geo-outlines/subdivisions.mjs
Normal file
188
frontend/scripts/geo-outlines/subdivisions.mjs
Normal file
@@ -0,0 +1,188 @@
|
||||
// Natural Earth admin-1 → one states/provinces topology per country.
|
||||
import { geoArea } from 'd3-geo';
|
||||
import { topology } from 'topojson-server';
|
||||
import { merge } from 'topojson-client';
|
||||
|
||||
import {
|
||||
MIN_SUBDIVISIONS,
|
||||
SUBDIVISION_ALIASES,
|
||||
SUBDIVISION_MERGES,
|
||||
SUBDIVISION_MIN_WEIGHT,
|
||||
SUBDIVISION_QUANTIZATION,
|
||||
} from './config.mjs';
|
||||
import { isCountryCode } from './countries.mjs';
|
||||
import { round, simplifyAndQuantize } from './geometry.mjs';
|
||||
|
||||
const SUBDIVISION_CODE = /^[A-Z]{2}-[A-Z0-9]{1,3}$/;
|
||||
|
||||
/** Retired code → `[current code, current name]`, from `SUBDIVISION_MERGES`. */
|
||||
const RECODES = new Map(
|
||||
Object.values(SUBDIVISION_MERGES).flatMap((merges) =>
|
||||
Object.entries(merges).flatMap(([code, [name, retired]]) =>
|
||||
retired.map((old) => [old, [code, name]]),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/** A country's aliases: the hand-written ones, then each merge's retired codes. */
|
||||
function aliasesOf(country) {
|
||||
const aliases = { ...SUBDIVISION_ALIASES[country] };
|
||||
for (const [code, [, retired]] of Object.entries(
|
||||
SUBDIVISION_MERGES[country] ?? {},
|
||||
)) {
|
||||
for (const old of retired) {
|
||||
if (old !== code) {
|
||||
aliases[old] = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
// Shapes without an ISO 3166-2 code can't be matched to data, and a code under
|
||||
// another country's prefix (Puerto Rico's `US-PR`) belongs to that country's
|
||||
// file. Retired codes are rewritten to the current ones here, so the merge
|
||||
// below dissolves them.
|
||||
function groupByCountry(sourceFeatures) {
|
||||
const byCountry = new Map();
|
||||
const uncoded = {};
|
||||
const recoded = new Set();
|
||||
for (const f of sourceFeatures) {
|
||||
const country = f.properties.iso_a2;
|
||||
if (!isCountryCode(country)) {
|
||||
continue;
|
||||
}
|
||||
const code = f.properties.iso_3166_2;
|
||||
if (!SUBDIVISION_CODE.test(code ?? '') || !code.startsWith(`${country}-`)) {
|
||||
uncoded[country] = (uncoded[country] ?? 0) + 1;
|
||||
continue;
|
||||
}
|
||||
if (!byCountry.has(country)) {
|
||||
byCountry.set(country, []);
|
||||
}
|
||||
const recode = RECODES.get(code);
|
||||
if (recode) {
|
||||
recoded.add(code);
|
||||
}
|
||||
byCountry.get(country).push(
|
||||
recode
|
||||
? {
|
||||
...f,
|
||||
properties: {
|
||||
...f.properties,
|
||||
iso_3166_2: recode[0],
|
||||
name: recode[1],
|
||||
},
|
||||
}
|
||||
: f,
|
||||
);
|
||||
}
|
||||
return { byCountry, uncoded, recoded };
|
||||
}
|
||||
|
||||
// Natural Earth splits some codes across shapes (Dublin's four councils are all
|
||||
// `IE-D`). Merging them through a topology drops the borders between the parts,
|
||||
// so one code is one shape with one outline.
|
||||
function mergeByCode(features) {
|
||||
const byCode = new Map();
|
||||
for (const f of features) {
|
||||
const code = f.properties.iso_3166_2;
|
||||
if (!byCode.has(code)) {
|
||||
byCode.set(code, []);
|
||||
}
|
||||
byCode.get(code).push(f);
|
||||
}
|
||||
const topo = topology({ parts: { type: 'FeatureCollection', features } });
|
||||
const { geometries } = topo.objects.parts;
|
||||
let merged = 0;
|
||||
const shapes = [...byCode].map(([code, members]) => {
|
||||
// The largest part carries the name and label point.
|
||||
const primary = members.reduce((a, b) => (geoArea(a) >= geoArea(b) ? a : b));
|
||||
let geometry = primary.geometry;
|
||||
if (members.length > 1) {
|
||||
merged += members.length - 1;
|
||||
geometry = merge(
|
||||
topo,
|
||||
geometries.filter((_, i) => features[i].properties.iso_3166_2 === code),
|
||||
);
|
||||
}
|
||||
return {
|
||||
type: 'Feature',
|
||||
id: code,
|
||||
properties: {
|
||||
code,
|
||||
name: primary.properties.name,
|
||||
label: [
|
||||
round(primary.properties.longitude),
|
||||
round(primary.properties.latitude),
|
||||
],
|
||||
},
|
||||
geometry,
|
||||
};
|
||||
});
|
||||
return { shapes: shapes.sort((a, b) => a.id.localeCompare(b.id)), merged };
|
||||
}
|
||||
|
||||
/** Lower-cased English names → code, for data that names a state. */
|
||||
function namesOf(members) {
|
||||
const names = {};
|
||||
for (const { properties: p } of members) {
|
||||
for (const name of [p.name, p.name_en, p.gn_name, p.woe_name]) {
|
||||
if (name) {
|
||||
names[name.toLowerCase()] ??= p.iso_3166_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Natural Earth admin-1 GeoJSON → `{ files, report }`, where `files` maps each
|
||||
* country with enough coded states to its outlines file, in code order.
|
||||
*/
|
||||
export function buildSubdivisions(source) {
|
||||
const { byCountry, uncoded, recoded } = groupByCountry(source.features);
|
||||
const files = new Map();
|
||||
const report = {
|
||||
skippedUnderMinimum: [],
|
||||
mergedParts: {},
|
||||
uncodedShapes: uncoded,
|
||||
droppedFlippedIslands: {},
|
||||
droppedCollapsedPolygons: 0,
|
||||
emptiedShapes: [],
|
||||
// Merge entries whose retired code Natural Earth no longer has.
|
||||
unmatchedRetiredCodes: [...RECODES.keys()].filter(
|
||||
(code) => !recoded.has(code),
|
||||
),
|
||||
};
|
||||
|
||||
const countries = [...byCountry.keys()].sort((a, b) => a.localeCompare(b));
|
||||
for (const country of countries) {
|
||||
const members = byCountry.get(country);
|
||||
const { shapes, merged } = mergeByCode(members);
|
||||
if (shapes.length < MIN_SUBDIVISIONS) {
|
||||
report.skippedUnderMinimum.push(country);
|
||||
continue;
|
||||
}
|
||||
if (merged) {
|
||||
report.mergedParts[country] = merged;
|
||||
}
|
||||
const { topo, flipped, collapsed, emptied } = simplifyAndQuantize(
|
||||
'subdivisions',
|
||||
shapes,
|
||||
SUBDIVISION_MIN_WEIGHT,
|
||||
SUBDIVISION_QUANTIZATION,
|
||||
);
|
||||
if (flipped) {
|
||||
report.droppedFlippedIslands[country] = flipped;
|
||||
}
|
||||
report.droppedCollapsedPolygons += collapsed;
|
||||
report.emptiedShapes.push(...emptied);
|
||||
files.set(country, {
|
||||
...topo,
|
||||
aliases: aliasesOf(country),
|
||||
names: namesOf(members),
|
||||
});
|
||||
}
|
||||
return { files, report };
|
||||
}
|
||||
110
frontend/scripts/geo-outlines/validate.mjs
Normal file
110
frontend/scripts/geo-outlines/validate.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
// Invariants the chart relies on, checked before anything is written. A
|
||||
// failure names every problem; the committed assets are left untouched.
|
||||
import { feature } from 'topojson-client';
|
||||
|
||||
import { MIN_SUBDIVISIONS } from './config.mjs';
|
||||
|
||||
// Natural Earth v5.1.2 yields 239 countries and 198 countries with states. A
|
||||
// drop below these means a changed or truncated download, not real geography.
|
||||
const MIN_COUNTRIES = 230;
|
||||
const MIN_SUBDIVISION_COUNTRIES = 190;
|
||||
|
||||
function duplicatesOf(values) {
|
||||
const seen = new Set();
|
||||
return values.filter((value) => seen.has(value) || !seen.add(value));
|
||||
}
|
||||
|
||||
function checkCountries({ topology, lookups, report }, problems) {
|
||||
const shapes = feature(topology, topology.objects.countries).features;
|
||||
const codes = shapes.map((shape) => shape.properties.code).filter(Boolean);
|
||||
const drawn = new Set(codes);
|
||||
|
||||
if (shapes.length < MIN_COUNTRIES) {
|
||||
problems.push(
|
||||
`only ${shapes.length} countries (expected ≥ ${MIN_COUNTRIES})`,
|
||||
);
|
||||
}
|
||||
if (report.emptied) {
|
||||
problems.push(`countries left with no outline: ${report.emptied}`);
|
||||
}
|
||||
for (const code of duplicatesOf(codes)) {
|
||||
problems.push(`country code ${code} is drawn twice`);
|
||||
}
|
||||
if (lookups.codes.join() !== [...drawn].sort().join()) {
|
||||
problems.push('lookups.codes differs from the drawn country codes');
|
||||
}
|
||||
for (const [alias, target] of Object.entries(lookups.aliases)) {
|
||||
if (!drawn.has(target)) {
|
||||
problems.push(`country alias ${alias} → ${target}, which has no shape`);
|
||||
}
|
||||
}
|
||||
for (const [alpha3, target] of Object.entries(lookups.alpha3)) {
|
||||
if (!drawn.has(target)) {
|
||||
problems.push(`alpha-3 ${alpha3} → ${target}, which has no shape`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkSubdivisionFile(country, file, problems) {
|
||||
const codes = file.objects.subdivisions.geometries.map(
|
||||
(geometry) => geometry.properties.code,
|
||||
);
|
||||
const known = new Set(codes);
|
||||
|
||||
if (codes.length < MIN_SUBDIVISIONS) {
|
||||
problems.push(
|
||||
`${country}: ${codes.length} states (expected ≥ ${MIN_SUBDIVISIONS})`,
|
||||
);
|
||||
}
|
||||
for (const code of duplicatesOf(codes)) {
|
||||
problems.push(`${country}: state code ${code} is drawn twice`);
|
||||
}
|
||||
for (const code of codes) {
|
||||
if (!code?.startsWith(`${country}-`)) {
|
||||
problems.push(`${country}: state code ${code} is outside the country`);
|
||||
}
|
||||
}
|
||||
for (const [alias, target] of Object.entries(file.aliases)) {
|
||||
if (!known.has(target)) {
|
||||
problems.push(`${country}: alias ${alias} → ${target}, which has no shape`);
|
||||
}
|
||||
}
|
||||
for (const [name, code] of Object.entries(file.names)) {
|
||||
if (!known.has(code)) {
|
||||
problems.push(`${country}: name "${name}" → ${code}, which has no shape`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkSubdivisions({ files, report }, problems) {
|
||||
if (files.size < MIN_SUBDIVISION_COUNTRIES) {
|
||||
problems.push(
|
||||
`only ${files.size} countries with states (expected ≥ ${MIN_SUBDIVISION_COUNTRIES})`,
|
||||
);
|
||||
}
|
||||
if (report.emptiedShapes.length > 0) {
|
||||
problems.push(
|
||||
`states left with no outline: ${report.emptiedShapes.join(' ')}`,
|
||||
);
|
||||
}
|
||||
if (report.unmatchedRetiredCodes.length > 0) {
|
||||
problems.push(
|
||||
`merge codes not found in Natural Earth: ${report.unmatchedRetiredCodes.join(' ')}`,
|
||||
);
|
||||
}
|
||||
for (const [country, file] of files) {
|
||||
checkSubdivisionFile(country, file, problems);
|
||||
}
|
||||
}
|
||||
|
||||
/** Throws one error listing every broken invariant. */
|
||||
export function validate(countries, subdivisions) {
|
||||
const problems = [];
|
||||
checkCountries(countries, problems);
|
||||
checkSubdivisions(subdivisions, problems);
|
||||
if (problems.length > 0) {
|
||||
throw new Error(
|
||||
`${problems.length} problem(s) in the generated outlines:\n- ${problems.join('\n- ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4971,6 +4971,13 @@ export interface DashboardtypesBasicVisualizationDTO {
|
||||
timePreference?: DashboardtypesTimePreferenceDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* @minimum 1
|
||||
* @maximum 64
|
||||
* @nullable
|
||||
*/
|
||||
export type DashboardtypesBubbleRadiusDTO = number | null;
|
||||
|
||||
export interface Querybuildertypesv5LogAggregationDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -5821,6 +5828,198 @@ export interface DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDa
|
||||
spec: DashboardtypesScatterPlotPanelSpecDTO;
|
||||
}
|
||||
|
||||
export enum DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpecDTOKind {
|
||||
'signoz/TopListPanel' = 'signoz/TopListPanel',
|
||||
}
|
||||
export interface DashboardtypesTopListAppearanceDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @description Hex color of every bar; a matching background threshold overrides it.
|
||||
*/
|
||||
barColor: string;
|
||||
/**
|
||||
* @type boolean
|
||||
* @description Numbers each row by its position in the list.
|
||||
*/
|
||||
showRank?: boolean;
|
||||
/**
|
||||
* @type boolean
|
||||
* @description Shows each row's share of the listed rows' total.
|
||||
*/
|
||||
showShare?: boolean;
|
||||
}
|
||||
|
||||
export interface DashboardtypesTopListPanelSpecDTO {
|
||||
appearance?: DashboardtypesTopListAppearanceDTO;
|
||||
formatting?: DashboardtypesPanelFormattingDTO;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
thresholds?: DashboardtypesComparisonThresholdDTO[] | null;
|
||||
visualization?: DashboardtypesBasicVisualizationDTO;
|
||||
}
|
||||
|
||||
export interface DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpecDTO {
|
||||
/**
|
||||
* @enum signoz/TopListPanel
|
||||
* @type string
|
||||
*/
|
||||
kind: DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpecDTOKind;
|
||||
spec: DashboardtypesTopListPanelSpecDTO;
|
||||
}
|
||||
|
||||
export enum DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpecDTOKind {
|
||||
'signoz/GeoMapPanel' = 'signoz/GeoMapPanel',
|
||||
}
|
||||
export interface DashboardtypesGeoMapBubblesDTO {
|
||||
maxRadius?: DashboardtypesBubbleRadiusDTO | null;
|
||||
minRadius?: DashboardtypesBubbleRadiusDTO | null;
|
||||
}
|
||||
|
||||
export interface DashboardtypesGeoMapColorsDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
fill?: string;
|
||||
/**
|
||||
* @type number,null
|
||||
*/
|
||||
max?: number | null;
|
||||
/**
|
||||
* @type number,null
|
||||
*/
|
||||
min?: number | null;
|
||||
mode?: DashboardtypesHeatmapColorModeDTO;
|
||||
palette?: DashboardtypesHeatmapPaletteDTO;
|
||||
scale?: DashboardtypesHeatmapColorScaleDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
steps?: number;
|
||||
}
|
||||
|
||||
export interface DashboardtypesGeoMapChartAppearanceDTO {
|
||||
bubbles?: DashboardtypesGeoMapBubblesDTO;
|
||||
colors?: DashboardtypesGeoMapColorsDTO;
|
||||
/**
|
||||
* @type boolean,null
|
||||
* @description Shows country and state names once each fits inside its shape.
|
||||
*/
|
||||
labels?: boolean | null;
|
||||
}
|
||||
|
||||
export enum DashboardtypesGeoMapCountryFormatDTO {
|
||||
auto = 'auto',
|
||||
alpha2 = 'alpha2',
|
||||
alpha3 = 'alpha3',
|
||||
name = 'name',
|
||||
}
|
||||
export enum DashboardtypesGeoMapLocationModeDTO {
|
||||
auto = 'auto',
|
||||
country = 'country',
|
||||
subdivision = 'subdivision',
|
||||
coordinates = 'coordinates',
|
||||
}
|
||||
export interface DashboardtypesGeoMapLocationDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @description Group-by key holding the country, for the country and subdivision modes.
|
||||
*/
|
||||
country?: string;
|
||||
countryFormat?: DashboardtypesGeoMapCountryFormatDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description Group-by key holding the latitude, for the coordinates mode.
|
||||
*/
|
||||
lat?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @description Group-by key holding the longitude, for the coordinates mode.
|
||||
*/
|
||||
lon?: string;
|
||||
mode?: DashboardtypesGeoMapLocationModeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description Group-by key holding the ISO 3166-2 region code (e.g. geo.region.iso_code), for the subdivision mode.
|
||||
*/
|
||||
region?: string;
|
||||
}
|
||||
|
||||
export enum DashboardtypesGeoMapLayerDTO {
|
||||
regions = 'regions',
|
||||
bubbles = 'bubbles',
|
||||
points = 'points',
|
||||
}
|
||||
export enum DashboardtypesGeoMapViewDTO {
|
||||
world = 'world',
|
||||
fit = 'fit',
|
||||
africa = 'africa',
|
||||
asia = 'asia',
|
||||
europe = 'europe',
|
||||
north_america = 'north_america',
|
||||
south_america = 'south_america',
|
||||
oceania = 'oceania',
|
||||
country = 'country',
|
||||
}
|
||||
export interface DashboardtypesGeoMapSettingsDTO {
|
||||
/**
|
||||
* @type string
|
||||
* @description Value column key that sets the colour. Empty uses the first value column.
|
||||
*/
|
||||
colorBy?: string;
|
||||
layer?: DashboardtypesGeoMapLayerDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description Value column key that sets the bubble or point size. Empty uses colorBy.
|
||||
*/
|
||||
sizeBy?: string;
|
||||
view?: DashboardtypesGeoMapViewDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description ISO 3166-1 alpha-2 code of the country the country view shows.
|
||||
*/
|
||||
viewCountry?: string;
|
||||
}
|
||||
|
||||
export interface DashboardtypesGeoMapThresholdDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
color: string;
|
||||
operator?: DashboardtypesComparisonOperatorDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
unit?: string;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface DashboardtypesGeoMapPanelSpecDTO {
|
||||
chartAppearance?: DashboardtypesGeoMapChartAppearanceDTO;
|
||||
formatting?: DashboardtypesTableFormattingDTO;
|
||||
legend?: DashboardtypesLegendDTO;
|
||||
location?: DashboardtypesGeoMapLocationDTO;
|
||||
map?: DashboardtypesGeoMapSettingsDTO;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
thresholds?: DashboardtypesGeoMapThresholdDTO[] | null;
|
||||
visualization?: DashboardtypesBasicVisualizationDTO;
|
||||
}
|
||||
|
||||
export interface DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpecDTO {
|
||||
/**
|
||||
* @enum signoz/GeoMapPanel
|
||||
* @type string
|
||||
*/
|
||||
kind: DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpecDTOKind;
|
||||
spec: DashboardtypesGeoMapPanelSpecDTO;
|
||||
}
|
||||
|
||||
export type DashboardtypesPanelPluginDTO =
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTimeSeriesPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBarChartPanelSpecDTO
|
||||
@@ -5832,7 +6031,9 @@ export type DashboardtypesPanelPluginDTO =
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesListPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTextPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesHeatmapPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesScatterPlotPanelSpecDTO;
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesScatterPlotPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesTopListPanelSpecDTO
|
||||
| DashboardtypesPanelPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesGeoMapPanelSpecDTO;
|
||||
|
||||
export enum Querybuildertypesv5RequestTypeDTO {
|
||||
scalar = 'scalar',
|
||||
@@ -6773,6 +6974,8 @@ export enum DashboardtypesPanelPluginKindDTO {
|
||||
'signoz/TextPanel' = 'signoz/TextPanel',
|
||||
'signoz/HeatmapPanel' = 'signoz/HeatmapPanel',
|
||||
'signoz/ScatterPlotPanel' = 'signoz/ScatterPlotPanel',
|
||||
'signoz/TopListPanel' = 'signoz/TopListPanel',
|
||||
'signoz/GeoMapPanel' = 'signoz/GeoMapPanel',
|
||||
}
|
||||
/**
|
||||
* @nullable
|
||||
|
||||
@@ -54,6 +54,8 @@ export function mapPanelTypeToRequestType(panelType: PANEL_TYPES): RequestType {
|
||||
case PANEL_TYPES.SCATTER:
|
||||
case PANEL_TYPES.PIE:
|
||||
case PANEL_TYPES.VALUE:
|
||||
case PANEL_TYPES.TOP_LIST:
|
||||
case PANEL_TYPES.GEO_MAP:
|
||||
return 'scalar';
|
||||
case PANEL_TYPES.TRACE:
|
||||
return 'trace';
|
||||
@@ -281,7 +283,9 @@ export function createAggregation(
|
||||
(panelType === PANEL_TYPES.TABLE ||
|
||||
panelType === PANEL_TYPES.SCATTER ||
|
||||
panelType === PANEL_TYPES.PIE ||
|
||||
panelType === PANEL_TYPES.VALUE);
|
||||
panelType === PANEL_TYPES.VALUE ||
|
||||
panelType === PANEL_TYPES.TOP_LIST ||
|
||||
panelType === PANEL_TYPES.GEO_MAP);
|
||||
|
||||
if (queryData.dataSource === DataSource.METRICS) {
|
||||
return [
|
||||
|
||||
@@ -146,10 +146,21 @@ function QueryAddOns({
|
||||
[resolvedFields],
|
||||
);
|
||||
|
||||
// A panel's default limit applies only to a lone query: per-query limits can keep
|
||||
// different groups and break a formula's join.
|
||||
const isLoneQuery =
|
||||
(currentQuery.builder?.queryData?.length ?? 1) +
|
||||
(currentQuery.builder?.queryFormulas?.length ?? 0) +
|
||||
(currentQuery.builder?.queryTraceOperator?.length ?? 0) <=
|
||||
1;
|
||||
|
||||
const placeholderOf = useCallback(
|
||||
(key: InputAddOnField): string =>
|
||||
resolvedFields.get(key)?.placeholder ?? ADD_ON_DEFINITIONS[key].placeholder,
|
||||
[resolvedFields],
|
||||
(key === QueryBuilderField.Limit && !isLoneQuery
|
||||
? undefined
|
||||
: resolvedFields.get(key)?.placeholder) ??
|
||||
ADD_ON_DEFINITIONS[key].placeholder,
|
||||
[resolvedFields, isLoneQuery],
|
||||
);
|
||||
|
||||
const isDisabled = useCallback(
|
||||
|
||||
@@ -49,7 +49,9 @@ function QueryAggregationOptions({
|
||||
isNonMetricSource &&
|
||||
(panelType === PANEL_TYPES.TABLE ||
|
||||
panelType === PANEL_TYPES.SCATTER ||
|
||||
panelType === PANEL_TYPES.PIE)
|
||||
panelType === PANEL_TYPES.PIE ||
|
||||
panelType === PANEL_TYPES.TOP_LIST ||
|
||||
panelType === PANEL_TYPES.GEO_MAP)
|
||||
) {
|
||||
return { hidden: true, disabled: false, pinned: false };
|
||||
}
|
||||
@@ -71,7 +73,9 @@ function QueryAggregationOptions({
|
||||
onChange={onChange}
|
||||
queryData={queryData}
|
||||
maxAggregations={
|
||||
panelType === PANEL_TYPES.VALUE || panelType === PANEL_TYPES.PIE
|
||||
panelType === PANEL_TYPES.VALUE ||
|
||||
panelType === PANEL_TYPES.PIE ||
|
||||
panelType === PANEL_TYPES.TOP_LIST
|
||||
? 1
|
||||
: undefined
|
||||
}
|
||||
|
||||
@@ -117,7 +117,9 @@ export const QueryV2 = forwardRef(function QueryV2(
|
||||
(panelType === PANEL_TYPES.TABLE ||
|
||||
panelType === PANEL_TYPES.SCATTER ||
|
||||
panelType === PANEL_TYPES.PIE ||
|
||||
panelType === PANEL_TYPES.VALUE),
|
||||
panelType === PANEL_TYPES.VALUE ||
|
||||
panelType === PANEL_TYPES.TOP_LIST ||
|
||||
panelType === PANEL_TYPES.GEO_MAP),
|
||||
[dataSource, panelType],
|
||||
);
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ import QueryAddOns from '../QueryV2/QueryAddOns/QueryAddOns';
|
||||
// Mocks: only what is required for this component to render and for us to assert handler calls
|
||||
const mockHandleChangeQueryData = jest.fn();
|
||||
const mockHandleSetQueryData = jest.fn();
|
||||
let mockCurrentQuery: { unit: string | undefined; builder?: unknown } = {
|
||||
unit: undefined,
|
||||
};
|
||||
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilderOperations', () => ({
|
||||
useQueryOperations: (): {
|
||||
@@ -27,10 +30,10 @@ jest.mock('hooks/queryBuilder/useQueryBuilderOperations', () => ({
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({
|
||||
useQueryBuilder: (): {
|
||||
handleSetQueryData: typeof mockHandleSetQueryData;
|
||||
currentQuery: { unit: string | undefined };
|
||||
currentQuery: typeof mockCurrentQuery;
|
||||
} => ({
|
||||
handleSetQueryData: mockHandleSetQueryData,
|
||||
currentQuery: { unit: undefined },
|
||||
currentQuery: mockCurrentQuery,
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -222,6 +225,59 @@ describe('QueryAddOns', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('a pinned limit with a default', () => {
|
||||
const renderPinnedLimit = (): void => {
|
||||
render(
|
||||
<QueryAddOns
|
||||
query={baseQuery()}
|
||||
version="v5"
|
||||
isRawQuery={false}
|
||||
showReduceTo={false}
|
||||
panelType={PANEL_TYPES.TOP_LIST}
|
||||
index={0}
|
||||
fieldsConfig={{
|
||||
[QueryBuilderField.Limit]: {
|
||||
state: 'pinned',
|
||||
placeholder: 'Default 10',
|
||||
},
|
||||
}}
|
||||
isForTraceOperator={false}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
mockCurrentQuery = { unit: undefined };
|
||||
});
|
||||
|
||||
it('is open with its default as the placeholder', () => {
|
||||
renderPinnedLimit();
|
||||
|
||||
expect(screen.getByTestId('input-Limit')).toHaveAttribute(
|
||||
'placeholder',
|
||||
'Default 10',
|
||||
);
|
||||
});
|
||||
|
||||
it('drops the default once a formula joins the query', () => {
|
||||
mockCurrentQuery = {
|
||||
unit: undefined,
|
||||
builder: {
|
||||
queryData: [baseQuery()],
|
||||
queryFormulas: [{ expression: 'A * 2' }],
|
||||
queryTraceOperator: [],
|
||||
},
|
||||
};
|
||||
|
||||
renderPinnedLimit();
|
||||
|
||||
expect(screen.getByTestId('input-Limit')).toHaveAttribute(
|
||||
'placeholder',
|
||||
'Enter limit',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('auto-opens Order By and Limit when present in query', () => {
|
||||
const query = baseQuery({
|
||||
orderBy: [{ columnName: 'duration', order: 'desc' }],
|
||||
|
||||
@@ -34,6 +34,8 @@ export const getComponentForPanelType = (
|
||||
[PANEL_TYPES.HISTOGRAM]: Uplot,
|
||||
// V2-only kind; it renders through the V2 panel registry.
|
||||
[PANEL_TYPES.HEATMAP]: null,
|
||||
[PANEL_TYPES.TOP_LIST]: null,
|
||||
[PANEL_TYPES.GEO_MAP]: null,
|
||||
// Dashboards v2 renders this kind; nothing reaches the V1 chart map for it.
|
||||
[PANEL_TYPES.TEXT]: null,
|
||||
[PANEL_TYPES.SCATTER]: null,
|
||||
|
||||
@@ -341,6 +341,8 @@ export enum PANEL_TYPES {
|
||||
PIE = 'pie',
|
||||
HISTOGRAM = 'histogram',
|
||||
HEATMAP = 'heatmap',
|
||||
TOP_LIST = 'top_list',
|
||||
GEO_MAP = 'geo_map',
|
||||
TEXT = 'text',
|
||||
EMPTY_WIDGET = 'EMPTY_WIDGET',
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ export const PANEL_TYPES_VS_FULL_VIEW_TABLE: PanelTypeAndGraphManagerVisibilityP
|
||||
PIE: false,
|
||||
HISTOGRAM: false,
|
||||
HEATMAP: false,
|
||||
TOP_LIST: false,
|
||||
GEO_MAP: false,
|
||||
TEXT: false,
|
||||
EMPTY_WIDGET: false,
|
||||
};
|
||||
|
||||
@@ -24,4 +24,6 @@ export const PanelTypeVsPanelWrapper = {
|
||||
[PANEL_TYPES.HISTOGRAM]: HistogramPanel,
|
||||
// V2-only kind; it renders through the V2 panel registry.
|
||||
[PANEL_TYPES.HEATMAP]: null,
|
||||
[PANEL_TYPES.TOP_LIST]: null,
|
||||
[PANEL_TYPES.GEO_MAP]: null,
|
||||
};
|
||||
|
||||
@@ -17,6 +17,8 @@ export type PartialPanelTypes = {
|
||||
[PANEL_TYPES.SCATTER]: 'scatter';
|
||||
[PANEL_TYPES.HISTOGRAM]: 'histogram';
|
||||
[PANEL_TYPES.HEATMAP]: 'heatmap';
|
||||
[PANEL_TYPES.TOP_LIST]: 'top_list';
|
||||
[PANEL_TYPES.GEO_MAP]: 'geo_map';
|
||||
};
|
||||
|
||||
export type BuilderField = keyof IBuilderQuery;
|
||||
@@ -152,6 +154,8 @@ export const panelTypeDataSourceFormValuesMap: Record<
|
||||
[PANEL_TYPES.TABLE]: bySource(SERIES, SCALAR_METRICS),
|
||||
[PANEL_TYPES.PIE]: bySource(SERIES, SCALAR_METRICS),
|
||||
[PANEL_TYPES.SCATTER]: bySource(SERIES, SCALAR_METRICS),
|
||||
[PANEL_TYPES.TOP_LIST]: bySource(SERIES, SCALAR_METRICS),
|
||||
[PANEL_TYPES.GEO_MAP]: bySource(SERIES, SCALAR_METRICS),
|
||||
[PANEL_TYPES.VALUE]: bySource(SINGLE_VALUE, SINGLE_VALUE_METRICS),
|
||||
[PANEL_TYPES.LIST]: bySource(RAW_ROWS, RAW_ROWS_METRICS),
|
||||
};
|
||||
|
||||
@@ -72,3 +72,32 @@
|
||||
var(--muted-foreground) 2px 3px
|
||||
);
|
||||
}
|
||||
|
||||
.vertical {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
|
||||
.track {
|
||||
flex: 0 0 8px;
|
||||
width: 8px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.marker {
|
||||
top: auto;
|
||||
right: -3px;
|
||||
left: -3px;
|
||||
width: auto;
|
||||
height: 2px;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
}
|
||||
|
||||
.scale {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface ColorBarProps {
|
||||
label?: string;
|
||||
/** Keys the one state a ramp cannot express: a hatched data gap. */
|
||||
showNoDataKey?: boolean;
|
||||
/** Vertical runs low to high from the bottom, with labels to the right. */
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
'data-testid'?: string;
|
||||
}
|
||||
|
||||
@@ -25,8 +27,10 @@ export default function ColorBar({
|
||||
markerPosition = null,
|
||||
label,
|
||||
showNoDataKey = false,
|
||||
orientation = 'horizontal',
|
||||
'data-testid': testId = 'color-bar',
|
||||
}: ColorBarProps): JSX.Element | null {
|
||||
const isVertical = orientation === 'vertical';
|
||||
const gradient = useMemo(() => {
|
||||
if (ramp.length === 0) {
|
||||
return undefined;
|
||||
@@ -39,8 +43,8 @@ export default function ColorBar({
|
||||
const to = ((index + 1) / ramp.length) * 100;
|
||||
return [`${color} ${from}%`, `${color} ${to}%`];
|
||||
});
|
||||
return `linear-gradient(to right, ${stops.join(', ')})`;
|
||||
}, [ramp]);
|
||||
return `linear-gradient(to ${isVertical ? 'top' : 'right'}, ${stops.join(', ')})`;
|
||||
}, [ramp, isVertical]);
|
||||
|
||||
if (gradient === undefined) {
|
||||
return null;
|
||||
@@ -49,6 +53,26 @@ export default function ColorBar({
|
||||
const clampedMarker =
|
||||
markerPosition === null ? null : Math.min(Math.max(markerPosition, 0), 1);
|
||||
|
||||
if (isVertical) {
|
||||
return (
|
||||
<div className={Styles.vertical} data-testid={testId}>
|
||||
<div className={Styles.track} style={{ background: gradient }}>
|
||||
{clampedMarker !== null && (
|
||||
<span
|
||||
className={Styles.marker}
|
||||
style={{ bottom: `${clampedMarker * 100}%` }}
|
||||
data-testid={`${testId}-marker`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={Styles.scale}>
|
||||
<span className={Styles.label}>{maxLabel}</span>
|
||||
<span className={Styles.label}>{minLabel}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={Styles.container} data-testid={testId}>
|
||||
{label && <span className={Styles.caption}>{label}</span>}
|
||||
|
||||
@@ -82,4 +82,20 @@ describe('ColorBar', () => {
|
||||
'linear-gradient(to right, #111111 0%, #111111 50%, #dddddd 50%, #dddddd 100%)',
|
||||
});
|
||||
});
|
||||
|
||||
it('runs low to high from the bottom when vertical', () => {
|
||||
render(
|
||||
<ColorBar
|
||||
ramp={['#000', '#fff']}
|
||||
minLabel="0.18%"
|
||||
maxLabel="14.2%"
|
||||
markerPosition={0.25}
|
||||
orientation="vertical"
|
||||
/>,
|
||||
);
|
||||
|
||||
const labels = screen.getAllByText(/%$/).map((label) => label.textContent);
|
||||
expect(labels).toStrictEqual(['14.2%', '0.18%']);
|
||||
expect(screen.getByTestId('color-bar-marker')).toHaveStyle({ bottom: '25%' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { useMemo } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Pin } from '@signozhq/icons';
|
||||
|
||||
import { ScatterTooltipProps } from '../types';
|
||||
import TooltipCard from './components/TooltipCard/TooltipCard';
|
||||
import { buildChannelRows, resolveHoveredPoint } from './scatterTooltipContent';
|
||||
|
||||
import Styles from './ScatterTooltip.module.scss';
|
||||
|
||||
/** Own component: the shared Tooltip lists one row per series at a shared x. */
|
||||
export default function ScatterTooltip({
|
||||
uPlotInstance,
|
||||
@@ -42,69 +39,18 @@ export default function ScatterTooltip({
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(Styles.container, { [Styles.pinned]: isPinned })}
|
||||
data-pinned={isPinned}
|
||||
data-testid="scatter-tooltip"
|
||||
>
|
||||
<div className={Styles.header}>
|
||||
<span
|
||||
className={Styles.marker}
|
||||
style={{ backgroundColor: point.color, borderColor: point.color }}
|
||||
/>
|
||||
<span
|
||||
className={Styles.title}
|
||||
title={point.label}
|
||||
data-testid="scatter-tooltip-title"
|
||||
>
|
||||
{point.label}
|
||||
</span>
|
||||
{isPinned && (
|
||||
<span className={Styles.status} data-testid="scatter-tooltip-status">
|
||||
<Pin size={12} />
|
||||
<span>Pinned</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className={Styles.divider} />
|
||||
|
||||
<div className={Styles.rows}>
|
||||
{rows.map((row) => (
|
||||
<div
|
||||
key={row.label}
|
||||
className={Styles.row}
|
||||
data-testid="scatter-tooltip-row"
|
||||
>
|
||||
<span className={Styles.rowLabel}>{row.label}</span>
|
||||
<span className={Styles.rowValue}>{row.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{labels.length > 0 && (
|
||||
<>
|
||||
<span className={Styles.divider} />
|
||||
<div className={Styles.rows}>
|
||||
{labels.map((label) => (
|
||||
<div
|
||||
key={label.key}
|
||||
className={cx(Styles.row, Styles.rowMuted)}
|
||||
data-testid="scatter-tooltip-label"
|
||||
>
|
||||
<span className={Styles.rowLabel} title={label.key}>
|
||||
{label.key}
|
||||
</span>
|
||||
<span className={Styles.rowValue} title={label.value}>
|
||||
{label.value}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{renderTooltipFooter?.({ isPinned, dismiss })}
|
||||
</div>
|
||||
<TooltipCard
|
||||
title={point.label}
|
||||
color={point.color}
|
||||
rows={rows.map((row) => ({ key: row.label, ...row }))}
|
||||
mutedRows={labels.map((label) => ({
|
||||
key: label.key,
|
||||
label: label.key,
|
||||
value: label.value,
|
||||
}))}
|
||||
isPinned={isPinned}
|
||||
footer={renderTooltipFooter?.({ isPinned, dismiss })}
|
||||
testId="scatter-tooltip"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: 600;
|
||||
color: var(--text-vanilla-100);
|
||||
color: var(--l1-foreground);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -60,7 +60,7 @@
|
||||
align-items: center;
|
||||
gap: var(--spacing-1);
|
||||
font-size: 11px;
|
||||
color: var(--text-vanilla-400);
|
||||
color: var(--l3-foreground);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -79,11 +79,11 @@
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-xs);
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--text-vanilla-100);
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
.rowMuted {
|
||||
color: var(--text-vanilla-400);
|
||||
color: var(--l3-foreground);
|
||||
}
|
||||
|
||||
.rowLabel {
|
||||
@@ -0,0 +1,99 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Pin } from '@signozhq/icons';
|
||||
|
||||
import Styles from './TooltipCard.module.scss';
|
||||
|
||||
export interface TooltipCardRow {
|
||||
key: string;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface TooltipCardProps {
|
||||
title: string;
|
||||
/** The marker beside the title, matching the item's mark on the chart. */
|
||||
color: string;
|
||||
/** What the item says. */
|
||||
rows: TooltipCardRow[];
|
||||
/** What names the item, below the rows and muted. */
|
||||
mutedRows?: TooltipCardRow[];
|
||||
isPinned?: boolean;
|
||||
footer?: ReactNode;
|
||||
/** Prefixes the test ids of the title, status, rows and muted rows. */
|
||||
testId: string;
|
||||
}
|
||||
|
||||
/** One item's tooltip: a marked title, then label/value rows, on the shared tooltip surface. */
|
||||
export default function TooltipCard({
|
||||
title,
|
||||
color,
|
||||
rows,
|
||||
mutedRows = [],
|
||||
isPinned = false,
|
||||
footer,
|
||||
testId,
|
||||
}: TooltipCardProps): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={cx(Styles.container, { [Styles.pinned]: isPinned })}
|
||||
data-pinned={isPinned}
|
||||
data-testid={testId}
|
||||
>
|
||||
<div className={Styles.header}>
|
||||
<span
|
||||
className={Styles.marker}
|
||||
style={{ backgroundColor: color, borderColor: color }}
|
||||
/>
|
||||
<span
|
||||
className={Styles.title}
|
||||
title={title}
|
||||
data-testid={`${testId}-title`}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
{isPinned && (
|
||||
<span className={Styles.status} data-testid={`${testId}-status`}>
|
||||
<Pin size={12} />
|
||||
<span>Pinned</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className={Styles.divider} />
|
||||
|
||||
<div className={Styles.rows}>
|
||||
{rows.map((row) => (
|
||||
<div key={row.key} className={Styles.row} data-testid={`${testId}-row`}>
|
||||
<span className={Styles.rowLabel}>{row.label}</span>
|
||||
<span className={Styles.rowValue}>{row.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{mutedRows.length > 0 && (
|
||||
<>
|
||||
<span className={Styles.divider} />
|
||||
<div className={Styles.rows}>
|
||||
{mutedRows.map((row) => (
|
||||
<div
|
||||
key={row.key}
|
||||
className={cx(Styles.row, Styles.rowMuted)}
|
||||
data-testid={`${testId}-label`}
|
||||
>
|
||||
<span className={Styles.rowLabel} title={row.label}>
|
||||
{row.label}
|
||||
</span>
|
||||
<span className={Styles.rowValue} title={row.value}>
|
||||
{row.value}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type uPlot from 'uplot';
|
||||
|
||||
import { DEFAULT_HEATMAP_COLORS } from '../colorScale';
|
||||
import { DEFAULT_COLOR_RAMP_OPTIONS } from 'lib/visualization/colorRamp/colorScale';
|
||||
import { resolveHeatmapYAxis } from '../geometry';
|
||||
import { createHeatmapHooks } from '../heatmapPlugin';
|
||||
import { HeatmapAxisScale, HeatmapCell } from '../types';
|
||||
@@ -103,7 +103,7 @@ function createHooks(
|
||||
return createHeatmapHooks({
|
||||
yAxis: Y_AXIS,
|
||||
step: STEP,
|
||||
colors: DEFAULT_HEATMAP_COLORS,
|
||||
colors: DEFAULT_COLOR_RAMP_OPTIONS,
|
||||
isDarkMode: true,
|
||||
seriesColor: '#4e74f8',
|
||||
dimOnHover,
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
import { Color as DesignToken } from '@signozhq/design-tokens';
|
||||
import Color from 'color';
|
||||
|
||||
import { DEFAULT_HEATMAP_PALETTE, getPaletteStops } from './palettes';
|
||||
import {
|
||||
HeatmapColorMode,
|
||||
HeatmapColorOptions,
|
||||
HeatmapColorScale,
|
||||
} from './types';
|
||||
|
||||
export const MIN_COLOR_STEPS = 2;
|
||||
export const MAX_COLOR_STEPS = 128;
|
||||
export const DEFAULT_COLOR_STEPS = 64;
|
||||
|
||||
/** Without a floor, the lowest counts read as "no data". */
|
||||
export const MIN_OPACITY_ALPHA = 0.1;
|
||||
|
||||
/** Log's bottom when the grid holds no positive count to take it from. */
|
||||
const DEFAULT_LOG_FLOOR = 1;
|
||||
|
||||
/** Widest span a single ramp is stretched over on a log scale. */
|
||||
const MAX_LOG_DECADES = 6;
|
||||
|
||||
/** Used when neither an explicit fill nor a series colour is available. */
|
||||
export const DEFAULT_OPACITY_FILL = DesignToken.BG_ROBIN_500;
|
||||
|
||||
/** Zero takes the panel surface so an empty stretch recedes, leaving the ramp's
|
||||
* bottom to the smallest count that did occur — on a log scale the two collapse. */
|
||||
const ZERO_FILL: Record<'dark' | 'light', string> = {
|
||||
dark: DesignToken.BG_INK_500,
|
||||
light: DesignToken.BG_VANILLA_100,
|
||||
};
|
||||
|
||||
export const DEFAULT_HEATMAP_COLORS: HeatmapColorOptions = {
|
||||
mode: HeatmapColorMode.Palette,
|
||||
scale: HeatmapColorScale.Log,
|
||||
minCount: null,
|
||||
maxCount: null,
|
||||
palette: DEFAULT_HEATMAP_PALETTE,
|
||||
steps: DEFAULT_COLOR_STEPS,
|
||||
fill: '',
|
||||
};
|
||||
|
||||
export interface CountDomain {
|
||||
min: number;
|
||||
max: number;
|
||||
/** Bottom decade of the log scale: the smallest count it separates from zero.
|
||||
* Always positive, since log has no bottom otherwise. */
|
||||
logFloor: number;
|
||||
}
|
||||
|
||||
/** Highest count, ignoring `null`. 0 for an empty grid. */
|
||||
export function getMaxCount(counts: Array<Array<number | null>>): number {
|
||||
let max = 0;
|
||||
for (const row of counts) {
|
||||
for (const count of row) {
|
||||
if (count !== null && Number.isFinite(count) && count > max) {
|
||||
max = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/** Smallest count above zero, ignoring `null`. `null` for a grid without one. */
|
||||
export function getSmallestPositiveCount(
|
||||
counts: Array<Array<number | null>>,
|
||||
): number | null {
|
||||
let smallest: number | null = null;
|
||||
for (const row of counts) {
|
||||
for (const count of row) {
|
||||
if (
|
||||
count !== null &&
|
||||
Number.isFinite(count) &&
|
||||
count > 0 &&
|
||||
(smallest === null || count < smallest)
|
||||
) {
|
||||
smallest = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return smallest;
|
||||
}
|
||||
|
||||
/**
|
||||
* The grid's own resolution: whole counts floor at 1, while a heatmap of rates or
|
||||
* ratios can live entirely below it, where a fixed floor of 1 would flatten every
|
||||
* cell onto the bottom colour. Capped at `MAX_LOG_DECADES` so one stray tiny cell
|
||||
* cannot stretch the ramp over a range nothing else occupies.
|
||||
*/
|
||||
function resolveLogFloor(
|
||||
counts: Array<Array<number | null>>,
|
||||
max: number,
|
||||
): number {
|
||||
const smallest = getSmallestPositiveCount(counts) ?? DEFAULT_LOG_FLOOR;
|
||||
return Math.max(smallest, max / 10 ** MAX_LOG_DECADES);
|
||||
}
|
||||
|
||||
/** Explicit clamps win; otherwise 0 to the grid's highest count. */
|
||||
export function resolveCountDomain(
|
||||
options: Pick<HeatmapColorOptions, 'minCount' | 'maxCount'>,
|
||||
counts: Array<Array<number | null>>,
|
||||
): CountDomain {
|
||||
const min = options.minCount ?? 0;
|
||||
const max = options.maxCount ?? getMaxCount(counts);
|
||||
const logFloor = resolveLogFloor(counts, max);
|
||||
return max > min ? { min, max, logFloor } : { min, max: min, logFloor };
|
||||
}
|
||||
|
||||
/** Position on the colour scale, 0..1. A degenerate domain collapses to 0 so an
|
||||
* all-zero grid renders at the bottom rather than disappearing. */
|
||||
export function normalizeCount({
|
||||
count,
|
||||
domain,
|
||||
scale,
|
||||
}: {
|
||||
count: number;
|
||||
domain: CountDomain;
|
||||
scale: HeatmapColorScale;
|
||||
}): number {
|
||||
const { min, max } = domain;
|
||||
if (!(max > min)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const clamped = Math.min(Math.max(count, min), max);
|
||||
|
||||
if (scale === HeatmapColorScale.Log) {
|
||||
// Anything at or below the floor sits at the bottom; log cannot place it.
|
||||
const bottom = Math.max(min, domain.logFloor);
|
||||
const logMin = Math.log10(bottom);
|
||||
const logMax = Math.log10(max);
|
||||
if (logMax > logMin) {
|
||||
return (Math.log10(Math.max(clamped, bottom)) - logMin) / (logMax - logMin);
|
||||
}
|
||||
// The floor already reaches the top of the domain, so there is no span to
|
||||
// spread logarithmically. Fall through rather than flatten the whole grid.
|
||||
}
|
||||
|
||||
const linear = (clamped - min) / (max - min);
|
||||
return scale === HeatmapColorScale.Sqrt ? Math.sqrt(linear) : linear;
|
||||
}
|
||||
|
||||
export function clampColorSteps(steps: number): number {
|
||||
if (!Number.isFinite(steps)) {
|
||||
return DEFAULT_COLOR_STEPS;
|
||||
}
|
||||
return Math.min(Math.max(Math.round(steps), MIN_COLOR_STEPS), MAX_COLOR_STEPS);
|
||||
}
|
||||
|
||||
/** Colour at `t` (0..1) along a multi-stop ramp. */
|
||||
function sampleStops(stops: string[], t: number): string {
|
||||
if (stops.length === 0) {
|
||||
return 'transparent';
|
||||
}
|
||||
if (stops.length === 1) {
|
||||
return stops[0];
|
||||
}
|
||||
const scaled = Math.min(Math.max(t, 0), 1) * (stops.length - 1);
|
||||
const lower = Math.min(Math.floor(scaled), stops.length - 2);
|
||||
return Color(stops[lower])
|
||||
.mix(Color(stops[lower + 1]), scaled - lower)
|
||||
.hex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Colour the densest cells are drawn with — the palette's extreme, or the opacity
|
||||
* fill at full strength. Depends only on the options, not on the data, so callers
|
||||
* can read it before a grid exists.
|
||||
*/
|
||||
export function resolveExtremeColor({
|
||||
options,
|
||||
isDarkMode,
|
||||
seriesColor,
|
||||
}: {
|
||||
options: HeatmapColorOptions;
|
||||
isDarkMode: boolean;
|
||||
seriesColor: string;
|
||||
}): string {
|
||||
if (options.mode === HeatmapColorMode.Opacity) {
|
||||
return options.fill || seriesColor || DEFAULT_OPACITY_FILL;
|
||||
}
|
||||
const stops = getPaletteStops(options.palette, isDarkMode);
|
||||
return stops[stops.length - 1] ?? DEFAULT_OPACITY_FILL;
|
||||
}
|
||||
|
||||
export interface HeatmapColorResolver {
|
||||
/** `null` for a `null` count, which must be hatched. */
|
||||
colorFor: (count: number | null) => string | null;
|
||||
/** 0..1, or `null` for a `null` count. */
|
||||
positionOf: (count: number | null) => number | null;
|
||||
/** Low to high. The colour bar renders exactly these. */
|
||||
ramp: string[];
|
||||
domain: CountDomain;
|
||||
}
|
||||
|
||||
/** Palette mode walks a sequential ramp; opacity mode varies the alpha of one
|
||||
* fill, so the grid matches its group's legend swatch. */
|
||||
export function createHeatmapColorResolver({
|
||||
options,
|
||||
domain,
|
||||
isDarkMode,
|
||||
seriesColor,
|
||||
}: {
|
||||
options: HeatmapColorOptions;
|
||||
domain: CountDomain;
|
||||
isDarkMode: boolean;
|
||||
/** Opacity-mode fill when `options.fill` is empty. */
|
||||
seriesColor: string;
|
||||
}): HeatmapColorResolver {
|
||||
const steps = clampColorSteps(options.steps);
|
||||
const positions = Array.from({ length: steps }, (_, index) =>
|
||||
steps === 1 ? 0 : index / (steps - 1),
|
||||
);
|
||||
|
||||
let ramp: string[];
|
||||
if (options.mode === HeatmapColorMode.Opacity) {
|
||||
const base = Color(options.fill || seriesColor || DEFAULT_OPACITY_FILL);
|
||||
ramp = positions.map((t) =>
|
||||
base
|
||||
.alpha(MIN_OPACITY_ALPHA + t * (1 - MIN_OPACITY_ALPHA))
|
||||
.rgb()
|
||||
.string(),
|
||||
);
|
||||
} else {
|
||||
const stops = getPaletteStops(options.palette, isDarkMode);
|
||||
ramp = positions.map((t) => sampleStops(stops, t));
|
||||
}
|
||||
|
||||
const positionOf = (count: number | null): number | null => {
|
||||
if (count === null || !Number.isFinite(count)) {
|
||||
return null;
|
||||
}
|
||||
return normalizeCount({ count, domain, scale: options.scale });
|
||||
};
|
||||
|
||||
// An explicit `minCount` above zero makes it a clamped value like any other.
|
||||
const zeroFill =
|
||||
domain.min <= 0 ? ZERO_FILL[isDarkMode ? 'dark' : 'light'] : null;
|
||||
|
||||
return {
|
||||
positionOf,
|
||||
colorFor: (count): string | null => {
|
||||
if (count === 0 && zeroFill !== null) {
|
||||
return zeroFill;
|
||||
}
|
||||
const t = positionOf(count);
|
||||
if (t === null) {
|
||||
return null;
|
||||
}
|
||||
const index = Math.min(Math.floor(t * steps), steps - 1);
|
||||
return ramp[index];
|
||||
},
|
||||
ramp,
|
||||
domain,
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import {
|
||||
createHeatmapColorResolver,
|
||||
HeatmapColorResolver,
|
||||
resolveCountDomain,
|
||||
} from './colorScale';
|
||||
createColorRampResolver,
|
||||
ColorRampResolver,
|
||||
resolveColorDomain,
|
||||
} from 'lib/visualization/colorRamp/colorScale';
|
||||
import { resolveColumnIndex, resolveRowIndex } from './geometry';
|
||||
import {
|
||||
createHoverOverlay,
|
||||
@@ -12,13 +12,14 @@ import {
|
||||
showHoverOverlay,
|
||||
} from './hoverOverlay';
|
||||
import { createHatchPattern, drawCells, drawOverflowBoundary } from './paint';
|
||||
import { HeatmapCell, HeatmapColorOptions, HeatmapYAxis } from './types';
|
||||
import { HeatmapCell, HeatmapYAxis } from './types';
|
||||
import { ColorRampOptions } from 'lib/visualization/colorRamp/types';
|
||||
|
||||
export interface HeatmapRenderOptions {
|
||||
yAxis: HeatmapYAxis;
|
||||
/** Column width in seconds. */
|
||||
step: number;
|
||||
colors: HeatmapColorOptions;
|
||||
colors: ColorRampOptions;
|
||||
isDarkMode: boolean;
|
||||
/** Opacity-mode fill when `colors.fill` is empty. */
|
||||
seriesColor: string;
|
||||
@@ -58,15 +59,15 @@ export function createHeatmapHooks({
|
||||
// config-build time. Resolving lazily keeps a refetch on uPlot's `setData` path
|
||||
// rather than forcing a rebuild.
|
||||
let cachedData: uPlot.AlignedData | null = null;
|
||||
let cachedResolver: HeatmapColorResolver | null = null;
|
||||
let cachedResolver: ColorRampResolver | null = null;
|
||||
|
||||
function getResolver(u: uPlot): HeatmapColorResolver {
|
||||
function getResolver(u: uPlot): ColorRampResolver {
|
||||
if (cachedResolver && cachedData === u.data) {
|
||||
return cachedResolver;
|
||||
}
|
||||
cachedResolver = createHeatmapColorResolver({
|
||||
cachedResolver = createColorRampResolver({
|
||||
options: colors,
|
||||
domain: resolveCountDomain(
|
||||
domain: resolveColorDomain(
|
||||
colors,
|
||||
u.data.slice(1) as Array<Array<number | null>>,
|
||||
),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { HeatmapColorResolver } from './colorScale';
|
||||
import { ColorRampResolver } from 'lib/visualization/colorRamp/colorScale';
|
||||
import { HeatmapYAxis } from './types';
|
||||
|
||||
/** Cells at least this wide/tall keep a hairline separator. */
|
||||
@@ -54,7 +54,7 @@ export function drawCells({
|
||||
u: uPlot;
|
||||
yAxis: HeatmapYAxis;
|
||||
step: number;
|
||||
resolver: HeatmapColorResolver;
|
||||
resolver: ColorRampResolver;
|
||||
hatchPattern: CanvasPattern | null;
|
||||
}): void {
|
||||
const { ctx } = u;
|
||||
|
||||
@@ -1,44 +1,3 @@
|
||||
export enum HeatmapColorScale {
|
||||
Log = 'log',
|
||||
Sqrt = 'sqrt',
|
||||
Linear = 'linear',
|
||||
}
|
||||
|
||||
export enum HeatmapColorMode {
|
||||
Palette = 'palette',
|
||||
Opacity = 'opacity',
|
||||
}
|
||||
|
||||
/** Sequential ramps only: colour means "count", so a midpoint or hue cycle would
|
||||
* read as a threshold that does not exist. */
|
||||
export enum HeatmapColorPalette {
|
||||
Ice = 'ice',
|
||||
Moss = 'moss',
|
||||
Rust = 'rust',
|
||||
Graphite = 'graphite',
|
||||
Ember = 'ember',
|
||||
Lagoon = 'lagoon',
|
||||
Orchid = 'orchid',
|
||||
Verdant = 'verdant',
|
||||
Lava = 'lava',
|
||||
Beacon = 'beacon',
|
||||
}
|
||||
|
||||
export interface HeatmapColorOptions {
|
||||
mode: HeatmapColorMode;
|
||||
scale: HeatmapColorScale;
|
||||
/** `null` derives it, which is always 0 — a count of 0 belongs at the bottom. */
|
||||
minCount: number | null;
|
||||
/** `null` derives it from the grid's highest count. */
|
||||
maxCount: number | null;
|
||||
palette: HeatmapColorPalette;
|
||||
/** Colour steps the ramp is quantised into, 2..128. Unrelated to `step`, the
|
||||
* column width in seconds. */
|
||||
steps: number;
|
||||
/** Opacity mode. Empty falls back to the caller's series colour. */
|
||||
fill: string;
|
||||
}
|
||||
|
||||
/** Row-height distribution of the bucket axis. */
|
||||
export enum HeatmapAxisScale {
|
||||
/** Whichever of the three below the boundaries admit: log when they are all
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Same theme hooks as the SVG chart; canvas reads them through readPalette().
|
||||
.wrapper {
|
||||
--geo-no-data: var(--l3-background);
|
||||
--geo-context: var(--l2-background);
|
||||
--geo-border: var(--l1-background);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wrapper[data-legend-position='right'] {
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mapArea {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
}
|
||||
247
frontend/src/lib/visualization/charts/GeoMap/GeoMap.tsx
Normal file
247
frontend/src/lib/visualization/charts/GeoMap/GeoMap.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
import { LegendPosition } from 'lib/uPlotV2/components/types';
|
||||
|
||||
import CountryTooltip from './components/CountryTooltip/CountryTooltip';
|
||||
import MapLegend from './components/MapLegend/MapLegend';
|
||||
import MapStatus from './components/MapStatus/MapStatus';
|
||||
import ZoomControls from './components/ZoomControls/ZoomControls';
|
||||
import { useGeoMapScales } from './hooks/useGeoMapScales';
|
||||
import { useGeoMapShapes } from './hooks/useGeoMapShapes';
|
||||
import { formatCoordinates } from './utils/coordinates';
|
||||
import { findShapeAt } from './utils/olFeatures';
|
||||
import { pointAsDatum } from './utils/scales';
|
||||
import { GeoMapProps } from './types';
|
||||
import { useOlLayers } from './hooks/useOlLayers';
|
||||
import { useOlMap } from './hooks/useOlMap';
|
||||
import { useOlPointer } from './hooks/useOlPointer';
|
||||
import { useOlPointsLayer } from './hooks/useOlPointsLayer';
|
||||
import { useOlView } from './hooks/useOlView';
|
||||
import { OlPalette, readPalette } from './utils/olStyles';
|
||||
|
||||
import styles from './GeoMap.module.scss';
|
||||
|
||||
export default function GeoMap({
|
||||
data,
|
||||
points,
|
||||
view,
|
||||
country,
|
||||
layer,
|
||||
colors,
|
||||
bubbleRadius,
|
||||
formatValue,
|
||||
valueLabel,
|
||||
sizeMetric,
|
||||
showLabels = true,
|
||||
colorKeys,
|
||||
legendPosition = LegendPosition.BOTTOM,
|
||||
isDarkMode,
|
||||
onSelect,
|
||||
renderTooltipFooter,
|
||||
}: GeoMapProps): JSX.Element {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const mapAreaRef = useRef<HTMLDivElement>(null);
|
||||
const canvasRef = useRef<HTMLDivElement>(null);
|
||||
const { width, height } = useResizeObserver(mapAreaRef, 100);
|
||||
const [palette, setPalette] = useState<OlPalette | null>(null);
|
||||
|
||||
const {
|
||||
tooltipOpen,
|
||||
tooltipLeft,
|
||||
tooltipTop,
|
||||
tooltipData,
|
||||
showTooltip,
|
||||
hideTooltip,
|
||||
} = useTooltip<string>();
|
||||
const { containerRef, TooltipInPortal } = useTooltipInPortal({
|
||||
scroll: true,
|
||||
detectBounds: true,
|
||||
});
|
||||
|
||||
// On the points layer the scales colour and size points, keyed by point id.
|
||||
const scaleData = useMemo(
|
||||
() => (layer === 'points' ? (points ?? []).map(pointAsDatum) : data),
|
||||
[layer, points, data],
|
||||
);
|
||||
const { resolver, valueByCode, sizeByCode, radiusFor, fillFor } =
|
||||
useGeoMapScales(
|
||||
scaleData,
|
||||
colors,
|
||||
bubbleRadius,
|
||||
isDarkMode,
|
||||
Boolean(sizeMetric),
|
||||
);
|
||||
const maxSize = useMemo(
|
||||
() => [...sizeByCode.values()].reduce((max, size) => Math.max(max, size), 0),
|
||||
[sizeByCode],
|
||||
);
|
||||
const { shapes, status, retry } = useGeoMapShapes(view, country);
|
||||
const handle = useOlMap(canvasRef);
|
||||
const pointsLayer = useOlPointsLayer(handle, layer === 'points');
|
||||
|
||||
useEffect(() => {
|
||||
if (wrapperRef.current) {
|
||||
setPalette(readPalette(wrapperRef.current));
|
||||
}
|
||||
}, [isDarkMode]);
|
||||
|
||||
const handleHover = useCallback(
|
||||
(code: string | null, clientX: number, clientY: number): void => {
|
||||
const rect = mapAreaRef.current?.getBoundingClientRect();
|
||||
if (!code || !rect || !valueByCode.has(code)) {
|
||||
hideTooltip();
|
||||
return;
|
||||
}
|
||||
showTooltip({
|
||||
tooltipData: code,
|
||||
tooltipLeft: clientX - rect.left,
|
||||
tooltipTop: clientY - rect.top,
|
||||
});
|
||||
},
|
||||
[valueByCode, showTooltip, hideTooltip],
|
||||
);
|
||||
|
||||
const pointById = useMemo(
|
||||
() =>
|
||||
new Map(
|
||||
(layer === 'points' ? (points ?? []) : []).map((point) => [
|
||||
point.id,
|
||||
point,
|
||||
]),
|
||||
),
|
||||
[layer, points],
|
||||
);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(code: string, clientX: number, clientY: number): void => {
|
||||
const datum = valueByCode.get(code);
|
||||
if (datum && onSelect) {
|
||||
hideTooltip();
|
||||
onSelect({ datum, point: pointById.get(code), clientX, clientY });
|
||||
}
|
||||
},
|
||||
[valueByCode, pointById, onSelect, hideTooltip],
|
||||
);
|
||||
|
||||
const hovered =
|
||||
tooltipOpen && tooltipData ? valueByCode.get(tooltipData) : undefined;
|
||||
const hoveredPoint = hovered ? pointById.get(hovered.code) : undefined;
|
||||
// A point without a label is named by the shape it falls in.
|
||||
const hoveredPlace = useMemo(() => {
|
||||
if (!hoveredPoint || hoveredPoint.label || !handle || !shapes) {
|
||||
return undefined;
|
||||
}
|
||||
const code = findShapeAt(
|
||||
[handle.layers.members.getSource(), handle.layers.context.getSource()],
|
||||
[hoveredPoint.lon, hoveredPoint.lat],
|
||||
);
|
||||
return code ? shapes.nameOf(code) : undefined;
|
||||
}, [hoveredPoint, handle, shapes]);
|
||||
|
||||
useOlLayers({
|
||||
handle,
|
||||
shapes,
|
||||
data,
|
||||
points,
|
||||
pointsLayer,
|
||||
layer,
|
||||
palette,
|
||||
fillFor,
|
||||
sizeByCode,
|
||||
radiusFor,
|
||||
hasSizeMetric: Boolean(sizeMetric),
|
||||
showLabels,
|
||||
hoveredCode: hovered?.code ?? null,
|
||||
});
|
||||
const zoom = useOlView(
|
||||
handle,
|
||||
view,
|
||||
country,
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
layer === 'points' ? points : undefined,
|
||||
);
|
||||
useOlPointer(handle, handleHover, handleSelect, pointsLayer.layer);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.wrapper}
|
||||
ref={wrapperRef}
|
||||
data-geo-map
|
||||
data-legend-position={legendPosition}
|
||||
data-testid="geo-map"
|
||||
>
|
||||
<div
|
||||
className={styles.mapArea}
|
||||
ref={(node): void => {
|
||||
(mapAreaRef as React.MutableRefObject<HTMLDivElement | null>).current =
|
||||
node;
|
||||
containerRef(node);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={canvasRef}
|
||||
className={styles.canvas}
|
||||
data-testid="geo-map-canvas"
|
||||
/>
|
||||
{status !== 'ready' && <MapStatus status={status} onRetry={retry} />}
|
||||
{status === 'ready' && (
|
||||
<ZoomControls
|
||||
canZoomIn={zoom.canZoomIn}
|
||||
canZoomOut={zoom.canZoomOut}
|
||||
onZoomIn={zoom.zoomIn}
|
||||
onZoomOut={zoom.zoomOut}
|
||||
onReset={zoom.reset}
|
||||
/>
|
||||
)}
|
||||
{hovered && (
|
||||
<TooltipInPortal
|
||||
left={tooltipLeft}
|
||||
top={tooltipTop}
|
||||
className={styles.tooltip}
|
||||
unstyled
|
||||
applyPositionStyle
|
||||
>
|
||||
<CountryTooltip
|
||||
datum={hovered}
|
||||
name={
|
||||
hoveredPoint
|
||||
? hoveredPoint.label ||
|
||||
hoveredPlace ||
|
||||
formatCoordinates(hoveredPoint.lat, hoveredPoint.lon)
|
||||
: (shapes?.nameOf(hovered.code) ?? hovered.code)
|
||||
}
|
||||
place={
|
||||
hoveredPoint && formatCoordinates(hoveredPoint.lat, hoveredPoint.lon)
|
||||
}
|
||||
color={fillFor(hovered.code) ?? resolver.colorFor(hovered.value)}
|
||||
formatValue={formatValue}
|
||||
valueLabel={valueLabel}
|
||||
sizeMetric={sizeMetric}
|
||||
footer={renderTooltipFooter?.()}
|
||||
/>
|
||||
</TooltipInPortal>
|
||||
)}
|
||||
</div>
|
||||
<MapLegend
|
||||
resolver={resolver}
|
||||
scale={colors.scale}
|
||||
formatValue={formatValue}
|
||||
markerValue={hovered?.value ?? null}
|
||||
valueLabel={valueLabel}
|
||||
colorKeys={colorKeys}
|
||||
position={legendPosition}
|
||||
size={
|
||||
sizeMetric && {
|
||||
label: sizeMetric.label,
|
||||
maxSize,
|
||||
radiusFor,
|
||||
formatValue: sizeMetric.formatValue,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[1.406456,42.428677,1.765091,42.649362],"transform":{"scale":[0.00003586708670867087,0.000022070707070707384],"translate":[1.406456,42.428677]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2]],"id":"AD-02","properties":{"code":"AD-02","name":"Canillo","label":[1.605,42.584]}},{"type":"Polygon","arcs":[[3,4,5,-2]],"id":"AD-03","properties":{"code":"AD-03","name":"Encamp","label":[1.644,42.538]}},{"type":"Polygon","arcs":[[6,-6,7,8,9]],"id":"AD-04","properties":{"code":"AD-04","name":"La Massana","label":[1.471,42.55]}},{"type":"Polygon","arcs":[[-3,-7,10]],"id":"AD-05","properties":{"code":"AD-05","name":"Ordino","label":[1.524,42.604]}},{"type":"Polygon","arcs":[[-9,11,12,13]],"id":"AD-06","properties":{"code":"AD-06","name":"Sant Julià de Lòria","label":[1.48,42.451]}},{"type":"Polygon","arcs":[[14,-12,-8]],"id":"AD-07","properties":{"code":"AD-07","name":"Andorra la Vella","label":[1.511,42.513]}},{"type":"Polygon","arcs":[[15,-13,-15,-5]],"id":"AD-08","properties":{"code":"AD-08","name":"Escaldes-Engordany","label":[1.594,42.495]}}]}},"arcs":[[[5322,8756],[306,-172],[3169,-375],[-242,-920],[450,-342],[648,-241],[235,-409],[111,-194],[-700,-79],[-510,-594],[-128,-391]],[[8661,5039],[-190,223],[-269,202],[-291,168],[-103,573],[-104,404],[-394,-202],[-539,0],[-581,-101],[-663,-168],[-539,0],[-394,0],[-436,-68]],[[4158,6070],[63,539],[186,438],[291,337],[373,135],[290,135],[0,404],[-39,698]],[[8661,5039],[-181,-551],[-100,-1130],[-266,-377],[-311,-175],[-337,-5],[-331,146],[-72,140],[-78,38]],[[6985,3125],[-90,553],[-352,371],[-332,202],[-580,67],[-436,-101],[-373,236],[-83,404],[-207,135],[-353,-34]],[[4179,4958],[0,641],[-21,471]],[[1253,7855],[231,-235],[311,0],[290,-67],[145,-236],[166,-573],[249,-303],[394,-135],[477,-135],[373,-67],[269,-34]],[[4179,4958],[-352,0],[-332,-168],[-394,-101],[-415,-337],[-435,-303]],[[2251,4049],[-394,-135],[-373,-236],[-271,-262]],[[1213,3416],[-95,717],[-490,524],[-628,-101],[92,516],[461,959],[3,175],[-233,190],[34,427],[156,458],[124,272],[616,302]],[[1253,7855],[430,1786],[882,-55],[818,376],[426,37],[1513,-1243]],[[2251,4049],[41,-405],[21,-269],[104,-438],[166,-202]],[[2583,2735],[477,-135],[373,-169],[518,101],[456,68],[291,-68],[-187,-404],[41,-472],[82,-551]],[[4634,1105],[-787,-123],[-156,-213],[-121,-260],[-176,-257],[-322,-215],[-228,-37],[-1688,270],[-320,286],[0,568],[-804,1508],[472,258],[159,50],[550,476]],[[4179,4958],[-186,-370],[-311,-371],[-146,-236],[-165,-370],[-229,-472],[-331,-236],[-228,-168]],[[6985,3125],[-84,-51],[-101,-141],[-302,-1223],[-893,-453],[-971,-152]]],"aliases":{},"names":{"ordino":"AD-05","parroquia d'ordino":"AD-05","canillo":"AD-02","encamp":"AD-03","parroquia d'encamp":"AD-03","la massana":"AD-04","parroquia de la massana":"AD-04","massana":"AD-04","sant julià de lòria":"AD-06","parroquia de sant julia de loria":"AD-06","sant juliã de lã²ria":"AD-06","escaldes-engordany":"AD-08","parroquia d'escaldes-engordany":"AD-08","andorra la vella":"AD-07","parroquia d'andorra la vella":"AD-07"}}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[51.569347,22.620946,56.383155522852284,26.07341032343234],"transform":{"scale":[0.0004814289951847469,0.0003452809604392781],"translate":[51.569347,22.620946]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1]]],"id":"AE-AJ","properties":{"code":"AE-AJ","name":"Ajman","label":[55.541,25.397]}},{"type":"MultiPolygon","arcs":[[[2,3,4]],[[5]],[[6]],[[7]],[[8]],[[9]],[[10]],[[11]],[[12]]],"id":"AE-AZ","properties":{"code":"AE-AZ","name":"Abu Dhabi","label":[54.082,23.524]}},{"type":"MultiPolygon","arcs":[[[13,14]],[[15,-4,16]]],"id":"AE-DU","properties":{"code":"AE-DU","name":"Dubay","label":[55.344,24.951]}},{"type":"MultiPolygon","arcs":[[[17,18,19,20,21,22,23]],[[24,25,26,27,28,29]],[[30,31,32]]],"id":"AE-FU","properties":{"code":"AE-FU","name":"Fujayrah","label":[56.222,25.475]}},{"type":"MultiPolygon","arcs":[[[33,-23,34,-31,35,-15,36,37,-26]],[[-28,38,39,40]]],"id":"AE-RK","properties":{"code":"AE-RK","name":"Ras Al Khaymah","label":[55.979,25.696]}},{"type":"MultiPolygon","arcs":[[[41,-30,42,-18]],[[-20,43]],[[44,-5,-16,45,-2,46,47,-39,-27,-38]],[[-22,48,-32,-35]]],"id":"AE-SH","properties":{"code":"AE-SH","name":"Sharjah","label":[55.779,25.172]}},{"type":"MultiPolygon","arcs":[[[49,-40,-48]]],"id":"AE-UQ","properties":{"code":"AE-UQ","name":"Umm Al Qaywayn","label":[55.722,25.522]}}]}},"arcs":[[[8008,8033],[140,190]],[[8148,8223],[291,-136],[-72,-155],[-175,12],[-184,89]],[[8788,6001],[38,-144],[-122,-246],[31,-384],[68,-246],[-107,-320],[190,-55],[178,27],[135,-403],[-397,-214],[-92,60],[-240,-9],[-395,-179],[153,-257],[16,-303],[-124,-272],[-69,-359],[-145,-240],[-113,-562],[-198,-446],[-71,-365],[13,-422],[-23,-423],[-169,-239],[-5240,898],[-92,68],[-1963,3255],[-42,143],[-8,372],[44,82],[-34,231],[103,-72],[22,-326],[79,148],[95,-159],[84,195],[49,-145],[-21,-185],[78,-509],[445,-37],[90,-70],[326,30],[243,97],[485,419],[13,137],[110,-28],[75,-131],[351,21],[225,-40],[109,80],[140,-127],[165,-1],[-9,129],[152,-71],[79,-124],[381,61],[171,-66],[129,-140],[252,64],[314,-25],[202,121],[357,118],[134,171],[136,50],[146,144],[124,-47],[132,179],[82,209],[0,169],[113,-41],[-85,160],[-170,137],[142,100],[57,-158],[143,-20],[113,259],[-42,68],[113,130],[-62,192],[138,208],[620,480]],[[7138,6803],[275,-965],[130,-121],[440,23],[537,307]],[[8520,6047],[268,-46]],[[4336,4453],[-61,39],[198,168],[242,100],[260,-266],[-92,-95],[-178,36],[-194,-54],[-175,72]],[[5446,4652],[-163,129],[51,160],[106,-143],[6,-146]],[[2088,4990],[76,85],[78,-185],[-118,-162],[-75,155],[39,107]],[[3642,4755],[-283,38],[363,217],[115,180],[63,-96],[-40,-148],[-200,-116],[-18,-75]],[[5844,5267],[-99,85],[127,156],[1,-114],[152,-136],[-181,9]],[[5603,4847],[-173,59],[102,124],[71,-183]],[[5726,5074],[-35,-171],[-62,146],[97,25]],[[1538,5556],[61,-110],[-116,-13],[55,123]],[[9559,6381],[-110,-258],[-87,-15],[61,262]],[[9423,6370],[136,11]],[[7801,7779],[257,0],[316,-309],[58,-104],[60,-317],[-79,-161],[86,-300],[42,-367],[-21,-174]],[[7138,6803],[159,136],[449,700],[55,140]],[[9928,7742],[71,79]],[[9999,7821],[-43,-733]],[[9956,7088],[-160,30],[42,-168],[-49,-200]],[[9789,6750],[-24,-36]],[[9765,6714],[-35,110],[-186,221]],[[9544,7045],[4,482],[85,20]],[[9633,7547],[60,-45],[225,160],[10,80]],[[9745,7849],[-57,-54]],[[9688,7795],[-147,65],[25,-142],[-137,-196],[-255,21]],[[9174,7543],[-18,164],[12,144],[38,142]],[[9206,7993],[158,-109],[116,62],[-86,153],[75,140],[-49,100],[-253,-14],[-35,235],[214,188],[174,48]],[[9520,8796],[143,-128],[120,39],[126,-66],[63,-229],[-11,-296]],[[9961,8116],[-216,-267]],[[9774,6520],[-45,66]],[[9729,6586],[47,55]],[[9776,6641],[-2,-121]],[[9688,7795],[-55,-248]],[[9544,7045],[35,-379],[150,-80]],[[9774,6520],[-215,-139]],[[9423,6370],[-167,180],[-103,260]],[[9153,6810],[-52,324],[58,177],[-53,93],[68,139]],[[9206,7993],[-152,-96]],[[9054,7897],[-99,133],[-75,231],[-88,442],[-116,106]],[[8676,8809],[274,225],[283,402],[-14,81],[145,446],[177,36],[43,-170],[-27,-399],[-54,-113],[48,-174],[-55,-130],[24,-217]],[[9928,7742],[-183,107]],[[9961,8116],[38,-295]],[[9956,7088],[43,-261],[-210,-77]],[[9153,6810],[-164,-10],[-140,-138],[-85,-191],[24,-470]],[[7801,7779],[207,254]],[[8148,8223],[48,153]],[[8196,8376],[512,-417],[94,-240],[202,26],[50,152]],[[9765,6714],[11,-73]],[[8196,8376],[231,67],[64,178],[185,188]]],"aliases":{},"names":{"fujayrah":"AE-FU","emirate of fujairah":"AE-FU","al fujayrah":"AE-FU","sharjah":"AE-SH","emirate of sharjah":"AE-SH","ash shariqah":"AE-SH","ras al khaymah":"AE-RK","emirate of ras al khaimah":"AE-RK","ra's al khaymah":"AE-RK","dubay":"AE-DU","emirate of dubai":"AE-DU","dubayy":"AE-DU","abu dhabi":"AE-AZ","emirate of abu dhabi":"AE-AZ","abu zaby":"AE-AZ","umm al qaywayn":"AE-UQ","emirate of umm al quwain":"AE-UQ","ajman":"AE-AJ","emirate of ajman":"AE-AJ","`ajman":"AE-AJ"}}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-62.34826,16.93197,-61.667592,17.727688],"transform":{"scale":[0.0000680736073607365,0.00007957975797579766],"translate":[-62.34826,16.93197]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2]],"id":"AG-03","properties":{"code":"AG-03","name":"Saint George","label":[-61.78,17.108]}},{"type":"Polygon","arcs":[[3,-3,4,5,6]],"id":"AG-04","properties":{"code":"AG-04","name":"Saint John","label":[-61.824,17.11]}},{"type":"Polygon","arcs":[[7,-7,8]],"id":"AG-05","properties":{"code":"AG-05","name":"Saint Mary","label":[-61.857,17.037]}},{"type":"Polygon","arcs":[[9,-9,-6,10,11]],"id":"AG-06","properties":{"code":"AG-06","name":"Saint Paul","label":[-61.767,17.035]}},{"type":"Polygon","arcs":[[12,13,-11,-5,-2]],"id":"AG-07","properties":{"code":"AG-07","name":"Saint Peter","label":[-61.74,17.094]}},{"type":"Polygon","arcs":[[14,-12,-14]],"id":"AG-08","properties":{"code":"AG-08","name":"Saint Philip","label":[-61.702,17.06]}},{"type":"Polygon","arcs":[[15]],"id":"AG-10","properties":{"code":"AG-10","name":"Barbuda","label":[-61.792,17.616]}},{"type":"Polygon","arcs":[[16]],"id":"AG-11","properties":{"code":"AG-11","name":"Redonda","label":[-62.347,16.934]}}]}},"arcs":[[[8151,2928],[187,-224],[112,-259],[244,-156]],[[8694,2289],[-81,-75],[75,-103],[15,-128],[-165,-51],[-105,-180]],[[8433,1752],[-105,39],[-210,115],[30,141],[-45,270],[-120,89],[-15,257],[210,13],[-27,252]],[[7081,1946],[-105,34],[-305,138],[162,125],[260,92],[339,83],[-119,166],[24,149],[134,131],[212,112],[468,-48]],[[8433,1752],[0,-141]],[[8433,1611],[-150,26],[-330,-77],[-120,-102]],[[7833,1458],[-75,141],[-225,-206],[60,321],[75,141],[-15,38],[-255,52],[-134,102],[-183,-101]],[[7994,947],[-202,-30],[-306,-1],[-192,35],[-469,234],[-99,115],[100,171],[64,144],[-52,122],[5,108],[238,101]],[[7833,1458],[105,-167],[-45,-141],[101,-203]],[[9138,1338],[-115,-45],[-5,-114],[69,-309],[-110,-150],[-240,95],[-239,178],[-104,95],[-99,-21],[-301,-120]],[[8433,1611],[375,0],[224,52]],[[9032,1663],[106,-325]],[[8694,2289],[14,112],[40,74],[139,158],[83,-107],[114,-63],[144,-20],[168,18],[-71,-66],[-41,-47],[-63,-34],[-134,-25],[67,-180],[151,-84]],[[9305,2025],[-198,-119],[-75,-243]],[[9305,2025],[194,4],[199,89],[109,0],[75,-62],[51,-62],[66,-141],[-100,6],[-202,-12],[-100,6],[137,-140],[126,-67],[71,-68],[-32,-147],[-166,-217],[-166,-10],[-190,80],[-239,54]],[[7482,8311],[-501,1474],[1745,-526],[375,-802],[-616,-747],[-1003,601]],[[39,90],[15,16],[22,6],[2,-32],[-2,-41],[-20,-32],[-38,-7],[-18,21],[14,47],[25,22]]],"aliases":{},"names":{"saint paul":"AG-06","parish of saint paul":"AG-06","saint philip":"AG-08","parish of saint philip":"AG-08","saint peter":"AG-07","parish of saint peter":"AG-07","saint george":"AG-03","parish of saint george":"AG-03","saint john":"AG-04","parish of saint john":"AG-04","saint mary":"AG-05","parish of saint mary":"AG-05","barbuda":"AG-10","redonda":"AG-11"}}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[43.436294,38.863701,46.602612,41.290452],"transform":{"scale":[0.00031666346634663504,0.000242699369936994],"translate":[43.436294,38.863701]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2,3,4,5]],"id":"AM-AG","properties":{"code":"AM-AG","name":"Aragatsotn","label":[44.074,40.321]}},{"type":"Polygon","arcs":[[6,7,8,9,10,11]],"id":"AM-AR","properties":{"code":"AM-AR","name":"Ararat","label":[44.73,39.92]}},{"type":"Polygon","arcs":[[-6,12,-8,13]],"id":"AM-AV","properties":{"code":"AM-AV","name":"Armavir","label":[44.046,40.099]}},{"type":"Polygon","arcs":[[-9,-13,-5,14]],"id":"AM-ER","properties":{"code":"AM-ER","name":"Erevan","label":[44.533,40.147]}},{"type":"MultiPolygon","arcs":[[[15,16,-11,17,18]],[[19]]],"id":"AM-GR","properties":{"code":"AM-GR","name":"Gegharkunik","label":[45.385,40.278]}},{"type":"Polygon","arcs":[[20,-18,-10,-15,-4,21]],"id":"AM-KT","properties":{"code":"AM-KT","name":"Kotayk","label":[44.719,40.375]}},{"type":"Polygon","arcs":[[22,-22,-3,23,24]],"id":"AM-LO","properties":{"code":"AM-LO","name":"Lori","label":[44.452,40.935]}},{"type":"Polygon","arcs":[[-24,-2,25]],"id":"AM-SH","properties":{"code":"AM-SH","name":"Shirak","label":[43.818,40.819]}},{"type":"Polygon","arcs":[[26,27]],"id":"AM-SU","properties":{"code":"AM-SU","name":"Syunik","label":[46.152,39.248]}},{"type":"Polygon","arcs":[[-19,-21,-23,28],[29],[30]],"id":"AM-TV","properties":{"code":"AM-TV","name":"Tavush","label":[45.176,40.888]}},{"type":"Polygon","arcs":[[31,-28,32,-12,-17]],"id":"AM-VD","properties":{"code":"AM-VD","name":"Vayots Dzor","label":[45.442,39.718]}}]}},"arcs":[[[802,5673],[-303,432],[95,271]],[[594,6376],[257,222],[276,136],[470,-110],[198,64],[137,-161],[478,263],[-59,308],[-170,-8],[-148,231],[271,412]],[[2304,7733],[281,-88],[103,91],[354,-129],[59,-116]],[[3101,7491],[33,-282],[393,-202],[-178,-610],[24,-149],[-301,-369],[53,-370]],[[3125,5509],[-24,-47]],[[3101,5462],[-430,202],[-339,18],[-121,-237],[-375,3],[-638,406],[-396,-181]],[[5175,3651],[-131,85],[-336,-188],[-482,-91],[-291,389],[-363,313],[-143,249],[-446,314]],[[2983,4722],[-94,302],[282,290]],[[3171,5314],[123,-234],[240,-172],[291,250]],[[3825,5158],[142,-133],[287,70],[564,255],[214,5]],[[5032,5355],[-47,-346],[217,-544],[82,-336]],[[5284,4129],[-109,-478]],[[3101,5462],[70,-148]],[[2983,4722],[-397,139],[-703,-144],[-380,65],[-346,191],[-433,163],[-41,119],[217,125],[-98,293]],[[3125,5509],[461,117],[146,-140],[-88,-248],[181,-80]],[[6118,7365],[482,-866],[187,-174],[735,-416],[137,-151],[199,21],[172,-176],[-89,-438],[-244,-443],[-220,-22],[-519,87],[-196,-171]],[[6762,4616],[-100,61],[-272,-198],[-624,-43],[-482,-307]],[[5032,5355],[-262,324],[34,208],[-148,546],[-146,155],[7,182],[-184,102],[-37,488]],[[4296,7360],[375,79],[257,-130],[214,68],[106,184],[240,137],[214,-24],[416,-309]],[[6581,7403],[128,-53],[-149,-188],[-108,199],[129,42]],[[4220,7423],[76,-63]],[[3101,7491],[342,74],[500,-143],[277,1]],[[4476,9723],[216,-112],[-121,-322],[165,-227],[-204,-156],[-313,-105],[420,-373],[-195,-356],[-289,-207],[65,-442]],[[2304,7733],[-179,13],[-228,169],[-69,186],[79,215],[127,-30],[134,345],[-205,61],[61,125],[-351,350],[37,313]],[[1710,9480],[397,44],[276,226],[239,-139],[286,65],[309,-130],[195,108],[110,-122],[194,190],[196,-61],[462,-9],[102,71]],[[594,6376],[-192,221],[281,266],[242,609],[-57,431],[-163,289],[-38,255],[-135,204],[-456,245],[-63,345],[399,72],[515,-71],[421,215],[362,23]],[[7493,4069],[167,-192],[370,-149],[209,-333],[318,-203],[191,-208],[433,126],[229,-183],[200,19],[151,-167],[-92,-265],[-292,-93],[-98,-298],[381,-226],[71,-186],[268,-262],[-279,-127],[-303,113],[-70,-196],[263,-205],[57,-252],[-68,-321],[120,-385],[-385,166],[-276,-25],[-323,-189],[-210,-28],[-184,358],[-165,466],[-212,362],[88,343],[-86,155],[-427,291],[-179,47],[114,386],[-47,415],[-250,81]],[[7177,2904],[71,571],[-60,112],[305,482]],[[4476,9723],[-104,206],[424,-44],[150,114],[156,-405],[261,-18],[210,-252],[-421,-65],[-33,-132],[198,3],[321,-268],[359,-85],[265,97],[-9,-231],[384,-352],[193,-55],[-137,-354],[-496,-279],[-79,-238]],[[4988,8898],[10,213],[-184,-62],[174,-151]],[[5601,8670],[0,0]],[[6762,4616],[106,-102],[329,-34],[155,-119],[141,-292]],[[7177,2904],[-387,-95],[-407,-231],[-457,183],[-167,272],[-291,-141],[-74,100],[-10,390],[-209,269]]],"aliases":{},"names":{"tavush":"AM-TV","tavushi marz":"AM-TV","lori":"AM-LO","lorru marz":"AM-LO","shirak":"AM-SH","shiraki marz":"AM-SH","gegharkunik":"AM-GR","geghark'unik'i marz":"AM-GR","vayots dzor":"AM-VD","vayots' dzori marz":"AM-VD","syunik":"AM-SU","syunik'i marz":"AM-SU","ararat":"AM-AR","ararati marz":"AM-AR","aragatsotn":"AM-AG","aragatsotni marz":"AM-AG","armavir":"AM-AV","armaviri marz":"AM-AV","kotayk":"AM-KT","kotayk'i marz":"AM-KT","erevan":"AM-ER","yerevan":"AM-ER"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[15.716074,42.574393,19.057069,45.218533],"transform":{"scale":[0.0003341329132913289,0.00026444044404440445],"translate":[15.716074,42.574393]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1]]],"id":"BA-BIH","properties":{"code":"BA-BIH","name":"West Bosnia","label":[16.858,43.977]}},{"type":"Polygon","arcs":[[2,-1,3,4]],"id":"BA-BRC","properties":{"code":"BA-BRC","name":"Brčko Distrikt","label":[18.854,44.831]}},{"type":"MultiPolygon","arcs":[[[-5,5]],[[6]]],"id":"BA-SRP","properties":{"code":"BA-SRP","name":"Posavina","label":[18.298,45.06]}}]}},"arcs":[[[9074,8586],[81,-373],[86,-5]],[[9241,8208],[-17,-152],[-167,-97],[-9,-206],[326,-314],[128,33],[180,314],[214,0],[17,-541],[-317,-241],[-252,-38],[-253,-222],[-4,-222],[120,-319],[-167,-357],[-231,-205],[-249,-65],[-201,-214],[-9,-514],[-72,-87],[-296,17],[-21,-184],[120,-195],[210,-11],[376,-200],[184,-16],[197,335],[557,-11],[308,-173],[86,-167],[-514,-309],[-574,-32],[-689,405],[-150,0],[-115,-408],[47,-546],[-1079,27],[51,-190],[30,-708],[-197,-195],[193,-179],[-124,-538],[77,-308],[488,-449],[428,-444],[71,-282],[-436,203],[-1094,714],[-140,352],[-330,36],[-144,-108],[-290,166],[318,117],[-103,306],[-123,134],[-434,266],[-126,240],[-214,168],[-183,352],[57,317],[-141,156],[-463,131],[-781,734],[-333,243],[-101,262],[-792,813],[-261,101],[-332,475],[-85,281],[46,293],[-129,154],[-130,457],[-330,74],[116,183],[-227,188],[-220,300],[-269,-83],[-264,340],[168,222],[-128,310],[98,183],[-23,252],[72,447],[134,191],[517,29],[210,-185],[109,-216],[326,-306],[407,-151],[-210,-70],[-17,-195],[180,-119],[676,-49],[433,-81],[372,-336],[30,-313],[120,-82],[9,-562],[-86,-206],[-295,-16],[-561,179],[-176,-6],[13,-140],[612,-449],[120,-11],[129,-211],[736,-363],[184,-140],[64,-266],[360,-59],[43,433],[-231,287],[-99,254],[-4,503],[766,-62],[283,-287],[685,32],[557,363],[-5,287],[-175,156],[-64,195],[158,306],[381,0],[103,-87],[261,-443],[741,11],[-82,270],[-788,281],[-25,222],[604,54],[179,368],[150,76],[291,-35],[0,-130],[296,16],[462,-130]],[[9356,8705],[397,-51],[-196,-298],[-316,-148]],[[9074,8586],[63,179]],[[9137,8765],[37,-192],[182,132]],[[9137,8765],[-354,232],[-368,122],[-138,307],[178,-44],[-19,133],[461,-39],[112,-205],[188,-77],[-17,-348],[176,-141]],[[7547,9768],[253,-131],[211,-16],[177,-127],[-44,-136],[-257,-99],[-60,-134],[-225,12],[-249,226],[-88,179],[282,226]]],"aliases":{},"names":{"herzegovina-neretva":"BA-BIH","herzegovina-neretva canton":"BA-BIH","hercegovacko-neretvanski kanton":"BA-BIH","una-sana":"BA-BIH","una-sana canton":"BA-BIH","unsko-sanski kanton":"BA-BIH","posavina":"BA-SRP","posavina canton":"BA-SRP","posavski kanton":"BA-SRP","brčko distrikt":"BA-BRC","brčko":"BA-BRC","brcko district of bosnia and herzegovina":"BA-BRC","west bosnia":"BA-BIH","livno":"BA-BIH","hercegovacko-bosanski kanton":"BA-BIH","livanjski kanton":"BA-BIH","west herzegovina":"BA-BIH","west herzegovina canton":"BA-BIH","zapadno-hercegovacki kanton":"BA-BIH","central bosnia":"BA-BIH","central bosnia canton":"BA-BIH","srednjobosanski kanton":"BA-BIH","srednjebosanski kanton":"BA-BIH","zenica-doboj":"BA-BIH","zenica-doboj canton":"BA-BIH","zenicko-dobojski kanton":"BA-BIH","zeničko-dobojski kanton":"BA-BIH","tuzla":"BA-BIH","tuzla canton":"BA-BIH","tuzlanski kanton":"BA-BIH","sarajevo":"BA-BIH","sarajevo canton":"BA-BIH","kanton sarajevo":"BA-BIH","sarajevski kanton":"BA-BIH","bosnian podrinje":"BA-BIH","bosnian podrinje canton":"BA-BIH","bosansko-podrinjski kanton":"BA-BIH"}}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-59.654205,13.051174,-59.42691,13.34455],"transform":{"scale":[0.000022731773177317536,0.00002934053405340537],"translate":[-59.654205,13.051174]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2,3]],"id":"BB-01","properties":{"code":"BB-01","name":"Christ Church","label":[-59.538,13.087]}},{"type":"Polygon","arcs":[[4,5,6,7,8]],"id":"BB-02","properties":{"code":"BB-02","name":"Saint Andrew","label":[-59.576,13.255]}},{"type":"Polygon","arcs":[[9,10,-3,11,12,13]],"id":"BB-03","properties":{"code":"BB-03","name":"Saint George","label":[-59.548,13.149]}},{"type":"Polygon","arcs":[[14,15,-8,16,17]],"id":"BB-04","properties":{"code":"BB-04","name":"Saint James","label":[-59.626,13.192]}},{"type":"Polygon","arcs":[[18,19,-10,20]],"id":"BB-05","properties":{"code":"BB-05","name":"Saint John","label":[-59.504,13.18]}},{"type":"Polygon","arcs":[[21,-21,-14,22,-6]],"id":"BB-06","properties":{"code":"BB-06","name":"Saint Joseph","label":[-59.549,13.212]}},{"type":"Polygon","arcs":[[23,24]],"id":"BB-07","properties":{"code":"BB-07","name":"Saint Lucy","label":[-59.615,13.318]}},{"type":"Polygon","arcs":[[25,-18,26,-12,-2]],"id":"BB-08","properties":{"code":"BB-08","name":"Saint Michael","label":[-59.605,13.118]}},{"type":"Polygon","arcs":[[27,-9,-16,28,-25]],"id":"BB-09","properties":{"code":"BB-09","name":"Saint Peter","label":[-59.611,13.268]}},{"type":"Polygon","arcs":[[29,-4,-11,-20]],"id":"BB-10","properties":{"code":"BB-10","name":"Saint Philip","label":[-59.47,13.137]}},{"type":"Polygon","arcs":[[-23,-13,-27,-17,-7]],"id":"BB-11","properties":{"code":"BB-11","name":"Saint Thomas","label":[-59.59,13.188]}}]}},"arcs":[[[7849,1204],[-517,-271],[-938,-724],[-346,-31],[-281,2],[-272,-36],[-300,-144],[-434,563],[-812,352],[-988,176],[-588,32]],[[2373,1123],[1363,547],[19,2],[-5,46],[-66,225]],[[3684,1943],[2523,724]],[[6207,2667],[412,-97],[64,-49],[75,-92],[72,-371],[13,-27],[258,-334],[55,-95],[-1,-14],[42,-25],[74,-18],[251,-74],[113,-42],[214,-225]],[[3750,8129],[843,-1381],[175,-182]],[[4768,6566],[-103,-19],[-556,-199],[-133,-26],[-55,19],[-12,9],[-15,18],[-11,7],[-12,5],[-16,2],[-18,-2],[-25,-5],[-137,-168],[-454,-799]],[[3221,5408],[-201,201],[-157,44],[-524,-23]],[[2339,5630],[-220,877]],[[2119,6507],[26,1150],[58,88],[42,148],[26,7],[15,7],[12,3],[12,-3],[13,-4],[14,-10],[27,-11],[31,-8],[36,-9],[22,0],[22,5],[19,4],[20,1],[34,-3],[13,0],[15,2],[11,1],[16,-3],[25,-14],[66,5],[104,32],[188,137],[100,44],[64,14],[21,-9],[23,-5],[16,-2],[15,-9],[20,-1],[26,1],[73,50],[9,0],[5,-4],[61,-10],[256,7],[75,21]],[[5015,4476],[923,-645],[87,-74],[375,-533]],[[6400,3224],[-193,-557]],[[3684,1943],[-732,1811]],[[2952,3754],[1038,310],[88,40],[317,289]],[[4395,4393],[590,81],[30,2]],[[598,3083],[-240,639],[-208,2674]],[[150,6396],[277,-55],[738,263],[954,-97]],[[2339,5630],[-237,-242],[-65,-47],[-7,3],[-9,2],[-5,-2],[-9,-7],[-11,-7],[-7,-2],[-14,2],[-6,0],[-12,2],[-16,0],[-10,-2],[-10,-5],[-30,-14],[-7,-4],[-14,-5],[-7,-2],[-7,0],[-48,-31],[-16,-7],[-10,-4],[-8,0],[-7,-3],[-17,0],[-9,0],[-8,-2],[-20,-11],[-82,-23],[-26,-60],[-116,-384],[-62,-112],[-45,-67],[-45,-11],[-16,-9],[-12,-12],[-16,-32],[-9,-17],[-12,-7],[21,-18],[57,-25],[291,-250],[256,-838]],[[1897,3377],[-467,-275],[-39,-31],[-79,-46],[-218,-72],[-89,-41],[-3,-7],[-54,25],[-225,160],[-125,-7]],[[5989,5669],[1155,-667],[666,-291],[395,-97]],[[8205,4614],[-67,-82],[-482,-443],[-56,-43],[-297,-141],[-315,-95],[-588,-586]],[[5015,4476],[41,534],[10,47],[22,64],[38,0],[18,1],[20,4],[721,509],[104,34]],[[4768,6566],[405,-424],[816,-473]],[[4395,4393],[-374,571],[-118,107],[-61,43],[-621,294]],[[56,7590],[-56,724],[88,528],[311,447],[625,485],[795,225],[809,-333],[605,-690],[211,-346]],[[3444,8630],[-40,-19],[-2361,-581],[-814,-412],[-173,-28]],[[2373,1123],[-347,18],[-590,318],[-509,747],[-329,877]],[[1897,3377],[705,109],[62,21],[288,247]],[[3444,8630],[306,-501]],[[150,6396],[-94,1194]],[[8205,4614],[466,-115],[812,-283],[516,-494],[-138,-1174],[-1129,-882],[-883,-462]]],"aliases":{},"names":{"saint joseph":"BB-06","saint andrew":"BB-06","saint peter":"BB-09","saint lucy":"BB-07","saint james":"BB-04","saint michael":"BB-08","christ church":"BB-01","saint philip":"BB-10","saint john":"BB-05","saint george":"BB-03","saint thomas":"BB-11"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[50.4529852360236,25.579891,50.774310367036705,26.287439],"transform":{"scale":[0.000032135726673977823,0.00007076187618761868],"translate":[50.4529852360236,25.579891]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1]]],"id":"BH-13","properties":{"code":"BH-13","name":"Al Manāmah","label":[50.578,26.22]}},{"type":"MultiPolygon","arcs":[[[2,3,4]],[[5]]],"id":"BH-14","properties":{"code":"BH-14","name":"Al Janūbīyah","label":[50.562,25.969]}},{"type":"MultiPolygon","arcs":[[[6]]],"id":"BH-15","properties":{"code":"BH-15","name":"Al Muḩarraq","label":[50.638,26.261]}},{"type":"MultiPolygon","arcs":[[[7,-5,8]]],"id":"BH-16","properties":{"code":"BH-16","name":"Al Wusţá","label":[50.574,26.137]}},{"type":"MultiPolygon","arcs":[[[9,-2,-9,-4]]],"id":"BH-17","properties":{"code":"BH-17","name":"Ash Shamālīyah","label":[50.487,26.162]}}]}},"arcs":[[[1585,9357],[388,-33],[498,-93],[406,-76],[3,1],[1281,208],[205,-55],[128,-185],[229,-128],[68,-58],[68,-58],[-175,-150],[-294,-33],[-886,50],[-436,-65]],[[3068,8682],[-514,272],[-1028,37],[59,366]],[[5082,7293],[66,-1854],[-464,-1557],[-1189,-919],[-564,896],[-2699,1870],[948,886]],[[1180,6615],[1497,117],[-452,1027]],[[2225,7759],[1398,-187],[369,93],[1090,-372]],[[9999,2003],[-551,-2003],[-356,1520],[907,483]],[[4626,9878],[969,121],[701,-152],[33,-212],[0,-212],[468,-273],[402,-76],[-168,-198],[-167,-75],[-501,121],[-134,197],[-166,197],[-435,46],[-401,30],[-266,-45],[-401,303],[66,228]],[[3068,8682],[1343,-480],[316,204],[467,152],[200,-212],[201,-334],[401,-15],[-316,-161],[-18,-264],[-628,-64],[48,-215]],[[2225,7759],[-453,691],[371,149],[925,83]],[[1180,6615],[-1180,1436],[256,1069],[1329,237]]],"aliases":{},"names":{"al manāmah":"BH-13","capital":"BH-13","capital governorate":"BH-13","ash shamālīyah":"BH-17","northern":"BH-17","northern governorate":"BH-17","al janūbīyah":"BH-14","southern":"BH-14","southern governorate":"BH-14","al wusţá":"BH-16","central":"BH-16","central governorate":"BH-16","al muḩarraq":"BH-15","muharraq":"BH-15","muharraq governorate":"BH-15"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-64.885994,32.248077,-64.647631,32.388658],"transform":{"scale":[0.000023838683868386095,0.0000140595059505948],"translate":[-64.885994,32.248077]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1,2,3,4]]],"id":"BM-DEV","properties":{"code":"BM-DEV","name":"Devonshire","label":[-64.757,32.303]}},{"type":"MultiPolygon","arcs":[[[5,6]]],"id":"BM-HA","properties":{"code":"BM-HA","name":"City of Hamilton","label":[-64.788,32.294]}},{"type":"MultiPolygon","arcs":[[[7,8,9,10]],[[11,12,13,14]],[[15,16]]],"id":"BM-HAM","properties":{"code":"BM-HAM","name":"Hamilton","label":[-64.72,32.345]}},{"type":"MultiPolygon","arcs":[[[17,-4,18,19]]],"id":"BM-PAG","properties":{"code":"BM-PAG","name":"Paget","label":[-64.781,32.278]}},{"type":"MultiPolygon","arcs":[[[20,-5,21,-7]]],"id":"BM-PEM","properties":{"code":"BM-PEM","name":"Pembroke","label":[-64.8,32.3]}},{"type":"MultiPolygon","arcs":[[[22,23]]],"id":"BM-SAN","properties":{"code":"BM-SAN","name":"Sandys","label":[-64.869,32.298]}},{"type":"MultiPolygon","arcs":[[[24]]],"id":"BM-SG","properties":{"code":"BM-SG","name":"City of Saint George","label":[-64.674,32.375]}},{"type":"MultiPolygon","arcs":[[[25,-13,-11]],[[26,-9],[-25]]],"id":"BM-SGE","properties":{"code":"BM-SGE","name":"Saint George's","label":[-64.7,32.365]}},{"type":"MultiPolygon","arcs":[[[27,-15,28,-2,29,-17]]],"id":"BM-SMI","properties":{"code":"BM-SMI","name":"Smith's","label":[-64.732,32.314]}},{"type":"MultiPolygon","arcs":[[[30,31,32,-24]]],"id":"BM-SOU","properties":{"code":"BM-SOU","name":"Southampton","label":[-64.838,32.252]}},{"type":"MultiPolygon","arcs":[[[33,-20,34,-32]]],"id":"BM-WAR","properties":{"code":"BM-WAR","name":"Warwick","label":[-64.804,32.266]}}]}},"arcs":[[[4440,3936],[769,535],[445,191],[128,207]],[[5782,4869],[429,-1024]],[[6211,3845],[-112,-120],[-599,-521],[-249,-494]],[[5251,2710],[-258,725],[-506,-375]],[[4487,3060],[-47,876]],[[4261,3022],[-424,-72]],[[3837,2950],[29,485],[394,69],[1,-482]],[[7576,6258],[-325,468],[-482,-274],[-347,-360],[-77,-429],[-199,71],[-91,311],[221,764],[602,619]],[[6878,7428],[585,-67]],[[7463,7361],[130,-607],[422,-221]],[[8015,6533],[-439,-275]],[[7219,5789],[357,469]],[[7576,6258],[293,-689]],[[7869,5569],[-373,-369]],[[7496,5200],[-277,589]],[[6012,5240],[480,96]],[[6492,5336],[-354,-437],[-126,341]],[[3729,2206],[679,601],[79,253]],[[5251,2710],[-130,-256],[-812,-1076],[-211,-154]],[[4098,1224],[-369,982]],[[3837,2950],[-455,275],[-361,751],[132,255],[1287,-295]],[[4487,3060],[-226,-38]],[[245,1270],[-115,412],[-130,772],[393,-261],[-57,510],[144,683],[-220,694],[309,-165],[146,413],[195,-413],[261,-386],[-374,-469],[-118,-805],[-94,-243],[212,-248],[764,-137],[0,-221],[-602,83]],[[959,1489],[-714,-219]],[[8974,9452],[150,-477],[-357,-319],[-169,446],[376,350]],[[8015,6533],[-146,-964]],[[6878,7428],[373,842],[666,652],[888,1077],[1194,-1453],[-325,-634],[-16,-634],[-196,220],[-292,386],[-562,200],[-186,-310],[-260,469],[-699,-882]],[[6492,5336],[532,40],[195,413]],[[7496,5200],[-407,-403],[-878,-952]],[[5782,4869],[230,371]],[[959,1489],[-49,-469],[277,-551],[455,0],[585,358]],[[2227,827],[513,156],[232,-542]],[[2972,441],[-793,-441],[-634,28],[-488,27],[-651,634],[-161,581]],[[2227,827],[163,496],[390,55],[699,607],[250,221]],[[4098,1224],[-879,-645],[-247,-138]]],"aliases":{},"names":{"hamilton":"BM-HAM","hamilton parish":"BM-HAM","smith's":"BM-SMI","smith's parish":"BM-SMI","devonshire":"BM-DEV","devonshire parish":"BM-DEV","pembroke":"BM-PEM","pembroke parish":"BM-PEM","city of hamilton":"BM-HA","paget":"BM-PAG","paget parish":"BM-PAG","warwick":"BM-WAR","warwick parish":"BM-WAR","southampton":"BM-SOU","southampton parish":"BM-SOU","sandys":"BM-SAN","sandys parish":"BM-SAN","saint george's":"BM-SGE","saint george":"BM-SGE","saint george's parish":"BM-SGE","city of saint george":"BM-SG","st. george":"BM-SG"}}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[113.99879,4.016681,115.360741,5.057196],"transform":{"scale":[0.00013620872087208768,0.00010406190619061908],"translate":[113.99879,4.016681]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1]],"id":"BN-BE","properties":{"code":"BN-BE","name":"Belait","label":[114.408,4.456]}},{"type":"Polygon","arcs":[[2,3]],"id":"BN-BM","properties":{"code":"BN-BM","name":"Brunei and Muara","label":[114.92,4.902]}},{"type":"Polygon","arcs":[[4]],"id":"BN-TE","properties":{"code":"BN-TE","name":"Temburong","label":[115.191,4.602]}},{"type":"Polygon","arcs":[[5,-1,6,-3]],"id":"BN-TU","properties":{"code":"BN-TU","name":"Tutong","label":[114.671,4.645]}}]}},"arcs":[[[3639,6386],[342,-763],[304,-36],[105,-728],[876,-1120],[309,-824],[264,-190]],[[5839,2725],[170,-390],[-370,-1229],[-675,-843],[-648,-217],[-127,413],[-675,774],[-422,1106],[-1008,46],[103,498],[-375,1583],[-930,771],[-882,379],[1857,-81],[550,137],[1232,714]],[[5465,6860],[125,1616]],[[5590,8476],[1235,979],[950,514],[-257,-1156],[-469,-649],[-78,-455],[-515,-58],[-991,-791]],[[8424,8570],[449,-797],[480,-1814],[-2,-1558],[648,-1394],[-267,-144],[-761,434],[-791,217],[-569,2296],[-93,1154],[186,581],[603,482],[117,543]],[[5465,6860],[251,-1114],[35,-1123],[176,-619],[307,-92],[-84,-639],[-311,-548]],[[3639,6386],[256,121],[532,828],[1163,1141]]],"aliases":{},"names":{"temburong":"BN-TE","temburong district":"BN-TE","brunei and muara":"BN-BM","brunei-muara":"BN-BM","brunei and muara district":"BN-BM","tutong":"BN-TU","tutong district":"BN-TU","belait":"BN-BE","belait district":"BN-BE"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-89.23418626022602,15.879652,-87.78307,18.48449171647165],"transform":{"scale":[0.00014512613863646662,0.0002605100226494298],"translate":[-89.23418626022602,15.879652]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1,2,3,4]],[[5]],[[6]],[[7]],[[8]]],"id":"BZ-BZ","properties":{"code":"BZ-BZ","name":"Belize","label":[-88.419,17.537]}},{"type":"MultiPolygon","arcs":[[[9,10,-3,11,12]]],"id":"BZ-CY","properties":{"code":"BZ-CY","name":"Cayo","label":[-88.883,16.99]}},{"type":"MultiPolygon","arcs":[[[-5,13,14]]],"id":"BZ-CZL","properties":{"code":"BZ-CZL","name":"Corozal","label":[-88.349,18.167]}},{"type":"MultiPolygon","arcs":[[[15,-14,-4,-11]]],"id":"BZ-OW","properties":{"code":"BZ-OW","name":"Orange Walk","label":[-88.855,17.712]}},{"type":"MultiPolygon","arcs":[[[16,17,-12,-2]]],"id":"BZ-SC","properties":{"code":"BZ-SC","name":"Stann Creek","label":[-88.496,16.809]}},{"type":"MultiPolygon","arcs":[[[-13,-18,18]]],"id":"BZ-TOL","properties":{"code":"BZ-TOL","name":"Toledo","label":[-88.812,16.376]}}]}},"arcs":[[[7355,7985],[-287,-299],[-41,-418],[-475,-740],[800,-262],[-438,-59],[-383,-603],[-98,-620],[77,-162]],[[6510,4822],[-331,-103],[-1826,-18]],[[4353,4701],[372,268],[-149,230],[-60,907],[-345,-62]],[[4171,6044],[-80,624],[167,200],[328,75],[65,769],[365,111],[1528,210]],[[6544,8033],[811,-48]],[[9618,5792],[283,-213],[-188,-156],[-95,369]],[[9692,6394],[307,-128],[-432,-374],[-374,-87],[-281,-382],[216,514],[727,252],[-163,205]],[[7399,6922],[615,197],[-225,-201],[-390,4]],[[9101,8239],[-11,-166],[-350,-265],[420,919],[385,-74],[-444,-414]],[[282,1969],[304,2471],[-11,1093]],[[575,5533],[494,193],[1083,51],[305,113],[744,-12],[970,166]],[[4353,4701],[243,-227],[-661,-511],[-71,-378],[-565,-238],[-129,-271]],[[3170,3076],[-311,-259],[-826,-274],[-120,-111],[-485,-1],[-826,-216],[-320,-246]],[[6544,8033],[-946,457],[-501,363],[-824,235]],[[4273,9088],[408,403],[151,350],[301,158],[1238,-8],[158,-75],[-669,-299],[456,-39],[-298,-256],[701,204],[444,-79],[711,119],[-346,-181],[345,15],[1,-806],[-519,-609]],[[575,5533],[-73,2228],[198,264],[573,112],[1165,-425],[992,627],[274,484],[569,265]],[[6510,4822],[454,-432],[42,-260],[-454,-247],[214,-303],[-340,-156],[-32,-456],[-599,-388],[21,-207]],[[5816,2373],[-1163,81],[65,185],[-335,384],[-497,94],[-716,-41]],[[5816,2373],[-397,-395],[-193,-52],[-415,-411],[-788,43],[-75,-116],[-544,-97],[-39,-288],[-1253,-657],[95,-345],[-262,-55],[-609,84],[-945,-5],[-350,-74],[-41,284],[282,1680]]],"aliases":{},"names":{"cayo":"BZ-CY","cayo district":"BZ-CY","orange walk":"BZ-OW","orange walk district":"BZ-OW","corozal":"BZ-CZL","corozal district":"BZ-CZL","toledo":"BZ-TOL","toledo district":"BZ-TOL","stann creek":"BZ-SC","stann creek district":"BZ-SC","belize":"BZ-BZ","belize district":"BZ-BZ"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-85.94650160486049,8.101943827182719,-82.57376968046805,11.209937],"transform":{"scale":[0.00033730692313155714,0.0003108304003217603],"translate":[-85.94650160486049,8.101943827182719]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1,2,3,4]]],"id":"CR-A","properties":{"code":"CR-A","name":"Alajuela","label":[-84.799,10.753]}},{"type":"MultiPolygon","arcs":[[[5,6]]],"id":"CR-C","properties":{"code":"CR-C","name":"Cartago","label":[-83.699,9.784]}},{"type":"MultiPolygon","arcs":[[[-4,7,8,9,10]]],"id":"CR-G","properties":{"code":"CR-G","name":"Guanacaste","label":[-85.355,10.333]}},{"type":"MultiPolygon","arcs":[[[11,12,-1,13]]],"id":"CR-H","properties":{"code":"CR-H","name":"Heredia","label":[-83.953,10.463]}},{"type":"MultiPolygon","arcs":[[[14,15,-6,16,-12,17]]],"id":"CR-L","properties":{"code":"CR-L","name":"Limón","label":[-83.149,9.702]}},{"type":"MultiPolygon","arcs":[[[18,-8,-3,19,-15]],[[20,-10]],[[21]]],"id":"CR-P","properties":{"code":"CR-P","name":"Puntarenas","label":[-83.229,8.934]}},{"type":"MultiPolygon","arcs":[[[-17,-7,-16,-20,-2,-13]]],"id":"CR-SJ","properties":{"code":"CR-SJ","name":"San José","label":[-84.01,9.604]}}]}},"arcs":[[[5303,8643],[-24,-2026],[-35,-497],[-66,-152]],[[5178,5968],[-377,-147],[-397,19],[-327,-299]],[[4077,5541],[-305,194],[90,158],[277,207],[-128,49],[-165,251],[-58,292],[60,187],[-104,124],[-195,-38],[-111,155]],[[3438,7120],[68,409],[-87,176],[-538,344],[-125,286],[-482,240],[-306,-25],[-99,198],[-225,109],[-136,167],[597,471]],[[2105,9495],[904,-359],[138,20],[525,370],[93,24],[767,-372],[185,128],[133,-241],[303,-189],[-55,-134],[205,-99]],[[5941,6545],[1048,-511],[741,-13],[10,-491],[-216,-286],[-91,-222],[-212,-311],[72,-317]],[[7293,4394],[-451,328],[-229,-101],[-140,185],[-145,9],[-110,193],[-191,46],[-221,306],[-238,-39],[-1,106],[196,251],[8,204],[281,49],[116,105],[-19,154],[-208,355]],[[3438,7120],[-235,-33],[-23,-405],[-132,-212],[-196,141],[-250,-4]],[[2602,6607],[-317,32],[-232,243],[82,-487],[174,-226]],[[2309,6169],[-47,-131],[-316,-107],[339,-295],[-34,-247],[-125,-169]],[[2126,5220],[-306,328],[-318,123],[-91,-46],[-498,178],[-415,561],[-194,485],[17,260],[-109,140],[294,297],[-100,152],[82,155],[239,63],[206,188],[-182,-55],[90,242],[-8,321],[-721,346],[-112,15],[350,187],[350,-68],[47,136],[-153,163],[232,60],[-101,132],[210,372],[99,44],[264,-185],[807,-319]],[[6472,8595],[175,-278],[-202,-26],[-161,-128],[11,-497],[-139,-232],[81,-255],[-71,-273],[-139,-159]],[[6027,6747],[-200,-118],[-268,-34],[183,-376],[-54,-170],[-366,-118],[-144,37]],[[5303,8643],[241,-79],[159,75],[265,-223],[219,13],[285,166]],[[8915,3117],[-350,300],[-65,235],[-255,256],[-185,4],[-277,154],[-211,-123],[-109,84]],[[7463,4027],[-170,367]],[[5941,6545],[86,202]],[[6472,8595],[298,109],[-77,410],[132,-29],[135,-222],[48,-260],[-122,262],[45,-359],[98,71],[324,-879],[212,-382],[566,-793],[351,-410],[176,9],[227,-442],[343,-412],[75,26],[107,-279],[405,-84],[184,-189],[-134,-288],[-175,37],[-100,152],[-347,185],[-150,-137],[54,-244],[-214,-44],[-32,-108],[14,-1178]],[[8915,3117],[89,3],[475,-315],[88,-168],[-131,-134],[-307,-136],[-166,-262],[218,-406],[40,-323],[-101,-295],[-247,-115],[-296,-281],[325,-214],[171,-471],[-245,383],[-540,476],[153,200],[-195,427],[123,81],[-142,155],[-204,-44],[-233,149],[-38,168],[-154,57],[-291,-88],[229,-397],[347,-152],[0,-532],[-519,213],[-335,7],[-487,575],[213,191],[238,491],[-126,310],[80,111],[-78,222],[-466,528],[-157,68],[-232,238],[-265,145],[-508,187],[-88,216],[-740,196],[-189,-20],[-281,188],[-183,235],[129,425],[-189,275],[-104,317],[-348,0],[186,66],[-325,90],[-507,450]],[[4077,5541],[72,-209],[-126,-22],[30,-242],[158,-348],[290,-22],[100,220],[748,-61],[46,-172],[151,-20],[14,-154],[374,-341],[107,13],[186,-295],[74,-229],[314,-27],[175,-297],[419,-233],[97,92],[12,287],[-65,213],[210,333]],[[2309,6169],[454,-240],[164,6],[143,-132],[-65,-222],[202,-44],[-280,-308],[-307,-199],[-163,-354],[-331,544]],[[2416,6424],[-81,-73],[-119,134],[200,-61]]],"aliases":{},"names":{"alajuela":"CR-A","provincia de alajuela":"CR-A","guanacaste":"CR-G","provincia de guanacaste":"CR-G","limón":"CR-L","provincia de limon":"CR-L","puntarenas":"CR-P","provincia de puntarenas":"CR-P","heredia":"CR-H","provincia de heredia":"CR-H","san josé":"CR-SJ","provincia de san jose":"CR-SJ","cartago":"CR-C","provincia de cartago":"CR-C"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[32.28654236363636,34.63443983968397,34.099132,35.197737119811976],"transform":{"scale":[0.00018127709134549855,0.00005633536154895537],"translate":[32.28654236363636,34.63443983968397]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1,2,3]]],"id":"CY-01","properties":{"code":"CY-01","name":"Nicosia","label":[33.043,35.021]}},{"type":"MultiPolygon","arcs":[[[4,-2,5,6]]],"id":"CY-02","properties":{"code":"CY-02","name":"Limassol","label":[32.967,34.776]}},{"type":"MultiPolygon","arcs":[[[-6,-1,7]],[[8,9]]],"id":"CY-03","properties":{"code":"CY-03","name":"Larnaca","label":[33.868,34.973]}},{"type":"MultiPolygon","arcs":[[[-9,10]]],"id":"CY-04","properties":{"code":"CY-04","name":"Famagusta","label":[33.949,35.016]}},{"type":"MultiPolygon","arcs":[[[11,-3,-5]]],"id":"CY-05","properties":{"code":"CY-05","name":"Paphos","label":[32.528,34.931]}}]}},"arcs":[[[6644,7369],[164,-879],[-135,-625],[-442,18],[-109,-678],[-423,85],[-307,-707],[-461,320],[-274,-549]],[[4657,4354],[-411,190],[-405,1163],[-613,-387],[-367,359],[-209,-920]],[[2652,4759],[-421,-233],[-164,418],[64,1219],[-358,1429],[120,777],[-348,1185]],[[1545,9554],[799,159],[111,-683],[349,-290],[245,-832],[460,334],[955,1260],[521,358],[690,-497],[469,636],[422,-1827],[78,-803]],[[1959,469],[-23,592],[377,839],[-25,639],[451,1753],[-87,467]],[[4657,4354],[439,-743],[341,-1148],[2,-1169]],[[5439,1294],[-1105,-365],[-312,-929],[-155,691],[-568,-120],[-289,610],[-395,-848],[-656,136]],[[6644,7369],[274,416],[280,-694],[486,0],[199,-650],[-339,-933],[-267,-2183],[-1296,-1613],[-542,-418]],[[8511,6510],[369,-696]],[[8880,5814],[-321,31],[-48,665]],[[8511,6510],[-167,663],[447,612],[729,-164],[479,-1751],[-483,399],[-636,-455]],[[1959,469],[-818,824],[-441,820],[-189,1697],[-274,663],[-237,3741],[473,-989],[428,308],[429,1744],[215,277]]],"aliases":{},"names":{"larnaca":"CY-03","larnaca district":"CY-03","famagusta":"CY-04","nicosia":"CY-01","eparchia lefkosias":"CY-01","limassol":"CY-02","eparchia lemesou":"CY-02","paphos":"CY-05","eparchia pafou":"CY-05"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[41.74911,10.929825,43.418712,12.707913],"transform":{"scale":[0.00016697689768976873,0.00017782658265826586],"translate":[41.74911,10.929825]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2,3,4,5]],"id":"DJ-AR","properties":{"code":"DJ-AR","name":"Arta","label":[42.803,11.446]}},{"type":"Polygon","arcs":[[6,7,-1]],"id":"DJ-AS","properties":{"code":"DJ-AS","name":"Ali Sabieh","label":[42.899,11.211]}},{"type":"Polygon","arcs":[[8,-2,-8,9]],"id":"DJ-DI","properties":{"code":"DJ-DI","name":"Dikhil","label":[42.228,11.388]}},{"type":"Polygon","arcs":[[10,-5]],"id":"DJ-DJ","properties":{"code":"DJ-DJ","name":"Djibouti","label":[43.037,11.552]}},{"type":"Polygon","arcs":[[11,12]],"id":"DJ-OB","properties":{"code":"DJ-OB","name":"Obock","label":[43.046,12.284]}},{"type":"Polygon","arcs":[[13,-12,14,-3,-9]],"id":"DJ-TA","properties":{"code":"DJ-TA","name":"Tadjourah","label":[42.504,11.797]}}]}},"arcs":[[[8622,2688],[-780,398],[-875,-162],[-287,-486],[-946,-550]],[[5734,1888],[-1242,202],[-490,670],[-311,1314]],[[3691,4074],[303,-307],[697,-294]],[[4691,3473],[38,-250],[316,-171],[575,139],[-133,103],[446,269],[964,116]],[[6897,3679],[333,-334],[564,-32],[734,128]],[[8528,3441],[405,-303],[-311,-450]],[[8622,2688],[-1587,-2300],[-303,-116],[-607,103],[-253,385],[-727,136]],[[5145,896],[106,734],[483,258]],[[1351,5348],[1317,-323],[1023,-951]],[[5145,896],[-770,-169],[-526,-299],[-319,54],[-813,-174],[-799,-17],[-186,-175],[-504,-52],[-931,166],[-224,143],[145,401],[15,1081],[-233,1565],[354,1115],[768,513],[229,300]],[[6897,3679],[1540,97],[91,-335]],[[8006,5112],[-431,639],[-1236,1267],[-568,330],[-216,351],[117,457]],[[5672,8156],[575,275],[-42,496],[501,612],[189,-55],[1301,515],[992,-1189],[276,-43],[267,-1101],[224,-288],[44,-847],[-249,-486],[-532,-210],[-490,-54],[-722,-669]],[[1351,5348],[737,1065],[1144,1410],[182,368],[669,753],[543,-135],[938,-766],[108,113]],[[8006,5112],[-266,-227],[-868,-101],[-558,-183],[-295,-225],[-420,-707],[-282,-128],[-659,129],[33,-197]]],"aliases":{},"names":{"ali sabieh":"DJ-AS","ali sabieh region":"DJ-AS","arta":"DJ-AR","arta region":"DJ-AR","dikhil":"DJ-DI","tadjourah":"DJ-TA","obock":"DJ-OB","djibouti":"DJ-DJ","djibouti region":"DJ-DJ"}}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-61.488922,15.201809,-61.249257,15.633857],"transform":{"scale":[0.000023968896889689194,0.00004320912091209121],"translate":[-61.488922,15.201809]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2,3,4]],"id":"DM-02","properties":{"code":"DM-02","name":"Saint Andrew","label":[-61.359,15.538]}},{"type":"Polygon","arcs":[[5,6,7,8,9,-2]],"id":"DM-03","properties":{"code":"DM-03","name":"Saint David","label":[-61.292,15.396]}},{"type":"Polygon","arcs":[[10,11,-8,12,13]],"id":"DM-04","properties":{"code":"DM-04","name":"Saint George","label":[-61.349,15.3]}},{"type":"Polygon","arcs":[[14,-5,15]],"id":"DM-05","properties":{"code":"DM-05","name":"Saint John","label":[-61.448,15.577]}},{"type":"Polygon","arcs":[[16,17,-3,-10,18]],"id":"DM-06","properties":{"code":"DM-06","name":"Saint Joseph","label":[-61.386,15.436]}},{"type":"Polygon","arcs":[[19,-14,20,21]],"id":"DM-07","properties":{"code":"DM-07","name":"Saint Luke","label":[-61.364,15.248]}},{"type":"Polygon","arcs":[[22,-22,23]],"id":"DM-08","properties":{"code":"DM-08","name":"Saint Mark","label":[-61.361,15.222]}},{"type":"Polygon","arcs":[[24,-24,-21,-13,-7]],"id":"DM-09","properties":{"code":"DM-09","name":"Saint Patrick","label":[-61.305,15.277]}},{"type":"Polygon","arcs":[[25,-19,-9,-12]],"id":"DM-10","properties":{"code":"DM-10","name":"Saint Paul","label":[-61.372,15.353]}},{"type":"Polygon","arcs":[[26,-16,-4,-18]],"id":"DM-11","properties":{"code":"DM-11","name":"Saint Peter","label":[-61.445,15.499]}}]}},"arcs":[[[1657,9981],[2694,-1035],[3210,-211],[1630,-1604]],[[9191,7131],[-230,-157],[-68,-86],[16,-33],[31,-470],[-11,-45],[-16,-3],[-26,-11],[-151,-83],[-43,-19],[-24,-7],[-23,-4],[-19,-2],[-68,0],[-14,-2],[-16,-4],[-106,-48],[-71,-42],[-36,-17],[-29,-9],[-14,0],[-28,3],[-16,1],[-30,-4],[-12,-1],[-14,3],[-18,4],[-16,5],[-191,-72],[-790,-403]],[[7158,5625],[-230,312],[-636,275],[-245,73],[-161,37],[-127,7],[-132,15],[-152,28],[-435,103],[-51,6],[-46,-5],[-171,-46],[-168,-29],[-246,-14],[-200,48],[-150,52],[-535,286]],[[3473,6773],[-85,198]],[[3388,6971],[-101,454],[-54,114],[-273,121],[-37,137],[25,670],[-40,250],[-19,5],[-32,25],[-11,4],[-14,3],[-13,2],[-15,-2],[-14,-1],[-19,-2],[-39,1],[-22,2],[-75,17],[-16,6],[-16,9],[-19,21],[-11,10],[-151,32],[-281,151],[-112,103],[-372,878]],[[9191,7131],[61,-49],[396,-815],[69,-848],[-288,-649],[190,-37],[173,-77],[207,-44],[-261,-845],[61,-738]],[[9799,3029],[-1766,-166],[-159,-45],[-335,-160]],[[7539,2658],[65,135],[2,25],[-9,44],[-19,38],[-33,47],[-8,17],[-2,16],[17,39],[-139,68],[-681,239]],[[6732,3326],[5,978],[-297,397]],[[6440,4701],[718,924]],[[4829,1355],[-4,140],[20,99],[-229,263],[-528,330],[-95,277],[-16,122]],[[3977,2586],[486,-15],[13,0],[74,-2],[25,1],[79,12],[22,0],[18,0],[19,-3],[17,-2],[29,4],[32,5],[62,29],[39,36],[92,163],[8,10],[6,8],[13,17],[1721,477]],[[7539,2658],[150,-59],[19,-13],[-6,-35],[-512,-459],[-120,-62],[-167,-63],[-386,-109],[-415,-179],[-582,-476]],[[5520,1203],[-691,152]],[[205,7586],[-35,17],[-170,125],[2,216],[203,151],[698,264],[214,231],[-289,30],[-505,99],[-321,16],[344,272],[486,992],[825,-18]],[[3388,6971],[-2316,666],[-15,-4],[-27,-2],[-32,2],[-178,18],[-18,0],[-218,30],[-34,3],[-39,-4],[-26,0],[-93,12],[-32,-10],[-17,-8],[-138,-88]],[[3042,4359],[-86,366],[-131,203],[-959,511],[-153,192],[-80,221],[-187,235],[-44,35]],[[1402,6122],[674,305],[254,138],[41,39],[60,40],[284,74],[758,55]],[[6440,4701],[-1396,-352],[-130,-9],[-181,14],[-712,92],[-515,-7],[-464,-80]],[[4845,819],[-16,536]],[[5520,1203],[40,-231]],[[5560,972],[-717,-183],[2,30]],[[5947,229],[-182,-152],[-506,-77],[-468,70],[-85,185],[139,564]],[[5560,972],[387,-743]],[[9799,3029],[13,-185],[-122,-911],[-857,-812],[-301,-126],[-203,-28],[-163,7],[-425,-30],[-355,45],[-263,-27],[-226,-111],[-213,-301],[-583,-137],[-136,-169],[-18,-15]],[[3977,2586],[-59,452],[-190,418],[-591,681],[-81,164],[-14,58]],[[1402,6122],[-197,152],[-231,75],[-271,121],[-92,280],[-39,562],[-173,178],[-194,96]]],"aliases":{},"names":{"saint patrick":"DM-09","saint david":"DM-03","saint andrew":"DM-02","saint john":"DM-05","saint peter":"DM-11","saint joseph":"DM-06","saint paul":"DM-10","saint george":"DM-04","saint luke":"DM-07","saint mark":"DM-08"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[36.423647,12.360022,43.123871,18.004828],"transform":{"scale":[0.000670089408940894,0.0005645370537053705],"translate":[36.423647,12.360022]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2,3]],"id":"ER-AN","properties":{"code":"ER-AN","name":"Anseba","label":[39.056,15.279]}},{"type":"Polygon","arcs":[[4,5]],"id":"ER-DK","properties":{"code":"ER-DK","name":"Debubawi Keyih Bahri","label":[41.739,13.571]}},{"type":"MultiPolygon","arcs":[[[-5,6,7,-3,8,9]],[[10]],[[11]]],"id":"ER-DU","properties":{"code":"ER-DU","name":"Debub","label":[40.169,15.618]}},{"type":"Polygon","arcs":[[12,-1,13,14]],"id":"ER-GB","properties":{"code":"ER-GB","name":"Gash Barka","label":[37.606,15.03]}},{"type":"Polygon","arcs":[[-14,-4,-8,15]],"id":"ER-MA","properties":{"code":"ER-MA","name":"Maekel","label":[38.968,14.858]}},{"type":"Polygon","arcs":[[-9,-2,-13,16]],"id":"ER-SK","properties":{"code":"ER-SK","name":"Semenawi Keyih Bahri","label":[37.918,16.626]}}]}},"arcs":[[[3529,4952],[-3,711]],[[3526,5663],[257,33]],[[3783,5696],[62,-158],[136,-241],[159,-183],[192,-126]],[[4332,4988],[-63,-36],[-740,0]],[[5865,3499],[123,240],[75,190],[133,219],[26,204],[-31,238]],[[6191,4590],[66,-5],[17,-100],[158,-131],[-15,-108],[64,-109],[176,34],[90,-66],[315,-64],[79,-130],[146,-140],[44,-154],[253,-334],[54,-155],[118,-110],[83,-219],[193,-19],[53,-87],[94,-1],[114,-74],[203,-349],[81,-26],[21,-72],[51,97],[103,-117],[10,-115],[71,-162],[55,-346],[77,-43],[114,15],[20,51],[201,-289],[123,-97],[54,-276],[92,5],[50,-77],[140,-19],[57,159],[127,-141],[47,-200],[-325,-162],[-47,17],[-124,-193],[10,-156],[-170,-122],[-234,241],[-135,43],[-77,-96],[-193,280],[-97,193],[-71,71],[-285,182],[-78,127],[-116,292],[-156,240],[-271,258],[-460,342],[-45,52],[-221,454],[-314,414],[-193,119],[-177,38],[-245,140],[-101,109]],[[5865,3499],[-114,123],[-258,108],[-186,-12],[-102,-63],[-216,144],[-65,-5]],[[4924,3794],[-514,1022],[-78,172]],[[3783,5696],[-51,217],[-172,302],[-202,29],[-213,115],[-49,106],[1,188],[34,160],[-70,202],[-107,130],[-37,140],[-97,202],[-339,70],[-80,102],[16,261],[-75,110],[20,202],[25,727],[-13,215]],[[2374,9174],[71,16],[22,-121],[125,137],[73,-62],[61,35],[9,107],[130,95],[132,347],[41,69],[212,202],[141,-351],[140,-244],[104,-276],[104,-205],[53,-186],[-18,-69],[69,-102],[85,-286],[20,-143],[88,-281],[53,-296],[-28,-33],[48,-156],[30,-394],[57,-337],[117,-380],[169,-130],[-5,-94],[81,-189],[-71,-102],[41,-139],[193,12],[36,-56],[78,-290],[82,-128],[-30,-249],[112,-93],[57,14],[67,202],[-58,157],[-42,-43],[-21,258],[149,179],[194,-218],[100,-53],[3,-77],[-71,-124],[73,-114],[33,-172],[61,-31],[31,-135],[190,-122],[90,54],[64,105],[42,-60],[54,62],[67,-75],[42,62],[67,-81]],[[5914,5812],[41,-76],[-189,-45],[-246,73],[-92,-39],[-145,61],[12,61],[163,0],[-63,103],[-162,36],[-8,67],[103,1],[-28,67],[87,19],[-11,100],[98,-58],[-5,-102],[72,24],[-5,-168],[46,-118],[102,-31],[18,133],[115,-24],[97,-84]],[[5479,6532],[2,-118],[-159,65],[86,81],[-6,70],[101,-51],[-24,-47]],[[752,7219],[71,-57],[61,-273],[80,-131],[271,-97],[150,-131],[127,-221],[97,-40],[74,-94],[297,4],[126,-83],[126,-16],[134,-61],[146,-113],[65,-7],[191,73],[120,-70],[131,-210],[81,-41],[273,-9],[153,21]],[[3529,4952],[-100,13],[-442,-121],[-132,-192],[-130,-99],[-55,-167],[60,-212],[-11,-113]],[[2719,4061],[-24,48],[-171,2],[-162,77],[-172,275],[-487,-1351],[-133,147],[-34,108],[-82,72],[-6,86],[-149,190],[-164,-14],[-94,-51],[-72,-240],[-100,-39],[-88,74],[-297,48],[-272,-69],[-59,-52],[-153,1502],[14,70],[137,171],[76,258],[78,93],[-17,152],[81,339],[132,203],[189,615],[87,121],[-25,323]],[[4924,3794],[-185,17],[-126,100],[-38,-77],[-183,-74],[-146,7],[-91,-82],[-74,212],[-124,137],[-117,3],[-4,-95],[-191,-163],[-212,-60],[-83,15],[-92,-56],[-269,-34],[-151,155],[-119,262]],[[752,7219],[-75,163],[36,227],[88,89],[37,106],[11,380],[-28,115],[40,49],[199,-100],[148,15],[114,54],[140,-45],[84,115],[62,178],[8,218],[319,116],[117,165],[109,-70],[213,180]]],"aliases":{},"names":{"debubawi keyih bahri":"ER-DK","southern red sea":"ER-DK","southern red sea region":"ER-DK","debub":"ER-DU","northern red sea region":"ER-DU","semenawi keyih bahri":"ER-SK","northern red sea":"ER-SK","anseba region":"ER-SK","gash barka":"ER-GB","gash-barka":"ER-GB","gash-barka region":"ER-GB","maekel":"ER-MA","debub region":"ER-MA","anseba":"ER-AN","maekel region":"ER-AN"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-180,-19.157148843484347,180,-12.475274],"transform":{"scale":[0.036003600360036005,0.0006682543097794126],"translate":[-180,-19.157148843484347]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0,1]]],"id":"FJ-C","properties":{"code":"FJ-C","name":"Central","label":[178.132,-18.382]}},{"type":"MultiPolygon","arcs":[[[2]],[[3]],[[4]],[[5]],[[6]],[[7]],[[8]],[[9]],[[10]],[[11]],[[12]]],"id":"FJ-E","properties":{"code":"FJ-E","name":"Eastern","label":[-178.72,-20.666]}},{"type":"MultiPolygon","arcs":[[[13]],[[14]],[[15]],[[16]],[[17]]],"id":"FJ-N","properties":{"code":"FJ-N","name":"Northern","label":[178.308,-16.811]}},{"type":"MultiPolygon","arcs":[[[18]]],"id":"FJ-R","properties":{"code":"FJ-R","name":"Rotuma","label":[177.067,-12.496]}},{"type":"MultiPolygon","arcs":[[[19]],[[20,-2]],[[21]],[[22]],[[23]]],"id":"FJ-W","properties":{"code":"FJ-W","name":"Western","label":[174.61,-21.688]}}]}},"arcs":[[[9956,2405],[3,-102],[1,-85],[-1,-120],[2,-148],[-1,-65],[1,-110],[1,-61],[1,29],[-1,-144],[-3,-61],[-1,58],[-2,-84],[-1,68],[-4,-68],[-3,-152],[-1,14],[-2,-39],[-5,-1],[-3,63]],[[9937,1397],[2,151],[-2,97],[1,109],[4,-23],[2,70],[-1,135],[0,83],[3,146],[0,33],[-2,-69],[1,244],[2,-145],[4,19],[0,50],[4,-63],[1,171]],[[4,286],[0,64],[2,-49],[-2,-15]],[[34,1446],[0,-82],[-2,42],[2,67],[0,-27]],[[47,1907],[2,-77],[-2,-44],[-1,27],[0,89],[1,5]],[[29,2963],[1,-56],[-2,-120],[0,18],[1,158]],[[9955,303],[2,-15],[0,-72],[-2,-53],[-2,0],[-1,76],[-3,-75],[-1,-102],[-2,-58],[-2,-4],[-2,41],[2,41],[1,62],[2,-14],[1,52],[1,88],[4,77],[2,-44]],[[9958,420],[0,-74],[-1,29],[1,45]],[[9997,898],[-1,-152],[-2,104],[3,48]],[[9981,1733],[0,-179],[-1,44],[-1,135],[-1,-61],[0,143],[2,-4],[1,-78]],[[9983,2040],[0,-84],[-1,105],[1,-21]],[[9965,2288],[1,-2],[0,-149],[-1,-28],[-1,111],[1,68]],[[9984,2793],[-1,-102],[-1,-47],[0,210],[1,13],[1,-74]],[[9953,3537],[-1,-61],[-1,71],[2,-10]],[[0,3547],[2,138],[2,3],[1,-130],[-1,-126],[-4,-102],[0,-49],[0,266]],[[0,3992],[3,90],[-1,-103],[-2,-65],[0,78]],[[9999,3284],[-2,-54],[-1,89],[2,121],[1,110],[0,-266]],[[9999,4471],[-4,-182],[-3,-252],[-4,-120],[-1,-128],[-2,-123],[0,-87],[1,0],[5,174],[0,67],[4,143],[2,68],[1,-54],[-3,-244],[1,-20],[1,-115],[-3,52],[-3,0],[-4,-110],[-8,-22],[2,112],[-2,62],[-3,-48],[-1,-101],[-2,-14],[-1,-185],[-4,96],[-2,-66],[0,-112],[-3,-32],[-1,150],[-2,178],[-2,-51],[-1,30],[1,94],[1,40],[0,133],[4,-78],[4,182],[2,49],[0,64],[1,0],[8,108],[4,-16],[0,71],[2,62],[0,68],[3,3],[0,72],[1,-42],[3,44],[1,63],[1,-75],[2,0],[2,76],[2,16]],[[9919,9982],[0,-23],[0,-14],[-1,-7],[0,-1],[-1,7],[0,5],[-1,2],[0,-3],[0,22],[0,8],[0,-7],[1,5],[0,23],[1,-8],[1,0],[0,-9]],[[9934,994],[0,-143],[-1,115],[1,28]],[[9937,1397],[-5,85],[-3,14],[-4,77],[-1,52],[-1,177],[1,151],[2,43],[0,111],[2,9],[-2,154],[3,78],[1,125],[9,207],[-1,-76],[2,42],[3,-11],[2,59],[3,15],[1,69],[2,-52],[0,-72],[-1,-90],[3,6],[1,-88],[2,-77]],[[9918,2861],[2,0],[-2,-83],[0,83]],[[9923,3107],[0,-48],[-2,-64],[0,53],[1,95],[1,-36]],[[9931,3711],[1,-34],[-3,-201],[-1,0],[3,169],[0,66]]],"aliases":{},"names":{"eastern":"FJ-E","eastern division":"FJ-E","western":"FJ-W","western division":"FJ-W","central":"FJ-C","central division":"FJ-C","northern":"FJ-N","northern division":"FJ-N","rotuma":"FJ-R"}}
|
||||
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[138.063813,5.269358806480648,163.04656,9.588669704770476],"transform":{"scale":[0.0024985245524552445,0.00043197428725770857],"translate":[138.063813,5.269358806480648]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"MultiPolygon","arcs":[[[0]]],"id":"FM-KSA","properties":{"code":"FM-KSA","name":"Kosrae","label":[162.927,5.356]}},{"type":"MultiPolygon","arcs":[[[1]],[[2]]],"id":"FM-PNI","properties":{"code":"FM-PNI","name":"Pohnpei","label":[158.163,6.866]}},{"type":"MultiPolygon","arcs":[[[3]]],"id":"FM-TRK","properties":{"code":"FM-TRK","name":"Chuuk","label":[151.839,7.44]}},{"type":"MultiPolygon","arcs":[[[4]]],"id":"FM-YAP","properties":{"code":"FM-YAP","name":"Yap","label":[138.114,9.581]}}]}},"arcs":[[[9991,166],[2,-12],[2,-4],[3,-6],[1,-19],[-1,-29],[-7,-71],[-3,-17],[-5,-8],[-5,8],[-5,10],[-6,7],[-8,0],[-6,4],[-6,12],[-2,39],[5,39],[19,94],[8,31],[8,12],[6,-26],[0,-15],[0,-35],[0,-14]],[[8096,3863],[13,-16],[6,-144],[-19,-172],[-39,-2],[-14,80],[0,144],[-13,39],[22,166],[34,-29],[10,-66]],[[6498,6726],[-1,-160],[-23,86],[24,74]],[[5413,4895],[14,18],[5,-10],[6,-41],[0,-35],[-3,-43],[-4,-18],[-4,35],[-3,0],[-3,-17],[-5,2],[-5,8],[-6,7],[1,8],[1,2],[3,4],[4,10],[11,12],[2,17],[-2,22],[-9,-8],[-3,27]],[[51,9970],[-4,-41],[1,-33],[6,-12],[8,23],[-2,-27],[-5,-37],[-5,-33],[-4,-16],[-6,10],[-3,49],[-5,-10],[-4,-22],[-22,-199],[-4,-31],[-2,55],[4,78],[6,72],[7,31],[3,16],[3,78],[2,25],[6,20],[2,-16],[2,-62],[0,23],[1,22],[2,21],[2,16],[4,15],[4,14],[4,0],[-1,-29]]],"aliases":{},"names":{"kosrae":"FM-KSA","state of kosrae":"FM-KSA","pohnpei":"FM-PNI","pohnpei state":"FM-PNI","state of pohnpei":"FM-PNI","chuuk":"FM-TRK","chuuk state":"FM-TRK","state of chuuk":"FM-TRK","yap":"FM-YAP","yap state":"FM-YAP","state of yap":"FM-YAP"}}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"type":"Topology","bbox":[-61.790517,12.002834,-61.42162,12.529731],"transform":{"scale":[0.000036893389338934296,0.00005269496949694969],"translate":[-61.790517,12.002834]},"objects":{"subdivisions":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0,1,2,3,4,5]],"id":"GD-01","properties":{"code":"GD-01","name":"Saint Andrew","label":[-61.633,12.122]}},{"type":"Polygon","arcs":[[6,7,-2]],"id":"GD-02","properties":{"code":"GD-02","name":"Saint David","label":[-61.662,12.055]}},{"type":"Polygon","arcs":[[8,9,-3,-8]],"id":"GD-03","properties":{"code":"GD-03","name":"Saint George","label":[-61.723,12.068]}},{"type":"Polygon","arcs":[[10,11,-4,-10]],"id":"GD-04","properties":{"code":"GD-04","name":"Saint John","label":[-61.709,12.145]}},{"type":"Polygon","arcs":[[12,13,-5,-12]],"id":"GD-05","properties":{"code":"GD-05","name":"Saint Mark","label":[-61.675,12.189]}},{"type":"Polygon","arcs":[[14,-6,-14]],"id":"GD-06","properties":{"code":"GD-06","name":"Saint Patrick","label":[-61.634,12.215]}},{"type":"Polygon","arcs":[[15]],"id":"GD-10","properties":{"code":"GD-10","name":"Carriacou and Petite Martinique","label":[-61.446,12.473]}}]}},"arcs":[[[5077,3299],[-84,-814],[-65,-133],[-119,-131],[-181,-291],[9,-148],[152,-310],[-69,-59],[-141,-45],[-79,-112]],[[4500,1256],[-496,264],[-423,125],[-491,-94]],[[3090,1551],[-134,125],[-133,375]],[[2823,2051],[-45,203],[312,265],[379,437]],[[3469,2956],[178,390]],[[3647,3346],[669,0],[761,-47]],[[4500,1256],[-33,-148],[-9,-155],[-150,-170],[-1161,-419],[0,142],[-232,-78]],[[2915,428],[-159,77],[-201,219],[0,328],[178,421],[357,78]],[[2915,428],[-173,-107],[-110,-142],[-41,-179],[-253,32],[-244,52],[-230,72],[-214,90],[-280,-169],[-460,-66],[-504,18],[-406,76],[940,415],[172,116],[96,277],[-79,222],[-133,219],[-72,259],[146,422]],[[1070,2035],[950,0],[803,16]],[[1070,2035],[562,745],[18,329],[393,328]],[[2043,3437],[512,-91],[513,-171],[401,-219]],[[2043,3437],[895,747]],[[2938,4184],[353,-182],[267,-266],[89,-390]],[[2938,4184],[289,242],[568,70],[619,-57],[587,-254],[-18,-58],[-170,-112],[227,-322],[49,-275],[-12,-119]],[[9825,8574],[-478,98],[-458,-88],[-452,-160],[-461,-121],[94,116],[182,299],[93,116],[-84,9],[-285,-9],[40,75],[160,184],[586,-122],[350,328],[295,450],[418,250],[174,-310],[-46,-346],[-111,-375],[-17,-394]]],"aliases":{},"names":{"saint george":"GD-03","saint david":"GD-02","saint andrew":"GD-01","saint patrick":"GD-06","saint mark":"GD-05","saint john":"GD-04","carriacou and petite martinique":"GD-10"}}
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user