portal-mojo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +55 -0
- package/docs/DetailView.md +173 -0
- package/docs/ModelTable.md +128 -0
- package/docs/README.md +98 -0
- package/docs/admin-assistant.md +72 -0
- package/docs/admin-bouncer.md +120 -0
- package/docs/admin-cloudwatch.md +38 -0
- package/docs/admin-credentials.md +116 -0
- package/docs/admin-dashboard.md +15 -0
- package/docs/admin-devices-geoip.md +284 -0
- package/docs/admin-dns-certificates.md +122 -0
- package/docs/admin-dns-records.md +65 -0
- package/docs/admin-dns-registrar.md +33 -0
- package/docs/admin-dns.md +80 -0
- package/docs/admin-identity-users.md +103 -0
- package/docs/admin-incidents.md +71 -0
- package/docs/admin-jobs.md +426 -0
- package/docs/admin-members.md +112 -0
- package/docs/admin-messaging.md +56 -0
- package/docs/admin-metrics-explorer.md +63 -0
- package/docs/admin-monitoring.md +99 -0
- package/docs/admin-network-security.md +393 -0
- package/docs/admin-phonehub.md +27 -0
- package/docs/admin-push.md +36 -0
- package/docs/admin-rules.md +87 -0
- package/docs/admin-security-tickets.md +40 -0
- package/docs/admin-settings.md +78 -0
- package/docs/admin-shortlinks.md +32 -0
- package/docs/admin-storage.md +141 -0
- package/docs/auth-pages.md +212 -0
- package/docs/auth.md +58 -0
- package/docs/calendar.md +122 -0
- package/docs/charts.md +303 -0
- package/docs/client.md +206 -0
- package/docs/collection-multiselect.md +117 -0
- package/docs/collection-select.md +132 -0
- package/docs/combobox.md +105 -0
- package/docs/dataview.md +226 -0
- package/docs/datepicker.md +151 -0
- package/docs/daterange.md +170 -0
- package/docs/datetimepicker.md +249 -0
- package/docs/defineModel.md +72 -0
- package/docs/detail-primitives.md +174 -0
- package/docs/feedback.md +85 -0
- package/docs/file-upload-ux.md +146 -0
- package/docs/forms.md +435 -0
- package/docs/grouping-and-fmt.md +98 -0
- package/docs/idioms.md +112 -0
- package/docs/image-editor.md +101 -0
- package/docs/loading.md +242 -0
- package/docs/location-address.md +76 -0
- package/docs/markdown.md +203 -0
- package/docs/menus-and-access.md +89 -0
- package/docs/multiselect-dropdown.md +153 -0
- package/docs/params.md +73 -0
- package/docs/popover.md +130 -0
- package/docs/realtime.md +61 -0
- package/docs/record-feed.md +238 -0
- package/docs/right-panel.md +42 -0
- package/docs/tabs-and-form-wizard.md +60 -0
- package/docs/taginput.md +120 -0
- package/docs/timepicker.md +173 -0
- package/docs/timezone-select.md +147 -0
- package/docs/worldmap.md +302 -0
- package/package.json +55 -0
- package/src/admin/assistant/AssistantFeed.tsx +29 -0
- package/src/admin/assistant/AssistantPanel.tsx +65 -0
- package/src/admin/assistant/api.ts +17 -0
- package/src/admin/assistant/data.ts +63 -0
- package/src/admin/assistant/index.ts +21 -0
- package/src/admin/assistant/launchers.tsx +33 -0
- package/src/admin/assistant/pages.tsx +78 -0
- package/src/admin/assistant/streaming.ts +238 -0
- package/src/admin/assistant/types.ts +30 -0
- package/src/admin/bouncer/devices.tsx +230 -0
- package/src/admin/bouncer/index.ts +34 -0
- package/src/admin/bouncer/models.ts +199 -0
- package/src/admin/bouncer/signals.tsx +182 -0
- package/src/admin/bouncer/signatures.tsx +220 -0
- package/src/admin/cloudwatch/CloudWatchChart.tsx +61 -0
- package/src/admin/cloudwatch/CloudWatchDashboardPage.tsx +52 -0
- package/src/admin/cloudwatch/CloudWatchResourceDetail.tsx +33 -0
- package/src/admin/cloudwatch/data.ts +163 -0
- package/src/admin/cloudwatch/index.ts +16 -0
- package/src/admin/credentials/group-api-keys.tsx +433 -0
- package/src/admin/credentials/index.ts +4 -0
- package/src/admin/credentials/models.ts +244 -0
- package/src/admin/credentials/secret-dialog.tsx +77 -0
- package/src/admin/credentials/webhook-subscriptions.tsx +423 -0
- package/src/admin/dashboard/AdminDashboardPage.tsx +101 -0
- package/src/admin/dashboard/data.ts +60 -0
- package/src/admin/dashboard/index.ts +2 -0
- package/src/admin/dns/CertificateDetail.tsx +115 -0
- package/src/admin/dns/CertificateLifecyclePoller.tsx +141 -0
- package/src/admin/dns/CertificateRequestDialog.tsx +103 -0
- package/src/admin/dns/CertificatesPage.tsx +85 -0
- package/src/admin/dns/DnsRecordEditor.tsx +110 -0
- package/src/admin/dns/DnsRecordsPage.tsx +36 -0
- package/src/admin/dns/DnsRecordsPanel.tsx +71 -0
- package/src/admin/dns/DomainCertificatesSection.tsx +40 -0
- package/src/admin/dns/DomainDetail.tsx +41 -0
- package/src/admin/dns/DomainOnboardingDialog.tsx +27 -0
- package/src/admin/dns/DomainPurchaseDetail.tsx +11 -0
- package/src/admin/dns/DomainPurchaseWizard.tsx +112 -0
- package/src/admin/dns/DomainPurchasesPage.tsx +16 -0
- package/src/admin/dns/DomainsPage.tsx +27 -0
- package/src/admin/dns/ProviderCredentialsPage.tsx +304 -0
- package/src/admin/dns/RegistrantContactPage.tsx +42 -0
- package/src/admin/dns/api.ts +515 -0
- package/src/admin/dns/certificate-data.ts +221 -0
- package/src/admin/dns/data.ts +32 -0
- package/src/admin/dns/dns-data.ts +266 -0
- package/src/admin/dns/dns-integration.ts +24 -0
- package/src/admin/dns/domain-links.ts +37 -0
- package/src/admin/dns/index.ts +86 -0
- package/src/admin/dns/models.ts +418 -0
- package/src/admin/dns/purchase-data.ts +67 -0
- package/src/admin/dns/registrant-data.ts +52 -0
- package/src/admin/identity/index.ts +5 -0
- package/src/admin/identity/members/GroupMembersPanel.tsx +146 -0
- package/src/admin/identity/members/MemberDetail.tsx +226 -0
- package/src/admin/identity/members/MembersPage.tsx +144 -0
- package/src/admin/identity/members/index.ts +24 -0
- package/src/admin/identity/members/member-flows.tsx +132 -0
- package/src/admin/identity/members/models.ts +204 -0
- package/src/admin/identity/users/UserDetail.tsx +257 -0
- package/src/admin/identity/users/UsersPage.tsx +163 -0
- package/src/admin/identity/users/index.ts +27 -0
- package/src/admin/identity/users/models.ts +232 -0
- package/src/admin/identity/users/sections/ApiKeysSection.tsx +119 -0
- package/src/admin/identity/users/sections/AuditSection.tsx +190 -0
- package/src/admin/identity/users/sections/DevicesSection.tsx +140 -0
- package/src/admin/identity/users/sections/GroupsSection.tsx +64 -0
- package/src/admin/identity/users/sections/LoginsSection.tsx +106 -0
- package/src/admin/identity/users/sections/MetadataPanel.tsx +36 -0
- package/src/admin/identity/users/sections/NotificationsSection.tsx +117 -0
- package/src/admin/identity/users/sections/OAuthSection.tsx +72 -0
- package/src/admin/identity/users/sections/OverviewSection.tsx +109 -0
- package/src/admin/identity/users/sections/PermissionsSection.tsx +35 -0
- package/src/admin/identity/users/sections/PersonalSection.tsx +73 -0
- package/src/admin/identity/users/sections/ProfileSection.tsx +175 -0
- package/src/admin/identity/users/sections/SecuritySection.tsx +123 -0
- package/src/admin/identity/users/sections/actions.tsx +752 -0
- package/src/admin/identity/users/sections/permission-catalog.ts +138 -0
- package/src/admin/identity/users/sections/queries.ts +74 -0
- package/src/admin/identity/users/sections/shared.tsx +299 -0
- package/src/admin/incidents/EventDetail.tsx +43 -0
- package/src/admin/incidents/EventsPage.tsx +52 -0
- package/src/admin/incidents/IncidentDetail.tsx +68 -0
- package/src/admin/incidents/IncidentsPage.tsx +102 -0
- package/src/admin/incidents/forensics.tsx +73 -0
- package/src/admin/incidents/index.ts +16 -0
- package/src/admin/incidents/models.ts +113 -0
- package/src/admin/incidents/sanitize.ts +71 -0
- package/src/admin/index.ts +383 -0
- package/src/admin/jobs/JobDashboardPage.tsx +111 -0
- package/src/admin/jobs/JobDetail.tsx +721 -0
- package/src/admin/jobs/JobRunnersPage.tsx +216 -0
- package/src/admin/jobs/JobsTablePage.tsx +122 -0
- package/src/admin/jobs/RunnerDetail.tsx +576 -0
- package/src/admin/jobs/ScheduledTaskDetail.tsx +421 -0
- package/src/admin/jobs/ScheduledTasksPage.tsx +167 -0
- package/src/admin/jobs/columns.tsx +297 -0
- package/src/admin/jobs/control.ts +488 -0
- package/src/admin/jobs/index.ts +85 -0
- package/src/admin/jobs/models.ts +599 -0
- package/src/admin/jobs/queries.ts +272 -0
- package/src/admin/jobs/scheduled-task-form.tsx +493 -0
- package/src/admin/jobs/sections/JobChannelsPanel.tsx +142 -0
- package/src/admin/jobs/sections/JobOperationsSection.tsx +423 -0
- package/src/admin/jobs/sections/JobRunnersStrip.tsx +82 -0
- package/src/admin/jobs/sections/JobThroughputSection.tsx +118 -0
- package/src/admin/messaging/EmailDomainWizard.tsx +24 -0
- package/src/admin/messaging/EmailDomainsPage.tsx +17 -0
- package/src/admin/messaging/EmailTemplatesPage.tsx +13 -0
- package/src/admin/messaging/MailboxesPage.tsx +15 -0
- package/src/admin/messaging/PublicMessagesPage.tsx +13 -0
- package/src/admin/messaging/SandboxedEmailPreview.tsx +23 -0
- package/src/admin/messaging/SentMessagesPage.tsx +11 -0
- package/src/admin/messaging/api.ts +17 -0
- package/src/admin/messaging/data.ts +10 -0
- package/src/admin/messaging/index.ts +23 -0
- package/src/admin/messaging/models.ts +76 -0
- package/src/admin/messaging/push/PushPage.tsx +44 -0
- package/src/admin/messaging/push/api.ts +17 -0
- package/src/admin/messaging/push/index.ts +9 -0
- package/src/admin/messaging/push/models.ts +43 -0
- package/src/admin/messaging/sanitize.ts +44 -0
- package/src/admin/monitoring/LogInspector.tsx +160 -0
- package/src/admin/monitoring/LogsPage.tsx +135 -0
- package/src/admin/monitoring/MetricsExplorerPage.tsx +360 -0
- package/src/admin/monitoring/MetricsPermissionsPage.tsx +166 -0
- package/src/admin/monitoring/MetricsSourcePicker.tsx +136 -0
- package/src/admin/monitoring/index.ts +55 -0
- package/src/admin/monitoring/metrics-explorer-client.ts +229 -0
- package/src/admin/monitoring/metrics-explorer-data.ts +158 -0
- package/src/admin/monitoring/models.ts +155 -0
- package/src/admin/network/BlockedIPsPage.tsx +194 -0
- package/src/admin/network/BlockedIpDetail.tsx +262 -0
- package/src/admin/network/FirewallLogPage.tsx +115 -0
- package/src/admin/network/IPSetDetail.tsx +280 -0
- package/src/admin/network/IPSetEditor.tsx +191 -0
- package/src/admin/network/IPSetsPage.tsx +177 -0
- package/src/admin/network/geofence/BlocksTab.tsx +289 -0
- package/src/admin/network/geofence/ExemptionsTab.tsx +349 -0
- package/src/admin/network/geofence/GeofencingPage.tsx +119 -0
- package/src/admin/network/geofence/PostureHeader.tsx +184 -0
- package/src/admin/network/geofence/RuleEditor.tsx +176 -0
- package/src/admin/network/geofence/RulesTab.tsx +254 -0
- package/src/admin/network/geofence/SimulatorTab.tsx +281 -0
- package/src/admin/network/geofence/geofence-data.ts +674 -0
- package/src/admin/network/index.ts +68 -0
- package/src/admin/network/models.ts +607 -0
- package/src/admin/phonehub/PhoneHubPage.tsx +57 -0
- package/src/admin/phonehub/api.ts +47 -0
- package/src/admin/phonehub/data.ts +6 -0
- package/src/admin/phonehub/index.ts +10 -0
- package/src/admin/phonehub/models.ts +68 -0
- package/src/admin/rules/HandlerChainBuilder.tsx +51 -0
- package/src/admin/rules/RuleSetDetailPage.tsx +33 -0
- package/src/admin/rules/RuleSetsPage.tsx +33 -0
- package/src/admin/rules/editors.tsx +54 -0
- package/src/admin/rules/handler-dsl.ts +213 -0
- package/src/admin/rules/index.ts +13 -0
- package/src/admin/rules/models.ts +112 -0
- package/src/admin/security/devices/LoginEventDetail.tsx +123 -0
- package/src/admin/security/devices/LoginLocationMap.tsx +321 -0
- package/src/admin/security/devices/LoginLocationsPage.tsx +162 -0
- package/src/admin/security/devices/UserDeviceDetail.tsx +546 -0
- package/src/admin/security/devices/UserDeviceLocationDetail.tsx +272 -0
- package/src/admin/security/devices/UserDevicesPage.tsx +84 -0
- package/src/admin/security/devices/index.ts +40 -0
- package/src/admin/security/devices/models.ts +470 -0
- package/src/admin/security/geoip/GeoIpCachePage.tsx +135 -0
- package/src/admin/security/geoip/GeoIpDossier.tsx +804 -0
- package/src/admin/security/geoip/geoip-forms.ts +102 -0
- package/src/admin/security/geoip/index.ts +27 -0
- package/src/admin/security/geoip/models.ts +397 -0
- package/src/admin/security/index.ts +31 -0
- package/src/admin/security/models.ts +137 -0
- package/src/admin/security/tickets.tsx +562 -0
- package/src/admin/security-permissions.ts +5 -0
- package/src/admin/settings/SettingDetail.tsx +57 -0
- package/src/admin/settings/SettingEditor.tsx +157 -0
- package/src/admin/settings/SettingsPage.tsx +72 -0
- package/src/admin/settings/index.ts +19 -0
- package/src/admin/settings/model.ts +143 -0
- package/src/admin/shortlinks/ShortlinkDetail.tsx +55 -0
- package/src/admin/shortlinks/ShortlinkHistoryPage.tsx +18 -0
- package/src/admin/shortlinks/ShortlinksPage.tsx +59 -0
- package/src/admin/shortlinks/index.ts +18 -0
- package/src/admin/shortlinks/models.ts +246 -0
- package/src/admin/storage/BackendsPage.tsx +43 -0
- package/src/admin/storage/BucketDetail.tsx +77 -0
- package/src/admin/storage/BucketsPage.tsx +64 -0
- package/src/admin/storage/FileManagerDetail.tsx +36 -0
- package/src/admin/storage/FilePreview.tsx +54 -0
- package/src/admin/storage/FileUploadSurface.tsx +250 -0
- package/src/admin/storage/FileView.tsx +109 -0
- package/src/admin/storage/FilesPage.tsx +53 -0
- package/src/admin/storage/file-renditions.ts +99 -0
- package/src/admin/storage/index.ts +31 -0
- package/src/admin/storage/models.ts +585 -0
- package/src/admin/storage/storage-dialogs.tsx +80 -0
- package/src/charts/CircularProgress.tsx +281 -0
- package/src/charts/KPIStrip.tsx +162 -0
- package/src/charts/KPITile.tsx +125 -0
- package/src/charts/MetricsChart.tsx +459 -0
- package/src/charts/MetricsMiniWidget.tsx +588 -0
- package/src/charts/MiniChart.tsx +283 -0
- package/src/charts/PieChart.tsx +354 -0
- package/src/charts/SeriesChart.tsx +378 -0
- package/src/charts/chart-dialogs.tsx +149 -0
- package/src/charts/exportChart.ts +144 -0
- package/src/charts/index.ts +28 -0
- package/src/charts/pie-math.ts +127 -0
- package/src/charts/stats.ts +265 -0
- package/src/charts/worldmap/WorldMap.tsx +649 -0
- package/src/charts/worldmap/countryCentroids.ts +304 -0
- package/src/charts/worldmap/geo.ts +293 -0
- package/src/charts/worldmap/use-world-land.ts +47 -0
- package/src/charts/worldmap/world-land.ts +18 -0
- package/src/charts/worldmap/worldmap-data.ts +285 -0
- package/src/client/auth.ts +737 -0
- package/src/client/client.ts +268 -0
- package/src/client/duid.ts +31 -0
- package/src/client/errors.ts +33 -0
- package/src/client/group-context.ts +28 -0
- package/src/client/group.tsx +126 -0
- package/src/client/hooks.ts +50 -0
- package/src/client/index.ts +37 -0
- package/src/client/jwt.ts +75 -0
- package/src/client/location.ts +285 -0
- package/src/client/lookups.ts +41 -0
- package/src/client/markdown-fallback.tsx +115 -0
- package/src/client/markdown-parse.ts +386 -0
- package/src/client/markdown-sanitize.ts +217 -0
- package/src/client/markdown.ts +122 -0
- package/src/client/me.ts +188 -0
- package/src/client/mock.ts +9984 -0
- package/src/client/model.ts +227 -0
- package/src/client/params.ts +292 -0
- package/src/client/realtime-mock.ts +221 -0
- package/src/client/realtime.ts +561 -0
- package/src/client/record-feed.ts +329 -0
- package/src/client/safe-export.ts +71 -0
- package/src/client/types.ts +278 -0
- package/src/client/upload.ts +421 -0
- package/src/ui/AddressField.tsx +273 -0
- package/src/ui/AttachmentQueue.tsx +92 -0
- package/src/ui/CollectionMultiSelect.tsx +622 -0
- package/src/ui/CollectionSelect.tsx +517 -0
- package/src/ui/ComboBox.tsx +336 -0
- package/src/ui/DetailView.tsx +438 -0
- package/src/ui/FileDrop.tsx +194 -0
- package/src/ui/FileField.tsx +369 -0
- package/src/ui/FilterBar.tsx +269 -0
- package/src/ui/FormFields.tsx +77 -0
- package/src/ui/FormView.tsx +360 -0
- package/src/ui/FormWizard.tsx +336 -0
- package/src/ui/GroupSwitcher.tsx +202 -0
- package/src/ui/Guarded.tsx +16 -0
- package/src/ui/MarkdownView.tsx +89 -0
- package/src/ui/ModelTable.tsx +848 -0
- package/src/ui/MultiSelectDropdown.tsx +390 -0
- package/src/ui/Popover.tsx +204 -0
- package/src/ui/RecordFeed.tsx +627 -0
- package/src/ui/RequiresGroup.tsx +32 -0
- package/src/ui/RightPanel.tsx +107 -0
- package/src/ui/SidebarNav.tsx +330 -0
- package/src/ui/Tabs.tsx +161 -0
- package/src/ui/TagInput.tsx +381 -0
- package/src/ui/ThemeProvider.tsx +52 -0
- package/src/ui/UploadQueue.tsx +603 -0
- package/src/ui/armed-button.tsx +91 -0
- package/src/ui/date/Calendar.tsx +498 -0
- package/src/ui/date/DatePicker.tsx +289 -0
- package/src/ui/date/DateRangePicker.tsx +280 -0
- package/src/ui/date/DateTimePicker.tsx +587 -0
- package/src/ui/date/PresetRail.tsx +165 -0
- package/src/ui/date/TimePicker.tsx +624 -0
- package/src/ui/date/TimezoneSelect.tsx +267 -0
- package/src/ui/date/fns.ts +679 -0
- package/src/ui/detail/DataView.tsx +692 -0
- package/src/ui/detail/FlowStrip.tsx +80 -0
- package/src/ui/detail/KnownFieldsCard.tsx +121 -0
- package/src/ui/detail/MetadataSection.tsx +219 -0
- package/src/ui/detail/StackTraceView.tsx +150 -0
- package/src/ui/detail/StatusPanel.tsx +73 -0
- package/src/ui/detail/Timeline.tsx +52 -0
- package/src/ui/detail/index.ts +12 -0
- package/src/ui/field-registry.tsx +428 -0
- package/src/ui/field-wire.ts +322 -0
- package/src/ui/form-autosave.ts +583 -0
- package/src/ui/format.ts +444 -0
- package/src/ui/grouping.ts +150 -0
- package/src/ui/image-editor/ImageEditor.tsx +484 -0
- package/src/ui/image-editor/canvas.ts +254 -0
- package/src/ui/image-editor/index.ts +3 -0
- package/src/ui/image-editor/math.ts +416 -0
- package/src/ui/index.ts +58 -0
- package/src/ui/loading.tsx +604 -0
- package/src/ui/menu-registry.ts +213 -0
- package/src/ui/modal.tsx +297 -0
- package/src/ui/password.tsx +302 -0
- package/src/ui/schema-form-core.tsx +315 -0
- package/src/ui/toast.tsx +219 -0
- package/src/ui/ui.tsx +45 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// Country centroid table — ported from web-mojo
|
|
2
|
+
// src/extensions/map/countryCentroids.js (itself auto-generated from that
|
|
3
|
+
// repo's countries.geojson, a FeatureCollection of Points). Coordinate
|
|
4
|
+
// values are VERBATIM; only the key set changed, and only because it had
|
|
5
|
+
// to.
|
|
6
|
+
//
|
|
7
|
+
// The source has 249 rows but 244 distinct ISO2 keys: three codes carry
|
|
8
|
+
// several territories, and in a JS object literal the LAST one wins. That
|
|
9
|
+
// silently made `ES` resolve to Canarias rather than Spain — web-mojo's
|
|
10
|
+
// geofence country picker listed "Canarias" and Spanish metrics plotted in
|
|
11
|
+
// the Canary Islands. Duplicate keys are also a hard TS error (TS1117), so
|
|
12
|
+
// the table could not be transcribed as-is regardless.
|
|
13
|
+
//
|
|
14
|
+
// Resolution: the PRIMARY territory keeps the key; the 5 secondary rows are
|
|
15
|
+
// dropped, recorded here in full so nothing is lost and a future
|
|
16
|
+
// subdivision-aware table can restore them:
|
|
17
|
+
//
|
|
18
|
+
// BQ Saba lat 17.632512616389718 lng -63.23739481909494
|
|
19
|
+
// BQ Saint Eustatius lat 17.4919042294197 lng -62.978230589445026
|
|
20
|
+
// ES Canarias lat 28.297665106525546 lng -16.53799441021647
|
|
21
|
+
// TF Juan De Nova Island lat -17.06449193630804 lng 42.74374761089645
|
|
22
|
+
// TF Glorioso Islands lat -11.566224871643417 lng 47.290948081543384
|
|
23
|
+
//
|
|
24
|
+
// Kept: BQ → Bonaire, ES → Spain, TF → French Southern Territories.
|
|
25
|
+
|
|
26
|
+
/** One row of the centroid table: display name + the point to plot. */
|
|
27
|
+
export interface CountryCentroid {
|
|
28
|
+
name: string;
|
|
29
|
+
lat: number;
|
|
30
|
+
lng: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** ISO2 → centroid. 244 entries; the framework's only country-name source. */
|
|
34
|
+
export const COUNTRY_CENTROIDS: Record<string, CountryCentroid> = {
|
|
35
|
+
AD: { name: 'Andorra', lat: 42.54859834854764, lng: 1.5802243611232873 },
|
|
36
|
+
AE: { name: 'United Arab Emirates', lat: 24.18250292309135, lng: 54.27920525789581 },
|
|
37
|
+
AF: { name: 'Afghanistan', lat: 34.13402601376932, lng: 66.59216131095278 },
|
|
38
|
+
AG: { name: 'Antigua and Barbuda', lat: 17.07146759372967, lng: -61.78530823226373 },
|
|
39
|
+
AI: { name: 'Anguilla', lat: 18.222874004219086, lng: -63.06008343771806 },
|
|
40
|
+
AL: { name: 'Albania', lat: 41.14165894891656, lng: 20.061082767269493 },
|
|
41
|
+
AM: { name: 'Armenia', lat: 40.17841274230679, lng: 45.05490831965259 },
|
|
42
|
+
AO: { name: 'Angola', lat: -12.167424062667942, lng: 17.651768783079 },
|
|
43
|
+
AQ: { name: 'Antarctica', lat: -77.16987521415838, lng: -177.56451613408842 },
|
|
44
|
+
AR: { name: 'Argentina', lat: -35.697270518120085, lng: -64.53238503843076 },
|
|
45
|
+
AS: { name: 'American Samoa', lat: -14.305711987770538, lng: -170.7007316174498 },
|
|
46
|
+
AT: { name: 'Austria', lat: 47.631858269895794, lng: 13.797778364631036 },
|
|
47
|
+
AU: { name: 'Australia', lat: -25.697337673983082, lng: 134.02277170916162 },
|
|
48
|
+
AW: { name: 'Aruba', lat: 12.515625722992898, lng: -69.97564014284046 },
|
|
49
|
+
AZ: { name: 'Azerbaijan', lat: 40.3920509942049, lng: 48.634592670644324 },
|
|
50
|
+
BA: { name: 'Bosnia and Herzegovina', lat: 44.14415356126429, lng: 17.83467240787538 },
|
|
51
|
+
BB: { name: 'Barbados', lat: 13.183219369337529, lng: -59.557383949150285 },
|
|
52
|
+
BD: { name: 'Bangladesh', lat: 23.673728665121, lng: 90.43212562608613 },
|
|
53
|
+
BE: { name: 'Belgium', lat: 50.6182138854095, lng: 4.675010154696485 },
|
|
54
|
+
BF: { name: 'Burkina Faso', lat: 12.108709036312737, lng: -1.6932816211842325 },
|
|
55
|
+
BG: { name: 'Bulgaria', lat: 42.82043677302438, lng: 25.251739122561908 },
|
|
56
|
+
BH: { name: 'Bahrain', lat: 26.04798501537066, lng: 50.540695402276775 },
|
|
57
|
+
BI: { name: 'Burundi', lat: -3.261251993278643, lng: 29.88518227845293 },
|
|
58
|
+
BJ: { name: 'Benin', lat: 9.503013199615893, lng: 2.305714528830206 },
|
|
59
|
+
BL: { name: 'Saint Barthelemy', lat: 17.90561691241738, lng: -62.83051610005156 },
|
|
60
|
+
BM: { name: 'Bermuda', lat: 32.315067430740726, lng: -64.7458500599169 },
|
|
61
|
+
BN: { name: 'Brunei Darussalam', lat: 4.543205889917609, lng: 114.6430958360464 },
|
|
62
|
+
BO: { name: 'Bolivia', lat: -16.7312488393574, lng: -64.45209597511206 },
|
|
63
|
+
BQ: { name: 'Bonaire', lat: 12.180844982440338, lng: -68.29350445958761 },
|
|
64
|
+
BR: { name: 'Brazil', lat: -11.524630416426652, lng: -54.355206608256424 },
|
|
65
|
+
BS: { name: 'Bahamas', lat: 24.72162633646784, lng: -78.07275370060313 },
|
|
66
|
+
BT: { name: 'Bhutan', lat: 27.42163933959824, lng: 90.46716647173861 },
|
|
67
|
+
BV: { name: 'Bouvet Island', lat: -54.42316679395248, lng: 3.411969465057627 },
|
|
68
|
+
BW: { name: 'Botswana', lat: -22.236609002062902, lng: 23.85779956995608 },
|
|
69
|
+
BY: { name: 'Belarus', lat: 53.46791374543163, lng: 27.964252054715104 },
|
|
70
|
+
BZ: { name: 'Belize', lat: 17.24252476647155, lng: -88.68273510023441 },
|
|
71
|
+
CA: { name: 'Canada', lat: 57.550480044655636, lng: -98.41680517868062 },
|
|
72
|
+
CC: { name: 'Cocos Islands', lat: -12.171249450199545, lng: 96.83688767323002 },
|
|
73
|
+
CD: { name: 'Congo DRC', lat: -3.338629596207896, lng: 23.419827574282188 },
|
|
74
|
+
CF: { name: 'Central African Republic', lat: 6.331390033944319, lng: 20.520743419397256 },
|
|
75
|
+
CG: { name: 'Congo', lat: -0.7294391595233845, lng: 14.879732849491393 },
|
|
76
|
+
CH: { name: 'Switzerland', lat: 46.73678128684938, lng: 8.286928794895285 },
|
|
77
|
+
CI: { name: "Côte d'Ivoire", lat: 7.536779279421307, lng: -5.571710194917734 },
|
|
78
|
+
CK: { name: 'Cook Islands', lat: -21.222613253399842, lng: -159.78768870952257 },
|
|
79
|
+
CL: { name: 'Chile', lat: -37.82938283049967, lng: -70.76863431739216 },
|
|
80
|
+
CM: { name: 'Cameroon', lat: 6.294168487480992, lng: 12.948474142398263 },
|
|
81
|
+
CN: { name: 'China', lat: 38.07325481105728, lng: 104.69113855849604 },
|
|
82
|
+
CO: { name: 'Colombia', lat: 4.187753877352739, lng: -72.6445066243485 },
|
|
83
|
+
CR: { name: 'Costa Rica', lat: 9.863467407406214, lng: -84.14673625701816 },
|
|
84
|
+
CU: { name: 'Cuba', lat: 21.476176522869448, lng: -79.69817857618705 },
|
|
85
|
+
CV: { name: 'Cabo Verde', lat: 15.076411518651643, lng: -23.63401005900474 },
|
|
86
|
+
CW: { name: 'Curacao', lat: 12.199996647939832, lng: -68.96939768599042 },
|
|
87
|
+
CX: { name: 'Christmas Island', lat: -10.446440802183416, lng: 105.70209512200549 },
|
|
88
|
+
CY: { name: 'Cyprus', lat: 35.11700416345239, lng: 33.375346009199205 },
|
|
89
|
+
CZ: { name: 'Czech Republic', lat: 49.74917370930982, lng: 15.383273292023533 },
|
|
90
|
+
DE: { name: 'Germany', lat: 51.08304539800482, lng: 10.426171427430804 },
|
|
91
|
+
DJ: { name: 'Djibouti', lat: 11.750235727618804, lng: 42.613496898789506 },
|
|
92
|
+
DK: { name: 'Denmark', lat: 56.00118817971057, lng: 9.378670542409406 },
|
|
93
|
+
DM: { name: 'Dominica', lat: 15.429269860940513, lng: -61.360471946942994 },
|
|
94
|
+
DO: { name: 'Dominican Republic', lat: 18.77954818522993, lng: -70.43495198520012 },
|
|
95
|
+
DZ: { name: 'Algeria', lat: 28.350969744889056, lng: 2.6558464719769135 },
|
|
96
|
+
EC: { name: 'Ecuador', lat: -1.5642721388853116, lng: -78.4630326109714 },
|
|
97
|
+
EE: { name: 'Estonia', lat: 58.648108311231034, lng: 25.916870250633806 },
|
|
98
|
+
EG: { name: 'Egypt', lat: 26.60517034450628, lng: 30.240135435012338 },
|
|
99
|
+
ER: { name: 'Eritrea', lat: 15.005533147667684, lng: 39.2672401449901 },
|
|
100
|
+
ES: { name: 'Spain', lat: 40.365008336683836, lng: -3.6516251409956983 },
|
|
101
|
+
ET: { name: 'Ethiopia', lat: 8.729389557048396, lng: 39.914902886544276 },
|
|
102
|
+
FI: { name: 'Finland', lat: 65.01578959749911, lng: 25.65738433454702 },
|
|
103
|
+
FJ: { name: 'Fiji', lat: -17.822470952336204, lng: 177.98144613732626 },
|
|
104
|
+
FK: { name: 'Falkland Islands', lat: -51.75901312766726, lng: -58.746646363799854 },
|
|
105
|
+
FM: { name: 'Micronesia', lat: 6.8789448129255435, lng: 158.2291899444527 },
|
|
106
|
+
FO: { name: 'Faroe Islands', lat: 62.130896281495346, lng: -6.9811060913122835 },
|
|
107
|
+
FR: { name: 'France', lat: 46.6423682169416, lng: 2.1940236627886227 },
|
|
108
|
+
GA: { name: 'Gabon', lat: -0.628448459921234, lng: 11.839410898545754 },
|
|
109
|
+
GB: { name: 'United Kingdom', lat: 53.97844735080214, lng: -2.852943909329258 },
|
|
110
|
+
GD: { name: 'Grenada', lat: 12.112926656338907, lng: -61.67937937204098 },
|
|
111
|
+
GE: { name: 'Georgia', lat: 42.17986277737226, lng: 43.378866534112234 },
|
|
112
|
+
GF: { name: 'French Guiana', lat: 3.857429742497078, lng: -53.32232307156624 },
|
|
113
|
+
GG: { name: 'Guernsey', lat: 49.45870771378872, lng: -2.576392582891568 },
|
|
114
|
+
GH: { name: 'Ghana', lat: 7.94530467243628, lng: -1.219233362526581 },
|
|
115
|
+
GI: { name: 'Gibraltar', lat: 36.14022671336082, lng: -5.345549484594358 },
|
|
116
|
+
GL: { name: 'Greenland', lat: 74.16847218965994, lng: -42.07567788066985 },
|
|
117
|
+
GM: { name: 'Gambia', lat: 13.428617959189328, lng: -15.383380385869662 },
|
|
118
|
+
GN: { name: 'Guinea', lat: 10.255986541378112, lng: -10.986948848040218 },
|
|
119
|
+
GP: { name: 'Guadeloupe', lat: 16.24420002705553, lng: -61.54382262282755 },
|
|
120
|
+
GQ: { name: 'Equatorial Guinea', lat: 1.5954643936590733, lng: 10.425456672353823 },
|
|
121
|
+
GR: { name: 'Greece', lat: 39.42012261727978, lng: 23.110368936161876 },
|
|
122
|
+
GS: { name: 'South Georgia and South Sandwich Islands', lat: -54.37666443862139, lng: -36.77509575898928 },
|
|
123
|
+
GT: { name: 'Guatemala', lat: 15.820878515352684, lng: -90.31219349119617 },
|
|
124
|
+
GU: { name: 'Guam', lat: 13.445430479945276, lng: 144.78024458298802 },
|
|
125
|
+
GW: { name: 'Guinea-Bissau', lat: 11.980075324820504, lng: -14.980186756910847 },
|
|
126
|
+
GY: { name: 'Guyana', lat: 4.68211981385056, lng: -58.91352612754667 },
|
|
127
|
+
HM: { name: 'Heard Island and McDonald Islands', lat: -53.084170035513736, lng: 73.49298560844045 },
|
|
128
|
+
HN: { name: 'Honduras', lat: 14.740370695750006, lng: -86.49251679006962 },
|
|
129
|
+
HR: { name: 'Croatia', lat: 44.91192100856702, lng: 16.625761129583374 },
|
|
130
|
+
HT: { name: 'Haiti', lat: 18.883520486983567, lng: -72.89291379842 },
|
|
131
|
+
HU: { name: 'Hungary', lat: 47.22527332486294, lng: 19.39620048366142 },
|
|
132
|
+
ID: { name: 'Indonesia', lat: 0.15591979959037652, lng: 113.96538246847302 },
|
|
133
|
+
IE: { name: 'Ireland', lat: 53.30489539816495, lng: -8.241128545554096 },
|
|
134
|
+
IL: { name: 'Israel', lat: 31.513542220043195, lng: 35.027923472437024 },
|
|
135
|
+
IM: { name: 'Isle of Man', lat: 54.22855301008011, lng: -4.532995055468449 },
|
|
136
|
+
IN: { name: 'India', lat: 23.586300567746722, lng: 81.17300408530181 },
|
|
137
|
+
IO: { name: 'British Indian Ocean Territory', lat: -7.323548444385743, lng: 72.43501618476016 },
|
|
138
|
+
IQ: { name: 'Iraq', lat: 33.105075667527906, lng: 43.832529181056884 },
|
|
139
|
+
IR: { name: 'Iran', lat: 32.906023742890056, lng: 54.237077001065444 },
|
|
140
|
+
IS: { name: 'Iceland', lat: 65.12360920205514, lng: -19.05682967106099 },
|
|
141
|
+
IT: { name: 'Italy', lat: 42.98201127614267, lng: 12.763657166123137 },
|
|
142
|
+
JE: { name: 'Jersey', lat: 49.215396925724306, lng: -2.1291601162653575 },
|
|
143
|
+
JM: { name: 'Jamaica', lat: 18.12207889341651, lng: -77.30358894542778 },
|
|
144
|
+
JO: { name: 'Jordan', lat: 31.387064884449156, lng: 36.95728884547246 },
|
|
145
|
+
JP: { name: 'Japan', lat: 36.76738832597829, lng: 137.46934234351835 },
|
|
146
|
+
KE: { name: 'Kenya', lat: 0.6899182318376179, lng: 37.95309411262371 },
|
|
147
|
+
KG: { name: 'Kyrgyzstan', lat: 41.35698905438358, lng: 74.17532903468319 },
|
|
148
|
+
KH: { name: 'Cambodia', lat: 12.699186865507775, lng: 105.03973078680701 },
|
|
149
|
+
KI: { name: 'Kiribati', lat: 1.8676673749241066, lng: -157.39024189323504 },
|
|
150
|
+
KM: { name: 'Comoros', lat: -11.658861474508491, lng: 43.34826587429403 },
|
|
151
|
+
KN: { name: 'Saint Kitts and Nevis', lat: 17.314736299587768, lng: -62.74560385895787 },
|
|
152
|
+
KP: { name: 'North Korea', lat: 40.19198091470839, lng: 127.3379805653744 },
|
|
153
|
+
KR: { name: 'South Korea', lat: 36.402386712544114, lng: 127.76224551357649 },
|
|
154
|
+
KW: { name: 'Kuwait', lat: 29.281360965443092, lng: 47.56311109320184 },
|
|
155
|
+
KY: { name: 'Cayman Islands', lat: 19.311231805620288, lng: -81.25203208977878 },
|
|
156
|
+
KZ: { name: 'Kazakhstan', lat: 47.641465195176835, lng: 66.3759193479301 },
|
|
157
|
+
LA: { name: 'Laos', lat: 18.117282736873282, lng: 103.76375899026448 },
|
|
158
|
+
LB: { name: 'Lebanon', lat: 33.91160170722086, lng: 35.89651946324749 },
|
|
159
|
+
LC: { name: 'Saint Lucia', lat: 13.895749185129906, lng: -60.9689510935251 },
|
|
160
|
+
LI: { name: 'Liechtenstein', lat: 47.14627562133036, lng: 9.547674672376376 },
|
|
161
|
+
LK: { name: 'Sri Lanka', lat: 7.696630939329944, lng: 80.66931169770622 },
|
|
162
|
+
LR: { name: 'Liberia', lat: 6.52012979398834, lng: -9.258988935497618 },
|
|
163
|
+
LS: { name: 'Lesotho', lat: -29.60168116924817, lng: 28.24475317864227 },
|
|
164
|
+
LT: { name: 'Lithuania', lat: 55.29437393417175, lng: 23.946021605013534 },
|
|
165
|
+
LU: { name: 'Luxembourg', lat: 49.77523454542369, lng: 6.103230338458876 },
|
|
166
|
+
LV: { name: 'Latvia', lat: 56.813853047554154, lng: 24.693671325654403 },
|
|
167
|
+
LY: { name: 'Libya', lat: 27.202915771690794, lng: 17.91133392454237 },
|
|
168
|
+
MA: { name: 'Morocco', lat: 28.687598134979325, lng: -8.817212587250811 },
|
|
169
|
+
MC: { name: 'Monaco', lat: 43.74798224995656, lng: 7.412820873271196 },
|
|
170
|
+
MD: { name: 'Moldova', lat: 47.0725674580696, lng: 28.391111865941348 },
|
|
171
|
+
ME: { name: 'Montenegro', lat: 42.73694835210454, lng: 19.29505087156758 },
|
|
172
|
+
MF: { name: 'Saint Martin', lat: 18.078012113224464, lng: -63.06678525361946 },
|
|
173
|
+
MG: { name: 'Madagascar', lat: -19.04163612493041, lng: 46.68493466832544 },
|
|
174
|
+
MH: { name: 'Marshall Islands', lat: 7.307929900994344, lng: 168.72016025351076 },
|
|
175
|
+
MK: { name: 'North Macedonia', lat: 41.59402890143112, lng: 21.70998923872772 },
|
|
176
|
+
ML: { name: 'Mali', lat: 17.168146208584837, lng: -4.346399841781153 },
|
|
177
|
+
MM: { name: 'Myanmar', lat: 19.901227931399873, lng: 97.08892285397344 },
|
|
178
|
+
MN: { name: 'Mongolia', lat: 47.08644454604851, lng: 103.3987360327455 },
|
|
179
|
+
MP: { name: 'Northern Mariana Islands', lat: 15.178063516432115, lng: 145.74119737203134 },
|
|
180
|
+
MQ: { name: 'Martinique', lat: 14.642697353597645, lng: -61.01432380875083 },
|
|
181
|
+
MR: { name: 'Mauritania', lat: 20.466731212820022, lng: -10.495079045035716 },
|
|
182
|
+
MS: { name: 'Montserrat', lat: 16.735363391460357, lng: -62.18693281256255 },
|
|
183
|
+
MT: { name: 'Malta', lat: 35.890522650899314, lng: 14.441922442508494 },
|
|
184
|
+
MU: { name: 'Mauritius', lat: -20.28142317475198, lng: 57.56415671066546 },
|
|
185
|
+
MV: { name: 'Maldives', lat: -0.6065577168009924, lng: 73.10076245140479 },
|
|
186
|
+
MW: { name: 'Malawi', lat: -13.128986464184024, lng: 34.23441182298881 },
|
|
187
|
+
MX: { name: 'Mexico', lat: 23.87436068093592, lng: -101.55399731630118 },
|
|
188
|
+
MY: { name: 'Malaysia', lat: 3.6716608556387857, lng: 114.63330303992869 },
|
|
189
|
+
MZ: { name: 'Mozambique', lat: -17.525230309488748, lng: 35.208577031290176 },
|
|
190
|
+
NA: { name: 'Namibia', lat: -21.90858163281473, lng: 18.16451345845268 },
|
|
191
|
+
NC: { name: 'New Caledonia', lat: -21.33003372660827, lng: 165.50767040438612 },
|
|
192
|
+
NE: { name: 'Niger', lat: 17.08105392407292, lng: 8.86863247002646 },
|
|
193
|
+
NF: { name: 'Norfolk Island', lat: -29.037654445046282, lng: 167.95259597483337 },
|
|
194
|
+
NG: { name: 'Nigeria', lat: 9.61029352034213, lng: 8.147714845256194 },
|
|
195
|
+
NI: { name: 'Nicaragua', lat: 12.893566631930554, lng: -85.016088327669 },
|
|
196
|
+
NL: { name: 'Netherlands', lat: 52.134054128923886, lng: 5.554136426128487 },
|
|
197
|
+
NO: { name: 'Norway', lat: 64.97775882947745, lng: 16.670259272390894 },
|
|
198
|
+
NP: { name: 'Nepal', lat: 28.300920699755657, lng: 84.1338898313567 },
|
|
199
|
+
NR: { name: 'Nauru', lat: -0.5221021440668993, lng: 166.92937633139178 },
|
|
200
|
+
NU: { name: 'Niue', lat: -19.05230921680393, lng: -169.86878106699083 },
|
|
201
|
+
NZ: { name: 'New Zealand', lat: -43.82765432544426, lng: 170.69035541428696 },
|
|
202
|
+
OM: { name: 'Oman', lat: 20.7242833183209, lng: 55.841088119829 },
|
|
203
|
+
PA: { name: 'Panama', lat: 8.439536749576892, lng: -80.14428761482796 },
|
|
204
|
+
PE: { name: 'Peru', lat: -8.522717984240291, lng: -74.11416196781884 },
|
|
205
|
+
PF: { name: 'French Polynesia', lat: -17.674684080120677, lng: -149.40041671099763 },
|
|
206
|
+
PG: { name: 'Papua New Guinea', lat: -7.156912819152135, lng: 144.8348942994695 },
|
|
207
|
+
PH: { name: 'Philippines', lat: 15.586542251094242, lng: 121.82208941416745 },
|
|
208
|
+
PK: { name: 'Pakistan', lat: 30.116188371410882, lng: 69.08835090769651 },
|
|
209
|
+
PL: { name: 'Poland', lat: 52.06848055692473, lng: 19.43573279234468 },
|
|
210
|
+
PM: { name: 'Saint Pierre and Miquelon', lat: 46.95153913615198, lng: -56.32465353437558 },
|
|
211
|
+
PN: { name: 'Pitcairn', lat: -24.366121747565458, lng: -128.3149848627581 },
|
|
212
|
+
PR: { name: 'Puerto Rico', lat: 18.216224086610914, lng: -66.49425339593509 },
|
|
213
|
+
PS: { name: 'Palestinian Territory', lat: 31.930818736453883, lng: 35.24251184154588 },
|
|
214
|
+
PT: { name: 'Portugal', lat: 39.67529214702138, lng: -7.933662183874006 },
|
|
215
|
+
PW: { name: 'Palau', lat: 7.534775852547396, lng: 134.57965086721052 },
|
|
216
|
+
PY: { name: 'Paraguay', lat: -23.42190559259428, lng: -58.38906357428651 },
|
|
217
|
+
QA: { name: 'Qatar', lat: 25.318528486425386, lng: 51.19794918743203 },
|
|
218
|
+
RE: { name: 'Réunion', lat: -21.119825460665105, lng: 55.54393506194689 },
|
|
219
|
+
RO: { name: 'Romania', lat: 45.82454894397586, lng: 25.094158201563292 },
|
|
220
|
+
RS: { name: 'Serbia', lat: 44.02679870131969, lng: 20.85677444395745 },
|
|
221
|
+
RU: { name: 'Russian Federation', lat: 59.039434214106194, lng: 98.6704990698032 },
|
|
222
|
+
RW: { name: 'Rwanda', lat: -2.014687460047154, lng: 29.919439681764082 },
|
|
223
|
+
SA: { name: 'Saudi Arabia', lat: 24.136038144757897, lng: 44.600958178225596 },
|
|
224
|
+
SB: { name: 'Solomon Islands', lat: -9.613104734596515, lng: 160.16475795033884 },
|
|
225
|
+
SC: { name: 'Seychelles', lat: -4.660002318822744, lng: 55.47250789595527 },
|
|
226
|
+
SD: { name: 'Sudan', lat: 15.67060230984256, lng: 29.951458283594064 },
|
|
227
|
+
SE: { name: 'Sweden', lat: 62.73420986108448, lng: 17.062431988004956 },
|
|
228
|
+
SG: { name: 'Singapore', lat: 1.3528251890006349, lng: 103.81025757634053 },
|
|
229
|
+
SH: { name: 'Saint Helena', lat: -15.962963318340398, lng: -5.717391620813109 },
|
|
230
|
+
SI: { name: 'Slovenia', lat: 46.13759229564504, lng: 14.890636899973781 },
|
|
231
|
+
SJ: { name: 'Svalbard', lat: 78.57318936469626, lng: 16.036378851505283 },
|
|
232
|
+
SK: { name: 'Slovakia', lat: 48.69808390520484, lng: 19.581015362490966 },
|
|
233
|
+
SL: { name: 'Sierra Leone', lat: 8.561330384750587, lng: -11.78656695731115 },
|
|
234
|
+
SM: { name: 'San Marino', lat: 43.942820729097896, lng: 12.461278349581722 },
|
|
235
|
+
SN: { name: 'Senegal', lat: 14.228861491763402, lng: -14.610875368352305 },
|
|
236
|
+
SO: { name: 'Somalia', lat: 6.524534573103924, lng: 45.40037867243972 },
|
|
237
|
+
SR: { name: 'Suriname', lat: 4.098723595920171, lng: -55.855514311561286 },
|
|
238
|
+
SS: { name: 'South Sudan', lat: 7.657782041763295, lng: 30.3851856901788 },
|
|
239
|
+
ST: { name: 'Sao Tome and Principe', lat: 0.22744704294793774, lng: 6.606158796857607 },
|
|
240
|
+
SV: { name: 'El Salvador', lat: 13.758041517055418, lng: -88.85911489238985 },
|
|
241
|
+
SX: { name: 'Sint Maarten', lat: 18.03942608463078, lng: -63.06883135915303 },
|
|
242
|
+
SY: { name: 'Syria', lat: 35.09751106058316, lng: 38.5117323139514 },
|
|
243
|
+
SZ: { name: 'Eswatini', lat: -26.562540935608702, lng: 31.510685746082007 },
|
|
244
|
+
TC: { name: 'Turks and Caicos Islands', lat: 21.799865427483745, lng: -71.74058946811508 },
|
|
245
|
+
TD: { name: 'Chad', lat: 15.283493546654503, lng: 18.427113900363025 },
|
|
246
|
+
TF: { name: 'French Southern Territories', lat: -49.26329687105643, lng: 69.54686984724839 },
|
|
247
|
+
TG: { name: 'Togo', lat: 8.660743037717811, lng: 0.8990857571109684 },
|
|
248
|
+
TH: { name: 'Thailand', lat: 13.66222784745538, lng: 101.08675116214552 },
|
|
249
|
+
TJ: { name: 'Tajikistan', lat: 38.56933138382972, lng: 70.94215281065698 },
|
|
250
|
+
TK: { name: 'Tokelau', lat: -9.195174767256544, lng: -171.85265950722743 },
|
|
251
|
+
TL: { name: 'Timor-Leste', lat: -8.809894630601962, lng: 125.95024049562659 },
|
|
252
|
+
TM: { name: 'Turkmenistan', lat: 39.06069118001429, lng: 58.4577357627054 },
|
|
253
|
+
TN: { name: 'Tunisia', lat: 34.08636179565723, lng: 9.65587551697984 },
|
|
254
|
+
TO: { name: 'Tonga', lat: -21.15927212823853, lng: -175.20415878511247 },
|
|
255
|
+
TR: { name: 'Turkey', lat: 38.93207363123396, lng: 35.56886764076691 },
|
|
256
|
+
TT: { name: 'Trinidad and Tobago', lat: 10.415515638298093, lng: -61.37236579976247 },
|
|
257
|
+
TV: { name: 'Tuvalu', lat: -8.514701506447222, lng: 179.217833977593 },
|
|
258
|
+
TZ: { name: 'Tanzania', lat: -6.355794440041147, lng: 34.81832206060381 },
|
|
259
|
+
UA: { name: 'Ukraine', lat: 48.657532515563794, lng: 31.27377208442636 },
|
|
260
|
+
UG: { name: 'Uganda', lat: 1.2821729218416205, lng: 32.34371768463123 },
|
|
261
|
+
UM: { name: 'United States Minor Outlying Islands', lat: 19.302045812215958, lng: 166.63800339749642 },
|
|
262
|
+
US: { name: 'United States', lat: 38.8208089190304, lng: -96.33161660829639 },
|
|
263
|
+
UY: { name: 'Uruguay', lat: -32.78195043831093, lng: -56.01919523458085 },
|
|
264
|
+
UZ: { name: 'Uzbekistan', lat: 41.4879065244633, lng: 63.8548297593985 },
|
|
265
|
+
VA: { name: 'Vatican City', lat: 41.90402351316735, lng: 12.451312917026133 },
|
|
266
|
+
VC: { name: 'Saint Vincent and the Grenadines', lat: 13.254808122970651, lng: -61.193766354393034 },
|
|
267
|
+
VE: { name: 'Venezuela', lat: 7.148324760507107, lng: -66.36492135985132 },
|
|
268
|
+
VG: { name: 'British Virgin Islands', lat: 18.42195819619707, lng: -64.62406519257699 },
|
|
269
|
+
VI: { name: 'US Virgin Islands', lat: 17.738009708772523, lng: -64.76155341409797 },
|
|
270
|
+
VN: { name: 'Vietnam', lat: 16.517347170839393, lng: 105.91338832758704 },
|
|
271
|
+
VU: { name: 'Vanuatu', lat: -15.189132121699332, lng: 166.84912735669738 },
|
|
272
|
+
WF: { name: 'Wallis and Futuna', lat: -14.283442307826677, lng: -178.12735555777184 },
|
|
273
|
+
WS: { name: 'Samoa', lat: -13.634252953274622, lng: -172.44107655740137 },
|
|
274
|
+
YE: { name: 'Yemen', lat: 16.001392616307445, lng: 47.46815793206386 },
|
|
275
|
+
YT: { name: 'Mayotte', lat: -12.824468416301052, lng: 45.128142327031064 },
|
|
276
|
+
ZA: { name: 'South Africa', lat: -28.55361930679731, lng: 24.75252746489084 },
|
|
277
|
+
ZM: { name: 'Zambia', lat: -13.162832953186246, lng: 27.75521363430896 },
|
|
278
|
+
ZW: { name: 'Zimbabwe', lat: -18.92700121981475, lng: 29.717829640720844 },
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* ISO2 (any case) → centroid, or `null` when the code is unknown. Unlike the
|
|
283
|
+
* source this never throws on a non-string; callers feed it wire data.
|
|
284
|
+
*/
|
|
285
|
+
export function getCountryCentroid(code: string | null | undefined): CountryCentroid | null {
|
|
286
|
+
if (!code) return null;
|
|
287
|
+
return COUNTRY_CENTROIDS[String(code).toUpperCase()] ?? null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** ISO2 → display name. Unknown codes pass through UPPERCASED, never blank. */
|
|
291
|
+
export function countryName(code: string | null | undefined): string {
|
|
292
|
+
if (!code) return '';
|
|
293
|
+
const upper = String(code).toUpperCase();
|
|
294
|
+
return COUNTRY_CENTROIDS[upper]?.name ?? upper;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* `{value, label}` options for country pickers, sorted by `localeCompare` on
|
|
299
|
+
* the label — the exact derivation web-mojo's GeofenceRuleForm.js:13-17 did
|
|
300
|
+
* inline, promoted to a toolkit export so the geofence UI has one source.
|
|
301
|
+
*/
|
|
302
|
+
export const COUNTRY_OPTIONS: readonly { value: string; label: string }[] = Object.entries(COUNTRY_CENTROIDS)
|
|
303
|
+
.map(([value, info]) => ({ value, label: info.name }))
|
|
304
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// geo.ts — the WorldMap's projection math. Pure functions, no React, no DOM:
|
|
2
|
+
// everything here is headless-testable (scripts/verify-worldmap.mjs) and is
|
|
3
|
+
// the piece a future tile-backed map would reuse unchanged.
|
|
4
|
+
//
|
|
5
|
+
// Equirectangular (plate carrée) is the projection web-mojo effectively
|
|
6
|
+
// showed at zoom 1.3 over a demo tile server, and it is the only sane choice
|
|
7
|
+
// for a graticule fallback: lat/lng map to y/x linearly, so the inverse is
|
|
8
|
+
// exact and a "no geometry shipped" map still tells the truth about where a
|
|
9
|
+
// point is.
|
|
10
|
+
//
|
|
11
|
+
// Coordinate validity is `Number.isFinite`, never truthiness — web-mojo's
|
|
12
|
+
// `if (!lng || !lat) return` (MapLibreView.js:223) dropped legitimate points
|
|
13
|
+
// on the equator and the prime meridian.
|
|
14
|
+
|
|
15
|
+
/** A geographic point. `lat` is degrees north, `lng` degrees east. */
|
|
16
|
+
export interface LatLng {
|
|
17
|
+
lat: number;
|
|
18
|
+
lng: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A geographic crop. All four values are degrees. */
|
|
22
|
+
export interface GeoBounds {
|
|
23
|
+
west: number;
|
|
24
|
+
east: number;
|
|
25
|
+
south: number;
|
|
26
|
+
north: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The viewport: which point sits at the center of the frame, and how far in.
|
|
31
|
+
* `zoom` 1 fits the whole `bounds` box; `MAX_ZOOM` is the hard ceiling.
|
|
32
|
+
*/
|
|
33
|
+
export interface WorldMapView {
|
|
34
|
+
lat: number;
|
|
35
|
+
lng: number;
|
|
36
|
+
zoom: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Pixel point in the SVG's own coordinate space (origin top-left). */
|
|
40
|
+
export interface PixelPoint {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface PixelSize {
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The default crop: the whole world minus the polar caps nothing plots in.
|
|
52
|
+
* web-mojo approximated the same framing with `center [10, 20], zoom 1.3` on
|
|
53
|
+
* a Mercator basemap. Antarctica (lat −77) sits OUTSIDE this box on purpose —
|
|
54
|
+
* it is counted as off-bounds, not clamped to the edge; widen `bounds` if a
|
|
55
|
+
* consumer really needs it.
|
|
56
|
+
*/
|
|
57
|
+
export const DEFAULT_BOUNDS: GeoBounds = { west: -180, east: 180, south: -58, north: 84 };
|
|
58
|
+
|
|
59
|
+
export const MIN_ZOOM = 1;
|
|
60
|
+
export const MAX_ZOOM = 12;
|
|
61
|
+
|
|
62
|
+
/** The whole-world view: the center of DEFAULT_BOUNDS at zoom 1. */
|
|
63
|
+
export const DEFAULT_VIEW: WorldMapView = { lat: 13, lng: 0, zoom: 1 };
|
|
64
|
+
|
|
65
|
+
/** True when both coordinates are real numbers. `{lat: 0, lng: 0}` passes. */
|
|
66
|
+
export function isFinitePoint(p: LatLng | null | undefined): p is LatLng {
|
|
67
|
+
return !!p && Number.isFinite(p.lat) && Number.isFinite(p.lng);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const clamp = (v: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, v));
|
|
71
|
+
|
|
72
|
+
/** A projection bound to one container size, crop and viewport. */
|
|
73
|
+
export interface Projection {
|
|
74
|
+
size: PixelSize;
|
|
75
|
+
bounds: GeoBounds;
|
|
76
|
+
view: WorldMapView;
|
|
77
|
+
/** Pixels per degree. Uniform in x and y — circles stay circles. */
|
|
78
|
+
scale: number;
|
|
79
|
+
project(p: LatLng): PixelPoint;
|
|
80
|
+
invert(pt: PixelPoint): LatLng;
|
|
81
|
+
/** True when the projected point falls inside the drawn frame. */
|
|
82
|
+
contains(p: LatLng): boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ProjectionOptions {
|
|
86
|
+
width: number;
|
|
87
|
+
height: number;
|
|
88
|
+
bounds?: GeoBounds;
|
|
89
|
+
view?: WorldMapView;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build an equirectangular projection.
|
|
94
|
+
*
|
|
95
|
+
* At `zoom: 1` the whole `bounds` box fits the frame ("contain"): the scale
|
|
96
|
+
* is `min(width/spanLng, height/spanLat)` in BOTH axes, so a container whose
|
|
97
|
+
* aspect does not match the crop letterboxes rather than stretching. A
|
|
98
|
+
* non-uniform fit would turn every marker into an ellipse and every distance
|
|
99
|
+
* into a lie, which is the opposite of what a fallback map is for.
|
|
100
|
+
*/
|
|
101
|
+
export function equirectangular({
|
|
102
|
+
width,
|
|
103
|
+
height,
|
|
104
|
+
bounds = DEFAULT_BOUNDS,
|
|
105
|
+
view = DEFAULT_VIEW,
|
|
106
|
+
}: ProjectionOptions): Projection {
|
|
107
|
+
const w = Math.max(1, width);
|
|
108
|
+
const h = Math.max(1, height);
|
|
109
|
+
const spanLng = Math.max(1e-9, bounds.east - bounds.west);
|
|
110
|
+
const spanLat = Math.max(1e-9, bounds.north - bounds.south);
|
|
111
|
+
const base = Math.min(w / spanLng, h / spanLat);
|
|
112
|
+
const zoom = clamp(Number.isFinite(view.zoom) ? view.zoom : 1, MIN_ZOOM, MAX_ZOOM);
|
|
113
|
+
const scale = base * zoom;
|
|
114
|
+
const cx = w / 2;
|
|
115
|
+
const cy = h / 2;
|
|
116
|
+
const centerLat = Number.isFinite(view.lat) ? view.lat : DEFAULT_VIEW.lat;
|
|
117
|
+
const centerLng = Number.isFinite(view.lng) ? view.lng : DEFAULT_VIEW.lng;
|
|
118
|
+
|
|
119
|
+
const project = (p: LatLng): PixelPoint => ({
|
|
120
|
+
x: cx + (p.lng - centerLng) * scale,
|
|
121
|
+
y: cy - (p.lat - centerLat) * scale,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
size: { width: w, height: h },
|
|
126
|
+
bounds,
|
|
127
|
+
view: { lat: centerLat, lng: centerLng, zoom },
|
|
128
|
+
scale,
|
|
129
|
+
project,
|
|
130
|
+
invert: (pt: PixelPoint): LatLng => ({
|
|
131
|
+
lat: centerLat - (pt.y - cy) / scale,
|
|
132
|
+
lng: centerLng + (pt.x - cx) / scale,
|
|
133
|
+
}),
|
|
134
|
+
contains: (p: LatLng): boolean => {
|
|
135
|
+
if (!isFinitePoint(p)) return false;
|
|
136
|
+
const { x, y } = project(p);
|
|
137
|
+
return x >= 0 && x <= w && y >= 0 && y <= h;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Clamp a viewport so the zoom stays in range and the map can never be
|
|
144
|
+
* dragged out of the frame: on each axis, either the whole crop is visible
|
|
145
|
+
* (then center on it) or the visible window is pinned inside the crop.
|
|
146
|
+
*/
|
|
147
|
+
export function clampView(view: WorldMapView, bounds: GeoBounds, size: PixelSize): WorldMapView {
|
|
148
|
+
const spanLng = Math.max(1e-9, bounds.east - bounds.west);
|
|
149
|
+
const spanLat = Math.max(1e-9, bounds.north - bounds.south);
|
|
150
|
+
const w = Math.max(1, size.width);
|
|
151
|
+
const h = Math.max(1, size.height);
|
|
152
|
+
const base = Math.min(w / spanLng, h / spanLat);
|
|
153
|
+
const zoom = clamp(Number.isFinite(view.zoom) ? view.zoom : 1, MIN_ZOOM, MAX_ZOOM);
|
|
154
|
+
const scale = base * zoom;
|
|
155
|
+
|
|
156
|
+
const halfLng = w / 2 / scale;
|
|
157
|
+
const halfLat = h / 2 / scale;
|
|
158
|
+
const midLng = (bounds.west + bounds.east) / 2;
|
|
159
|
+
const midLat = (bounds.south + bounds.north) / 2;
|
|
160
|
+
|
|
161
|
+
const lng = halfLng * 2 >= spanLng
|
|
162
|
+
? midLng
|
|
163
|
+
: clamp(Number.isFinite(view.lng) ? view.lng : midLng, bounds.west + halfLng, bounds.east - halfLng);
|
|
164
|
+
const lat = halfLat * 2 >= spanLat
|
|
165
|
+
? midLat
|
|
166
|
+
: clamp(Number.isFinite(view.lat) ? view.lat : midLat, bounds.south + halfLat, bounds.north - halfLat);
|
|
167
|
+
|
|
168
|
+
return { lat, lng, zoom };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface FitOptions {
|
|
172
|
+
size: PixelSize;
|
|
173
|
+
bounds?: GeoBounds;
|
|
174
|
+
/** Breathing room around the fitted box, in px. Source parity: 50. */
|
|
175
|
+
padding?: number;
|
|
176
|
+
/** Source parity: MapLibre's `fitBounds({maxZoom: 15})`, clamped here. */
|
|
177
|
+
maxZoom?: number;
|
|
178
|
+
/** Returned when there is nothing to fit; its zoom is kept for one point. */
|
|
179
|
+
fallback?: WorldMapView;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The `fitBounds()` replacement (MapLibreView.js:261-274) — used by the
|
|
184
|
+
* drill-down flow. Zero points returns `fallback` untouched; ONE point
|
|
185
|
+
* re-centers but KEEPS the current zoom, because a single-point bounding box
|
|
186
|
+
* has zero span and "fit" would divide by zero (MapLibre answered that case
|
|
187
|
+
* with a `flyTo`, same idea).
|
|
188
|
+
*/
|
|
189
|
+
export function fitViewToPoints(points: readonly LatLng[], opts: FitOptions): WorldMapView {
|
|
190
|
+
const bounds = opts.bounds ?? DEFAULT_BOUNDS;
|
|
191
|
+
const fallback = opts.fallback ?? DEFAULT_VIEW;
|
|
192
|
+
const padding = opts.padding ?? 50;
|
|
193
|
+
const maxZoom = clamp(opts.maxZoom ?? MAX_ZOOM, MIN_ZOOM, MAX_ZOOM);
|
|
194
|
+
const usable = (points ?? []).filter(isFinitePoint);
|
|
195
|
+
if (!usable.length) return fallback;
|
|
196
|
+
|
|
197
|
+
let west = Infinity;
|
|
198
|
+
let east = -Infinity;
|
|
199
|
+
let south = Infinity;
|
|
200
|
+
let north = -Infinity;
|
|
201
|
+
for (const p of usable) {
|
|
202
|
+
west = Math.min(west, p.lng);
|
|
203
|
+
east = Math.max(east, p.lng);
|
|
204
|
+
south = Math.min(south, p.lat);
|
|
205
|
+
north = Math.max(north, p.lat);
|
|
206
|
+
}
|
|
207
|
+
const center = { lat: (south + north) / 2, lng: (west + east) / 2 };
|
|
208
|
+
|
|
209
|
+
const spanLng = east - west;
|
|
210
|
+
const spanLat = north - south;
|
|
211
|
+
if (spanLng <= 0 && spanLat <= 0) {
|
|
212
|
+
return clampView({ ...center, zoom: fallback.zoom }, bounds, opts.size);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const w = Math.max(1, opts.size.width);
|
|
216
|
+
const h = Math.max(1, opts.size.height);
|
|
217
|
+
const base = Math.min(w / Math.max(1e-9, bounds.east - bounds.west), h / Math.max(1e-9, bounds.north - bounds.south));
|
|
218
|
+
const usableW = Math.max(1, w - padding * 2);
|
|
219
|
+
const usableH = Math.max(1, h - padding * 2);
|
|
220
|
+
const needed = Math.min(
|
|
221
|
+
spanLng > 0 ? usableW / spanLng : Infinity,
|
|
222
|
+
spanLat > 0 ? usableH / spanLat : Infinity,
|
|
223
|
+
);
|
|
224
|
+
const zoom = clamp(needed / base, MIN_ZOOM, maxZoom);
|
|
225
|
+
return clampView({ ...center, zoom }, bounds, opts.size);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Split a great-circle-ish segment at the antimeridian.
|
|
230
|
+
*
|
|
231
|
+
* On an equirectangular map a Tokyo→Los Angeles line drawn naively runs
|
|
232
|
+
* BACKWARDS across the entire world. When the shorter path wraps past ±180°
|
|
233
|
+
* this returns two segments — one to the edge, one from the other edge —
|
|
234
|
+
* with the latitude linearly interpolated at the crossing. Otherwise it
|
|
235
|
+
* returns the single original segment.
|
|
236
|
+
*/
|
|
237
|
+
export function splitAntimeridian(from: LatLng, to: LatLng): [LatLng, LatLng][] {
|
|
238
|
+
if (!isFinitePoint(from) || !isFinitePoint(to)) return [];
|
|
239
|
+
const dLng = to.lng - from.lng;
|
|
240
|
+
if (Math.abs(dLng) <= 180) return [[from, to]];
|
|
241
|
+
|
|
242
|
+
// The short way wraps: restate `to` on the continuous side of `from`.
|
|
243
|
+
const wrappedLng = dLng > 0 ? to.lng - 360 : to.lng + 360;
|
|
244
|
+
const edge = dLng > 0 ? -180 : 180;
|
|
245
|
+
const t = (edge - from.lng) / (wrappedLng - from.lng);
|
|
246
|
+
const lat = from.lat + (to.lat - from.lat) * t;
|
|
247
|
+
return [
|
|
248
|
+
[from, { lat, lng: edge }],
|
|
249
|
+
[{ lat, lng: -edge }, to],
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* A quadratic-bezier arc between two projected points. The control point sits
|
|
255
|
+
* perpendicular to the chord, offset proportionally to the chord's length, so
|
|
256
|
+
* short hops stay nearly straight and long ones bow. `curvature: 0` degrades
|
|
257
|
+
* to a straight line (the source's flat LineStrings).
|
|
258
|
+
*
|
|
259
|
+
* Returns '' for a zero-length or non-finite pair — the caller skips it
|
|
260
|
+
* rather than emitting `M NaN NaN`.
|
|
261
|
+
*
|
|
262
|
+
* Named `geoArcPath`, not `arcPath`: `charts/pie-math` already exports an
|
|
263
|
+
* `arcPath` (pie/doughnut segments) and both live under `portal-mojo/charts`.
|
|
264
|
+
*/
|
|
265
|
+
export function geoArcPath(projection: Projection, from: LatLng, to: LatLng, curvature = 0.22): string {
|
|
266
|
+
if (!isFinitePoint(from) || !isFinitePoint(to)) return '';
|
|
267
|
+
const a = projection.project(from);
|
|
268
|
+
const b = projection.project(to);
|
|
269
|
+
const dx = b.x - a.x;
|
|
270
|
+
const dy = b.y - a.y;
|
|
271
|
+
const len = Math.hypot(dx, dy);
|
|
272
|
+
if (!(len > 0.5)) return '';
|
|
273
|
+
const off = len * curvature;
|
|
274
|
+
// Unit normal, always to the LEFT of a→b, so parallel routes fan the
|
|
275
|
+
// same way instead of crossing each other.
|
|
276
|
+
const cxp = (a.x + b.x) / 2 + (-dy / len) * off;
|
|
277
|
+
const cyp = (a.y + b.y) / 2 + (dx / len) * off;
|
|
278
|
+
return `M${a.x.toFixed(2)},${a.y.toFixed(2)} Q${cxp.toFixed(2)},${cyp.toFixed(2)} ${b.x.toFixed(2)},${b.y.toFixed(2)}`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Graticule line positions inside `bounds`. Returns the meridians and
|
|
283
|
+
* parallels the fallback basemap draws; `0` (equator / prime meridian) is
|
|
284
|
+
* flagged so the component can stroke it with the stronger token.
|
|
285
|
+
*/
|
|
286
|
+
export function graticule(bounds: GeoBounds, lngStep = 30, latStep = 15) {
|
|
287
|
+
const meridians: number[] = [];
|
|
288
|
+
const parallels: number[] = [];
|
|
289
|
+
const first = (from: number, step: number) => Math.ceil(from / step) * step;
|
|
290
|
+
for (let lng = first(bounds.west, lngStep); lng <= bounds.east; lng += lngStep) meridians.push(lng);
|
|
291
|
+
for (let lat = first(bounds.south, latStep); lat <= bounds.north; lat += latStep) parallels.push(lat);
|
|
292
|
+
return { meridians, parallels };
|
|
293
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import type { GeoLandCollection } from './worldmap-data';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lazily load the shipped basemap geometry.
|
|
6
|
+
*
|
|
7
|
+
* `world-land.ts` is ~143KB of coordinates (~40KB gzipped). Importing it
|
|
8
|
+
* statically puts it in whatever chunk touches `portal-mojo/charts`, which in
|
|
9
|
+
* this app is the main bundle — so every page would pay for it, including the
|
|
10
|
+
* many with no map on them. The dynamic `import()` here is what actually makes
|
|
11
|
+
* Rollup emit it as its own chunk, fetched only when a map first renders.
|
|
12
|
+
*
|
|
13
|
+
* The module-level cache means the second and later maps in a session get the
|
|
14
|
+
* geometry synchronously on first render, and concurrent mounts share one
|
|
15
|
+
* request rather than racing.
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` until it resolves, which is not a special case: `WorldMap`
|
|
18
|
+
* treats a null `land` as "draw the ocean + graticule fallback", so a map is
|
|
19
|
+
* fully readable — markers, legend, tooltips, status — before the coastlines
|
|
20
|
+
* arrive, and simply gains them a moment later.
|
|
21
|
+
*/
|
|
22
|
+
let cached: GeoLandCollection | null = null;
|
|
23
|
+
let inflight: Promise<GeoLandCollection> | null = null;
|
|
24
|
+
|
|
25
|
+
export function useWorldLand(enabled = true): GeoLandCollection | null {
|
|
26
|
+
const [land, setLand] = useState<GeoLandCollection | null>(cached);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!enabled || cached) return;
|
|
30
|
+
let alive = true;
|
|
31
|
+
inflight ??= import('./world-land').then((mod) => {
|
|
32
|
+
cached = mod.WORLD_LAND;
|
|
33
|
+
return cached;
|
|
34
|
+
});
|
|
35
|
+
inflight
|
|
36
|
+
.then((loaded) => { if (alive) setLand(loaded); })
|
|
37
|
+
.catch((err) => {
|
|
38
|
+
// A failed chunk fetch must not blank the map: the fallback is a
|
|
39
|
+
// legitimate rendering, so warn and carry on rather than throw.
|
|
40
|
+
inflight = null;
|
|
41
|
+
console.warn('WorldMap: basemap geometry failed to load; drawing the graticule fallback', err);
|
|
42
|
+
});
|
|
43
|
+
return () => { alive = false; };
|
|
44
|
+
}, [enabled]);
|
|
45
|
+
|
|
46
|
+
return enabled ? land : null;
|
|
47
|
+
}
|