@bicharts/chart-host 0.1.6 → 0.1.8
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.
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/contract.ts", "../src/defaults.ts", "../src/geoLazy.ts", "../../shape-core/src/geoPointTables.generated.ts", "../../shape-core/src/geoPoint.ts", "../../shape-core/src/geoDetector.ts", "../../shape-core/src/geoPointRoles.ts", "../src/payload.ts", "../src/selection.ts", "../src/host.ts"],
|
|
4
|
-
"sourcesContent": ["// @bicharts/chart-host \u2014 the HOST CONTRACT the generated D3 code targets.\n//\n// Single source of truth for: the render options surface, the interaction grammar\n// (mark classes, the cross-filter event, container lifecycle slots), and the\n// option defaults/clamps. Shared by the Power BI visual (which assembles\n// options through resolveOptions) and any other host (MCP preview/contract, SDK),\n// so a knob's default lives in ONE place instead of drifting across the visual, the\n// archetype `||` fallbacks, and the MCP prose.\n//\n// Types and defaults only; the runtime (createChartHost) and the selection extraction\n// live alongside them.\n// The generated code is unchanged \u2014 this describes what it already expects.\n\n// Bump when the contract changes in a way a host must adapt to (semver). Stamped\n// into the MCP integration contract so a caller can pin behavior.\nexport const HOST_CONTRACT_VERSION = \"1.0.0\";\n\n// The coarsest geocoding tier a point map used. Declared HERE, not imported from\n// @bicharts/shape-core, because this type is part of chart-host's PUBLIC surface: shape-core\n// is a build-time devDependency (its code is bundled in), so a consumer never installs it and\n// a `.d.ts` importing from it would dangle. That bug shipped in the first packed build and\n// went unnoticed because the test app had `skipLibCheck: true`, which skips .d.ts checking\n// entirely. The two declarations are kept identical by a test in this package.\nexport type GeoPointPrecision = \"latlon\" | \"city\" | \"zip3\" | \"state\";\n/** Every valid tier, ordered most precise \u2192 coarsest. Runtime form of GeoPointPrecision. */\nexport const GEO_POINT_PRECISIONS: readonly GeoPointPrecision[] =\n [\"latlon\", \"city\", \"zip3\", \"state\"] as const;\n\n// ---- Interaction grammar (what generated code emits; the host binds to these) ----\n\n// CSS classes the generated code stamps on marks.\nexport const MARK_CLASS = \"d3-mark\"; // a data mark\nexport const LEGEND_MARK_CLASS = \"d3-legend-mark\"; // a legend swatch (also filterable)\nexport const AXIS_FILTER_CLASS = \"d3-axis-filter\"; // an axis/scrubber tick (union of row idxs)\n\n// The attribute each filterable mark carries: comma-joined __rowIdx__ values,\n// frame-scoped for animated charts.\nexport const ROW_IDX_ATTR = \"data-row-idx\";\n\n// Bubbling CustomEvent a chart dispatches on its container when its own selection\n// changes (e.g. a scrubber period). detail = { clear:true, source } | { mark, source }.\nexport const XFILTER_REFRESH_EVENT = \"llm-xfilter-refresh\";\n\n// Lifecycle slots the generated code sets on the container; the host calls/reads them.\nexport const CONTAINER_SLOT_ANIM_STOP = \"__llmAnimStop\"; // call before unmount/re-render\nexport const CONTAINER_SLOT_XF_CLEAR = \"__llmXfClear\"; // call to clear the chart's selection\nexport const CONTAINER_SLOT_INITIAL_XF_MARK = \"__llmInitialXfMark\"; // a mark to apply as the initial filter\n\n// ---- Selection AFFORDANCE (what a selection LOOKS like) ----\n//\n// Generated code emits marks; it never styles the selected state \u2014 that is the host's\n// job, because only the host knows what is selected. The Power BI visual has always\n// done this by toggling two classes and dimming the rest through a CSS custom property\n// (the visual injects the rules and sets the property). These\n// constants name that SAME grammar so any other host produces the same affordance\n// instead of inventing one \u2014 a chart should not look different because of where it runs.\n// A drift test on the visual's side asserts these literals still match its CSS.\nexport const HOST_CONTAINER_CLASS = \"bic-chart-host\"; // stamped on every hosted container\nexport const SELECTION_ACTIVE_CLASS = \"lch-has-selection\"; // on the container while a selection exists\nexport const MARK_SELECTED_CLASS = \"lch-mark-selected\"; // on each mark in the selection\nexport const DIM_OPACITY_VAR = \"--lch-dim-opacity\"; // unselected-mark opacity (visual default 0.25)\nexport const DIM_OPACITY_DEFAULT = 0.25;\n\n// ---- Option defaults / clamps (mirrored by the archetype `||` fallbacks) ----\nexport const ANIM_PLAY_SPEED_DEFAULT = 1000;\nexport const ANIM_PLAY_SPEED_MIN = 250;\nexport const ANIM_PLAY_SPEED_MAX = 5000;\nexport const ANIM_MAX_IDEAL_FRAMES_DEFAULT = 60;\nexport const ANIM_MAX_IDEAL_FRAMES_MIN = 3;\nexport const ANIM_MAX_IDEAL_FRAMES_MAX = 500;\n// Outlier clamp for colorScaleScope=\"self\": the diverging domain's half-width is the\n// Nth percentile of |relative change| across all region-frame pairs (not the raw max),\n// so one runaway region can't flatten everyone else's contrast to nothing. Deliberately a\n// SETTING rather than a hardcoded 95 (2026-07-25): a percentile is a judgement about the\n// data, and the person looking at the data is better placed to make it than we are.\n// Floored at a minimum spread in resolveOptions so a near-flat dataset doesn't amplify\n// noise to full saturation.\nexport const COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT = 95;\nexport const COLOR_SCALE_SELF_CLAMP_PCT_MIN = 50;\nexport const COLOR_SCALE_SELF_CLAMP_PCT_MAX = 100;\n\nexport type TimelineStyle = \"\" | \"line\" | \"boxes\";\n// Colour Scale Scope for animated continuous scales: \"\" = ONE global domain over\n// every period (default; a colour means the same value in every frame); \"frame\" =\n// re-scale each keyframe to its own min-max (in-frame contrast lens; the chart must\n// redraw + relabel the colorbar per frame); \"self\" = diverging change-vs-own-average\n// (2026-07-25 \u2014 encodes MOVEMENT rather\n// than magnitude: each region's colour is its relative deviation from ITS OWN mean\n// across all periods, so a small region's big swing reads as strongly as a large\n// region's). Unknown values fail open to global.\nexport type ColorScaleScope = \"\" | \"frame\" | \"self\";\n\n// The object handed to a generated render(container, data, options). Host-computed\n// fields (geometry, theme, closures) are supplied by the caller; the knob fields\n// carry the defaults/clamps applied by resolveOptions.\nexport interface RenderOptions {\n width: number;\n height: number;\n palette: string[];\n // Geo choropleths: {count, examples} of unresolved regions; the geometry\n // FeatureCollection (feature.id = the __geoIso__ join code).\n geoUnmatched?: { count: number; examples: string[] } | null;\n geo?: any;\n // POINT maps drawing from resolved place names (__geoLat__/__geoLon__ present).\n // `precision` is the COARSEST tier any row needed \u2014 \"latlon\" (real coordinates),\n // \"city\", \"zip3\" (a whole 3-digit ZIP prefix) or \"state\" (a whole state/province) \u2014\n // so the chart can say what it actually plotted rather than implying exact positions.\n // `unplaced` feeds the off-map annotation; `ambiguousRows` counts rows placed by the\n // largest-match tie-break on a colliding bare city name. Null on every other chart.\n // `rolesBackfilled` / `rolesRefused` report what the ROLE resolution did to the\n // binding it was handed. The codegen response names which column is the city, the\n // state and the ZIP, and that answer is verified rather than trusted: a role it omits\n // is filled in from the values, and one it names wrongly is dropped. Both are ABSENT\n // when nothing was adjusted, so presence means \"this binding is not what was asked\n // for\" \u2014 worth a line on the chart, because it changes how the points were placed.\n geoPoint?: {\n precision: GeoPointPrecision | null;\n unplaced: number;\n unplacedExamples: string[];\n ambiguousRows: number;\n rolesBackfilled?: string[];\n rolesRefused?: string[];\n } | null;\n // Point-map mark-count ceiling (Format > Data \"Max Map Points\", default 1000).\n // The eligibility gate refuses to OFFER a point-map type above this many rows, but\n // a CACHED chart's row count is fixed at generation time while this dial can be\n // lowered afterward with no regeneration \u2014 so the chart itself must cap what it\n // draws and say so, live, every render. Undefined = no cap known (older client).\n maxMapPoints?: number;\n // Live-restyle knobs (changing any of these = re-render, never a regeneration).\n colorScaleLow?: string;\n colorScaleHigh?: string;\n colorScaleScope?: ColorScaleScope;\n // Outlier clamp (percentile, 50-100) for colorScaleScope=\"self\" only; ignored by\n // the other two scopes. Default 95 \u2014 see COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT.\n colorScaleSelfClampPct?: number;\n // Map fills. geoLandColor = the basemap landmass (and, on a choropleth, everywhere the\n // data doesn't reach); geoNoDataColor = regions in the data's geography that have no value\n // for what's currently shown, defaulting to geoLandColor so they're indistinguishable\n // unless deliberately set apart. BOTH are already high-contrast-resolved by the caller \u2014\n // in HC the host substitutes the reserved background slot, so generated code consumes\n // these directly and never branches on isHighContrast (same contract as themeAccent).\n geoLandColor?: string;\n geoNoDataColor?: string;\n aggregation?: string;\n // Persisted view-state (animated charts persist {frame, selIdx}).\n uiState?: unknown;\n setUiState?: (s: unknown) => void;\n backgroundColor?: string;\n // Host theme (report theme canvas/text + high-contrast-safe accent).\n themeBg?: string;\n themeFg?: string;\n isHighContrast?: boolean;\n themeAccent?: string;\n cultureCode?: string;\n allowTooltips?: boolean;\n noDataText?: string;\n // Animation.\n animAutoPlay?: boolean;\n animPlaySpeedMs?: number;\n animMaxIdealFrames?: number;\n animStopAtEnd?: boolean;\n filtersDuringPlay?: boolean;\n animTimelineStyle?: TimelineStyle;\n // Optional selection callback some non-animated charts invoke (the DOM event +\n // data-row-idx bridge is the primary mechanism; see the interaction grammar).\n onSelect?: (rowIdxs: number[]) => void;\n}\n", "// resolveOptions \u2014 the ONE place the render-option defaults/clamps live.\n//\n// It reproduces EXACTLY the normalization that was inline in the visual's option\n// assembly, so moving the assembly onto it is behavior-identical (locked by\n// tests/defaults.test.ts, which checks each field against the original expression).\n//\n// Split of responsibility:\n// \u2022 NORMALIZED knobs (animation / colour-scale / aggregation): the caller passes\n// the RAW setting value; resolveOptions applies the default + clamp.\n// \u2022 HOST fields (width/height/palette/geo/theme/cultureCode/allowTooltips/\n// noDataText/uiState/setUiState/onSelect): the caller has already computed the\n// final value; resolveOptions passes it through untouched.\nimport {\n type RenderOptions,\n ANIM_PLAY_SPEED_DEFAULT, ANIM_PLAY_SPEED_MIN, ANIM_PLAY_SPEED_MAX,\n ANIM_MAX_IDEAL_FRAMES_DEFAULT, ANIM_MAX_IDEAL_FRAMES_MIN, ANIM_MAX_IDEAL_FRAMES_MAX,\n COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT, COLOR_SCALE_SELF_CLAMP_PCT_MIN, COLOR_SCALE_SELF_CLAMP_PCT_MAX,\n} from \"./contract\";\n\n// Raw input: every field optional/loose (the knobs arrive as raw setting values).\nexport type ResolveOptionsInput = { [K in keyof RenderOptions]?: any };\n\nconst clamp = (n: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, n));\n\nexport function resolveOptions(p: ResolveOptionsInput): RenderOptions {\n return {\n // ---- host fields: pass through exactly as supplied ----\n width: p.width,\n height: p.height,\n palette: p.palette,\n geoUnmatched: p.geoUnmatched,\n geo: p.geo,\n geoPoint: p.geoPoint,\n maxMapPoints: p.maxMapPoints,\n uiState: p.uiState,\n setUiState: p.setUiState,\n backgroundColor: p.backgroundColor,\n themeBg: p.themeBg,\n themeFg: p.themeFg,\n isHighContrast: p.isHighContrast,\n themeAccent: p.themeAccent,\n cultureCode: p.cultureCode,\n allowTooltips: p.allowTooltips,\n noDataText: p.noDataText,\n onSelect: p.onSelect,\n\n // ---- live-restyle knobs: `raw || undefined` / `.toString() || undefined` ----\n colorScaleLow: p.colorScaleLow || undefined,\n colorScaleHigh: p.colorScaleHigh || undefined,\n // \"\" (global, the default), \"frame\", or \"self\"; anything else fails open to global.\n colorScaleScope: (p.colorScaleScope === \"frame\" || p.colorScaleScope === \"self\" ? p.colorScaleScope : \"\") as RenderOptions[\"colorScaleScope\"],\n // Math.max(50, Math.min(100, Number(raw) || 95)) \u2014 only meaningful under \"self\".\n colorScaleSelfClampPct: clamp(Number(p.colorScaleSelfClampPct) || COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT, COLOR_SCALE_SELF_CLAMP_PCT_MIN, COLOR_SCALE_SELF_CLAMP_PCT_MAX),\n // Map fills: `raw || undefined` like the colour-scale endpoints. The no-data ->\n // land cascade is left to the chart (so the fallback chain is visible in the\n // generated code); HC substitution has already happened in the caller.\n geoLandColor: p.geoLandColor || undefined,\n geoNoDataColor: p.geoNoDataColor || undefined,\n aggregation: (p.aggregation == null ? \"\" : String(p.aggregation)) || undefined,\n\n // ---- animation: booleans, default+clamp numerics, \"\" default enum ----\n animAutoPlay: !!p.animAutoPlay,\n // Math.max(250, Math.min(5000, Number(raw) || 1000))\n animPlaySpeedMs: clamp(Number(p.animPlaySpeedMs) || ANIM_PLAY_SPEED_DEFAULT, ANIM_PLAY_SPEED_MIN, ANIM_PLAY_SPEED_MAX),\n // Math.max(3, Math.min(500, Number(raw) || 60))\n animMaxIdealFrames: clamp(Number(p.animMaxIdealFrames) || ANIM_MAX_IDEAL_FRAMES_DEFAULT, ANIM_MAX_IDEAL_FRAMES_MIN, ANIM_MAX_IDEAL_FRAMES_MAX),\n animStopAtEnd: !!p.animStopAtEnd,\n filtersDuringPlay: !!p.filtersDuringPlay,\n animTimelineStyle: (p.animTimelineStyle || \"\").toString() as RenderOptions[\"animTimelineStyle\"],\n };\n}\n", "// LAZY geometry \u2014 the split half of GAP-11.\n//\n// `geo.ts` STATICALLY imports all three generated assets (world-110m 198 KB + us-states\n// 250 KB + us-zip3 810 KB). That is right for the Power BI visual, which ships offline and\n// air-gapped where the assets ARE the product. It is wrong for a web SDK: `createChartHost`\n// imported geoForKind, so every consumer paid ~1.3 MB raw to render a bar chart. Measured\n// on the first real build: a 2 KB entry pulling a 1,423 KB shared chunk.\n//\n// This module holds NO static asset import, so the runtime entry stays lean. A host that\n// wants bundled geometry either awaits loadGeo() (per-asset dynamic import \u2192 its own chunk,\n// which any bundler can split and any CDN can cache separately) or imports the sync\n// `geoForKind` from the \"@bicharts/chart-host/geo\" subpath and passes options.geo itself.\n//\n// The cache is module-level and shared with the sync path's result shape, so a chart\n// rendered after loadGeo() resolves gets the geometry with no further work from the caller.\nconst cache = new Map<string, any>();\n\n/** Normalise a server geoKind to the ASSET it needs \u2014 the one place that mapping lives. */\nfunction assetFor(geoKind: string): \"world\" | \"us-states\" | \"us-zip3\" | null {\n const k = geoKind.toLowerCase();\n if (/^country/.test(k)) return \"world\";\n if (k === \"north-america\") return \"world\"; // derived from the world asset\n if (k === \"us-state-code\" || k === \"us-state-name\") return \"us-states\";\n if (k === \"us-zip5\") return \"us-zip3\";\n return null;\n}\n\n/**\n * Load (and cache) the geometry for a geoKind. Per-asset dynamic import: requesting a US\n * state map never downloads the 810 KB zip3 asset. Resolves to undefined for an unknown\n * kind \u2014 callers treat that exactly like \"no geometry\", same as the sync path.\n */\nexport async function loadGeo(geoKind: string | null | undefined): Promise<any | undefined> {\n if (!geoKind) return undefined;\n const key = geoKind.toLowerCase();\n if (cache.has(key)) return cache.get(key);\n const asset = assetFor(key);\n if (!asset) return undefined;\n let geo: any;\n if (asset === \"us-states\") {\n geo = (await import(\"./geoUsStates.generated\")).US_STATES;\n } else if (asset === \"us-zip3\") {\n geo = (await import(\"./geoUsZip3.generated\")).US_ZIP3;\n } else {\n const { WORLD_110M } = await import(\"./geoWorld110m.generated\");\n if (key === \"north-america\") {\n // Same derivation as geo.ts's northAmerica(), kept in geoNaRegions (which carries\n // no geometry) so this module stays asset-free until the dynamic import runs.\n const { NA_ISO3, NA_LAT_MAX, clipFeatureToLat } = await import(\"./geoNaRegions\");\n geo = {\n type: \"FeatureCollection\",\n features: (WORLD_110M.features as any[])\n .filter((f: any) => NA_ISO3.has(f?.id) && f?.geometry && f.geometry.type !== \"Point\")\n .map((f: any) => clipFeatureToLat(f, NA_LAT_MAX))\n .filter((f: any) => f !== null),\n };\n } else {\n geo = WORLD_110M;\n }\n }\n cache.set(key, geo);\n return geo;\n}\n\n/** Synchronous cache read \u2014 what createChartHost uses, so render() stays sync. Returns\n * undefined until loadGeo() has resolved for this kind (or a provider supplied it). */\nexport function geoFromCache(geoKind: string | null | undefined): any | undefined {\n return geoKind ? cache.get(geoKind.toLowerCase()) : undefined;\n}\n\n/** Seed the cache from geometry the caller already holds \u2014 e.g. the MCP's data.geo.json,\n * or the sync geoForKind from the \"@bicharts/chart-host/geo\" subpath. */\nexport function registerGeo(geoKind: string, geo: any): void {\n if (geoKind && geo) cache.set(geoKind.toLowerCase(), geo);\n}\n\r\n", "// AUTO-GENERATED by tools/geo_build_points.py. DO NOT EDIT.\n//\n// Point-geocode tables for the resolution cascade in geoPoint.ts:\n// City+State -> ZIP5/ZIP3 -> State/Province\n//\n// CITY \u2014 GeoNames cities15000, US/CA/MX, population >= 25,000.\n// *** GeoNames is CC BY 4.0 \u2014 ATTRIBUTION IS REQUIRED. ***\n// See geo/README.md; the credit also ships in the visual's help text.\n// Fields: asciiName|admin1|lon|lat|popThousands (population is ONLY the\n// tie-break for an ambiguous bare city name \u2014 9% of NA city names collide.)\n// ADMIN1 \u2014 population-WEIGHTED centroid per state/province: US states + DC, Canadian\n// provinces/territories, Mexican states. Population-weighted on purpose \u2014 a\n// geometric centroid puts Alaska in empty tundra and Quebec in the subarctic.\n// ZIP3 \u2014 area centroid of each 3-digit ZIP prefix, derived from the ALREADY-BUNDLED\n// geo/us-zip3-20m.v1.json (US Census/TIGER, public domain). No new source data,\n// no new licence. ZIP-5 resolves via its 3-digit prefix \u2014 the honest precision\n// limit of the geometry we ship (callers get precision=\"zip3\").\n//\n// Coordinates are 2 dp (~1.1 km), far finer than a bubble dot resolves.\n/* eslint-disable */\n\nexport const CITY_PACKED = \"Abasolo|MX11|-101.53|20.45|27,Abbotsford|BC|-122.25|49.06|141,Aberdeen|SD|-98.49|45.46|28,Abilene|TX|-99.73|32.45|125,Abington|PA|-75.12|40.12|55,Acambaro|MX11|-100.72|20.03|57,Acapulco de Juarez|MX12|-99.91|16.85|658,Acatzingo|MX21|-97.78|18.98|25,Acayucan|MX30|-94.91|17.95|50,Actopan|MX13|-98.94|20.27|32,Addison|IL|-87.99|41.93|37,Adelanto|CA|-117.41|34.58|33,Agawam|MA|-72.61|42.07|28,Agincourt North|ON|-79.28|43.8|29,Agua Caliente|CA|-122.49|38.32|27,Agua Dulce|MX30|-94.15|18.14|36,Agua Prieta|MX26|-109.54|31.32|77,Aguascalientes|MX01|-102.28|21.88|722,Ahuntsic-Cartierville|QC|-73.67|45.57|135,Ahwatukee Foothills|AZ|-111.98|33.34|83,Aiken|SC|-81.72|33.56|30,Airdrie|AB|-114.04|51.3|90,Airport|HI|-157.93|21.34|28,Ajalpan|MX21|-97.26|18.38|28,Ajax|ON|-79.03|43.85|119,Akron|OH|-81.52|41.08|197,Alabaster|AL|-86.82|33.24|32,Alafaya|FL|-81.21|28.56|78,Alameda|CA|-122.26|37.77|78,Alamo|MX30|-97.68|20.91|25,Alamogordo|NM|-105.96|32.9|30,Albany|NY|-73.76|42.65|101,Albany|GA|-84.16|31.58|74,Albany|OR|-123.11|44.64|52,Albany Park|IL|-87.72|41.97|52,Albuquerque|NM|-106.65|35.08|564,Alexandria|VA|-77.05|38.8|159,Alexandria|LA|-92.45|31.31|47,Algonquin|IL|-88.29|42.17|30,Alhambra|AZ|-112.13|33.5|127,Alhambra|CA|-118.13|34.1|85,Aliamanu / Salt Lakes / Foster Village|HI|-157.92|21.36|38,Alief|TX|-95.6|29.71|98,Aliso Viejo|CA|-117.73|33.57|50,Allapattah|FL|-80.22|25.81|54,Allen|TX|-96.67|33.1|98,Allen Park|MI|-83.21|42.26|27,Allentown|PA|-75.49|40.61|120,Allston|MA|-71.13|42.36|28,Alma|QC|-71.65|48.55|29,Aloha|OR|-122.87|45.49|49,Alpharetta|GA|-84.29|34.08|63,Altadena|CA|-118.13|34.19|42,Altamira|MX28|-97.94|22.39|59,Altamonte Springs|FL|-81.37|28.66|43,Alton|IL|-90.18|38.89|27,Altoona|PA|-78.39|40.52|45,Alvaro Obregon|MX09|-99.2|19.36|726,Alvin|TX|-95.24|29.42|25,Amarillo|TX|-101.83|35.22|198,Ameca|MX14|-104.05|20.55|36,Amecameca|MX15|-98.77|19.12|31,American Fork|UT|-111.8|40.38|28,Ames|IA|-93.62|42.03|65,Amherst|NY|-78.8|42.98|122,Amherst|MA|-72.52|42.37|39,Amozoc de Mota|MX21|-98.05|19.05|95,Anaheim|CA|-117.91|33.84|350,Ancaster|ON|-79.99|43.22|40,Anchorage|AK|-149.9|61.22|289,Anderson|IN|-85.68|40.11|55,Anderson|SC|-82.65|34.5|27,Andover|MN|-93.29|45.23|32,Ankeny|IA|-93.61|41.73|56,Ann Arbor|MI|-83.74|42.28|117,Annandale|VA|-77.2|38.83|41,Annapolis|MD|-76.49|38.98|40,Annex|ON|-79.4|43.67|30,Antelope|CA|-121.33|38.71|45,Antioch|CA|-121.81|38|110,Apache Junction|AZ|-111.55|33.42|38,Apan|MX13|-98.45|19.71|26,Apaseo el Alto|MX11|-100.62|20.46|27,Apaseo el Grande|MX11|-100.69|20.55|26,Apatzingan|MX16|-102.36|19.09|102,Apex|NC|-78.85|35.73|45,Apizaco|MX29|-98.14|19.41|49,Apopka|FL|-81.51|28.68|48,Apple Valley|CA|-117.19|34.5|72,Apple Valley|MN|-93.22|44.73|51,Appleton|WI|-88.42|44.26|74,Arandas|MX14|-102.35|20.71|52,Arcadia|CA|-118.04|34.14|58,Arcola East|SK|-104.54|50.43|33,Arden-Arcade|CA|-121.38|38.6|92,Ardmore|OK|-97.14|34.17|25,Arlington|TX|-97.11|32.74|388,Arlington|VA|-77.1|38.88|207,Arlington|MA|-71.16|42.42|42,Arlington Heights|IL|-87.98|42.09|75,Arvada|CO|-105.09|39.8|115,Ashburn|VA|-77.49|39.04|43,Ashburn|IL|-87.71|41.75|42,Asheboro|NC|-79.81|35.71|26,Asheville|NC|-82.55|35.6|95,Ashmont|MA|-71.07|42.28|30,Aspen Hill|MD|-77.07|39.08|48,Astoria|NY|-73.93|40.77|150,Atascadero|CA|-120.67|35.49|29,Atascocita|TX|-95.18|30|65,Athens|GA|-83.38|33.96|127,Athens|OH|-82.1|39.33|25,Atlanta|GA|-84.39|33.75|510,Atlantic City|NJ|-74.42|39.36|39,Atlixco|MX21|-98.44|18.91|86,Atotonilco el Alto|MX14|-102.51|20.55|26,Attleboro|MA|-71.29|41.94|44,Atwater|CA|-120.61|37.35|29,Auburn|WA|-122.23|47.31|77,Auburn|AL|-85.48|32.61|62,Auburn|NY|-76.57|42.93|26,Auburn Gresham|IL|-87.65|41.74|45,Augusta|GA|-81.97|33.47|43,Aurora|CO|-104.83|39.73|359,Aurora|IL|-88.32|41.76|200,Aurora|ON|-79.47|44|55,Austin|TX|-97.74|30.27|974,Austintown|OH|-80.76|41.1|29,Autlan de Navarro|MX14|-104.37|19.77|45,Aventura|FL|-80.14|25.96|37,Avondale|AZ|-112.35|33.44|80,Avondale|IL|-87.71|41.94|39,Aylmer|QC|-75.81|45.4|56,Azcapotzalco|MX09|-99.19|19.49|414,Azusa|CA|-117.91|34.13|49,Back Mountain|PA|-76|41.34|26,Bakersfield|CA|-119.02|35.37|373,Balch Springs|TX|-96.62|32.73|25,Baldwin Park|CA|-117.96|34.09|77,Ballwin|MO|-90.55|38.6|30,Baltimore|MD|-76.61|39.29|585,Banbury-Don Mills|ON|-79.35|43.74|27,Bangor|ME|-68.77|44.8|32,Banning|CA|-116.88|33.93|30,Barberton|OH|-81.61|41.01|26,Barnstable|MA|-70.3|41.7|47,Barrie|ON|-79.67|44.4|147,Bartlesville|OK|-95.98|36.75|36,Bartlett|TN|-89.87|35.2|58,Bartlett|IL|-88.19|42|41,Batavia|IL|-88.31|41.85|26,Bath Beach|NY|-74|40.6|33,Baton Rouge|LA|-91.19|30.44|227,Battle Creek|MI|-85.18|42.32|51,Bay City|MI|-83.89|43.59|33,Bay Shore|NY|-73.25|40.73|26,Bay Street Corridor|ON|-79.39|43.66|25,Bayonne|NJ|-74.11|40.67|66,Bayside|NY|-73.78|40.77|66,Baytown|TX|-94.98|29.74|76,Bayview-Hunters Point|CA|-122.38|37.73|34,Beaumont|TX|-94.1|30.09|115,Beaumont|CA|-116.98|33.93|43,Beauport|QC|-71.19|46.86|81,Beavercreek|OH|-84.06|39.71|46,Beaverton|OR|-122.8|45.49|96,Bedford|TX|-97.14|32.84|49,Bel Air North|MD|-76.37|39.55|30,Bel Air South|MD|-76.32|39.51|47,Bell|CA|-118.19|33.98|36,Bell Gardens|CA|-118.15|33.97|43,Bella Vista|AR|-94.27|36.48|27,Belleville|ON|-77.38|44.17|50,Belleville|IL|-89.98|38.52|42,Belleville|NJ|-74.15|40.79|36,Bellevue|WA|-122.2|47.61|139,Bellevue|NE|-95.89|41.14|55,Bellflower|CA|-118.12|33.88|78,Bellingham|WA|-122.49|48.76|85,Belmont|CA|-122.28|37.52|27,Belmont Cragin|IL|-87.77|41.93|79,Beloit|WI|-89.03|42.51|36,Beltline|AB|-114.07|51.04|25,Belvidere|IL|-88.84|42.26|25,Bend|OR|-121.32|44.06|87,Bendale|ON|-79.25|43.77|29,Benicia|CA|-122.16|38.05|28,Benito Juarez|MX09|-99.16|19.37|385,Benito Juarez|MX09|-99.16|19.4|355,Bensalem|PA|-74.95|40.1|60,Bensonhurst|NY|-73.99|40.6|60,Benton|AR|-92.59|34.56|34,Bentonville|AR|-94.21|36.37|44,Bergenfield|NJ|-74|40.93|27,Berkeley|CA|-122.27|37.87|120,Berriozabal|MX05|-93.27|16.8|28,Berwyn|IL|-87.79|41.85|56,Bessemer|AL|-86.95|33.4|26,Bethel Park|PA|-80.04|40.33|32,Bethesda|MD|-77.1|38.98|60,Bethlehem|PA|-75.37|40.63|74,Bettendorf|IA|-90.52|41.52|35,Beverly|MA|-70.88|42.56|41,Beverly Cove|MA|-70.85|42.55|40,Beverly Hills|CA|-118.4|34.07|34,Big Spring|TX|-101.48|32.25|28,Billerica|MA|-71.27|42.56|39,Billings|MT|-108.5|45.78|117,Biloxi|MS|-88.89|30.4|45,Binghamton|NY|-75.92|42.1|46,Birmingham|AL|-86.8|33.52|196,Bismarck|ND|-100.78|46.81|75,Blacksburg|VA|-80.41|37.23|44,Blaine|MN|-93.23|45.16|62,Blainville|QC|-73.88|45.67|46,Bloomfield|NJ|-74.19|40.81|49,Bloomington|MN|-93.3|44.84|86,Bloomington|IN|-86.53|39.17|84,Bloomington|IL|-88.99|40.48|78,Blue Springs|MO|-94.28|39.02|54,Boardman|OH|-80.66|41.02|35,Boca Raton|FL|-80.08|26.36|93,Boisbriand|QC|-73.83|45.62|26,Boise|ID|-116.2|43.61|235,Bolingbrook|IL|-88.07|41.7|74,Bolton|ON|-79.74|43.88|26,Bonita Springs|FL|-81.78|26.34|51,Borough Park|NY|-74|40.63|149,Bossier City|LA|-93.73|32.52|68,Boston|MA|-71.06|42.36|653,Bothell|WA|-122.21|47.76|42,Boucherville|QC|-73.44|45.59|39,Boulder|CO|-105.27|40.01|106,Bountiful|UT|-111.88|40.89|43,Bowie|MD|-76.73|38.94|58,Bowling Green|KY|-86.44|36.99|63,Bowling Green|OH|-83.65|41.37|31,Bowmanville|ON|-78.68|43.92|39,Boyle Heights|CA|-118.21|34.03|92,Boynton Beach|FL|-80.07|26.53|73,Bozeman|MT|-111.04|45.68|43,Bradenton|FL|-82.57|27.5|54,Braintree|MA|-71|42.2|37,Brampton|ON|-79.77|43.68|656,Brandon|FL|-82.29|27.94|103,Brandon|MB|-99.95|49.85|48,Branford|CT|-72.82|41.28|29,Brant|ON|-80.35|43.13|34,Brantford|ON|-80.27|43.13|104,Brawley|CA|-115.53|32.98|25,Brea|CA|-117.9|33.92|41,Bremerton|WA|-122.63|47.57|39,Brentwood|NY|-73.25|40.78|60,Brentwood|CA|-121.7|37.93|58,Brentwood|TN|-86.78|36.03|41,Brentwood|CA|-118.47|34.05|33,Brentwood Estates|TN|-86.78|36.03|31,Briarwood|NY|-73.82|40.71|53,Brick|NJ|-74.14|40.06|76,Bridgeport|CT|-73.19|41.18|147,Bridgeport|IL|-87.65|41.84|33,Bridgeton|NJ|-75.23|39.43|25,Bridgewater|NJ|-74.65|40.6|44,Brighouse-City Centre|BC|-123.13|49.17|62,Brighton|MA|-71.16|42.35|45,Brighton|CO|-104.82|39.99|37,Brighton|NY|-77.55|43.15|36,Brighton Beach|NY|-73.96|40.58|31,Brighton Park|IL|-87.7|41.82|44,Bristol|CT|-72.95|41.67|60,Bristol|TN|-82.19|36.6|26,Brockton|MA|-71.02|42.08|95,Broken Arrow|OK|-95.79|36.05|106,Brookfield|WI|-88.11|43.06|38,Brookhaven|GA|-84.34|33.86|51,Brooklin|ON|-78.96|43.96|25,Brookline|MA|-71.12|42.33|58,Brooklyn|NY|-73.95|40.65|2736,Brooklyn Center|MN|-93.33|45.08|30,Brooklyn Park|MN|-93.36|45.09|79,Broomfield|CO|-105.09|39.92|65,Brossard|QC|-73.47|45.45|69,Brownsville|TX|-97.5|25.9|186,Brownsville|NY|-73.92|40.66|74,Brunswick|OH|-81.84|41.24|34,Bryan|TX|-96.37|30.67|82,Buckeye|AZ|-112.58|33.37|50,Buena Park|CA|-118|33.87|83,Buenaventura Lakes|FL|-81.35|28.34|26,Buenavista|MX15|-99.17|19.61|216,Buffalo|NY|-78.88|42.89|258,Buffalo Grove|IL|-87.96|42.15|41,Bullhead City|AZ|-114.57|35.15|39,Burbank|CA|-118.31|34.18|105,Burbank|IL|-87.78|41.73|29,Burien|WA|-122.35|47.47|50,Burke|VA|-77.27|38.79|41,Burleson|TX|-97.32|32.54|43,Burlingame|CA|-122.37|37.58|30,Burlington|ON|-79.84|43.39|186,Burlington|NC|-79.44|36.1|52,Burlington|VT|-73.21|44.48|42,Burlington|IA|-91.11|40.81|25,Burnaby|BC|-122.95|49.27|249,Burnsville|MN|-93.28|44.77|61,Burton|MI|-83.62|43|28,Bushwick|NY|-73.92|40.69|112,Bustleton|PA|-75.03|40.08|32,Butte|MT|-112.53|46|34,Cabo San Lucas|MX03|-109.91|22.89|202,Cabot|AR|-92.02|34.97|25,Cadereyta|MX19|-99.98|25.58|67,Cadereyta Jimenez|MX19|-100|25.59|68,Caldwell|ID|-116.69|43.66|51,Caledon|ON|-79.99|43.87|76,Calexico|CA|-115.5|32.68|40,Calgary|AB|-114.09|51.05|1306,Calpulalpan|MX29|-98.57|19.59|33,Calumet City|IL|-87.53|41.62|37,Camarillo|CA|-119.04|34.22|67,Cambridge|ON|-80.31|43.36|129,Cambridge|MA|-71.11|42.38|110,Camden|NJ|-75.12|39.93|76,Campbell|CA|-121.95|37.29|41,Campbell River|BC|-125.24|50.02|33,Campeche|MX04|-90.52|19.84|220,Cananea|MX26|-110.3|30.98|31,Canarsie|NY|-73.9|40.64|87,Cancun|MX23|-86.85|21.17|888,Canoga Park|CA|-118.6|34.2|60,Canton|MI|-83.48|42.31|86,Canton|OH|-81.38|40.8|71,Canton|GA|-84.49|34.24|25,Cantonment|FL|-87.34|30.61|26,Canyon Country|CA|-118.47|34.42|59,Cape Coral|FL|-81.95|26.56|175,Cape Girardeau|MO|-89.52|37.31|39,Carbondale|IL|-89.22|37.73|26,Cardenas|MX27|-93.38|18|91,Carlsbad|CA|-117.35|33.16|114,Carlsbad|NM|-104.23|32.42|28,Carmel|IN|-86.12|39.98|88,Carmichael|CA|-121.33|38.62|61,Carney|MD|-76.52|39.39|29,Carol City|FL|-80.25|25.94|63,Carol Stream|IL|-88.13|41.91|40,Carpentersville|IL|-88.26|42.12|38,Carrollton|TX|-96.89|32.95|133,Carrollton|GA|-85.08|33.58|26,Carrollwood|FL|-82.49|28.05|33,Carrollwood Village|FL|-82.52|28.07|40,Carson|CA|-118.28|33.83|93,Carson City|NV|-119.77|39.16|58,Cartierville|QC|-73.7|45.53|34,Cary|NC|-78.78|35.79|159,Casa Grande|AZ|-111.76|32.88|51,Casas Adobes|AZ|-111|32.32|66,Casper|WY|-106.31|42.87|60,Casselberry|FL|-81.33|28.68|27,Castle Rock|CO|-104.86|39.37|55,Castlewood|CO|-104.9|39.58|25,Castro Valley|CA|-122.09|37.69|61,Catalina Foothills|AZ|-110.92|32.3|50,Catemaco|MX30|-95.11|18.42|27,Cathedral City|CA|-116.47|33.78|53,Catonsville|MD|-76.73|39.27|41,Cedar City|UT|-113.06|37.68|30,Cedar Falls|IA|-92.45|42.53|41,Cedar Hill|TX|-96.96|32.59|48,Cedar Park|TX|-97.82|30.51|65,Cedar Rapids|IA|-91.64|42.01|130,Celaya|MX11|-100.81|20.52|340,Celina|TX|-96.78|33.32|64,Centennial|CO|-104.88|39.58|109,Center City|PA|-75.16|39.95|57,Centereach|NY|-73.1|40.86|31,Central|LA|-91.04|30.55|28,Central 14th Street / Spring Road|DC|-77.03|38.94|25,Central City|AZ|-112.06|33.44|58,Central Islip|NY|-73.2|40.79|34,Centre City|AB|-114.07|51.05|41,Centretown|ON|-75.7|45.42|25,Centreville|VA|-77.43|38.84|71,Centro Familiar la Soledad|MX11|-101.75|21.14|32,Ceres|CA|-120.96|37.59|47,Cerritos|CA|-118.06|33.86|49,Chalco|MX15|-98.9|19.26|168,Chamblee|GA|-84.3|33.89|28,Champaign|IL|-88.24|40.12|86,Champoton|MX04|-90.72|19.34|30,Chandler|AZ|-111.84|33.31|260,Chanhassen|MN|-93.53|44.86|25,Channelview|TX|-95.11|29.78|38,Chapel Hill|NC|-79.06|35.91|59,Charlesbourg|QC|-71.31|46.9|82,Charleston|SC|-79.93|32.78|132,Charleston|WV|-81.63|38.35|46,Charlotte|NC|-80.84|35.23|911,Charlottesville|VA|-78.48|38.03|46,Charlottetown|PE|-63.13|46.23|38,Chaska|MN|-93.6|44.79|25,Chateauguay|QC|-73.75|45.38|42,Chatham|ON|-82.18|42.41|43,Chatham|IL|-87.61|41.74|31,Chatsworth|CA|-118.6|34.26|41,Chattanooga|TN|-85.31|35.05|181,Cheektowaga|NY|-78.75|42.9|75,Chelmsford|MA|-71.37|42.6|33,Chelsea|MA|-71.03|42.39|39,Cherry Hill|NJ|-75.03|39.93|70,Chesapeake|VA|-76.27|36.82|235,Cheshire|CT|-72.9|41.5|29,Chester|PA|-75.36|39.85|34,Chesterfield|MO|-90.58|38.66|47,Chestermere|AB|-113.82|51.03|32,Chetumal|MX23|-88.3|18.52|169,Cheyenne|WY|-104.82|41.14|65,Chiapa de Corzo|MX05|-93.01|16.71|45,Chicago|IL|-87.65|41.85|2664,Chicago Heights|IL|-87.64|41.51|30,Chicago Lawn|IL|-87.7|41.78|55,Chicago Loop|IL|-87.63|41.88|33,Chico|CA|-121.84|39.73|121,Chicoloapan|MX15|-98.9|19.42|172,Chicopee|MA|-72.61|42.15|56,Chicoutimi|QC|-71.06|48.42|69,Chihuahua|MX06|-106.09|28.64|925,Chilapa de Alvarez|MX12|-99.17|17.6|31,Chilliwack|BC|-121.95|49.17|101,Chilliwack-Downtown|BC|-121.96|49.16|31,Chillum|MD|-76.99|38.96|33,Chilpancingo|MX12|-99.5|17.55|187,Chinatown|CA|-122.41|37.8|100,Chinatown|NY|-74|40.72|90,Chino|CA|-117.69|34.01|85,Chino Hills|CA|-117.76|33.99|78,Cholula|MX21|-98.3|19.06|292,Chomedey|QC|-73.75|45.53|94,Chula Vista|CA|-117.08|32.64|265,Church-Yonge Corridor|ON|-79.38|43.66|31,Cibolo|TX|-98.23|29.56|33,Cicero|IL|-87.75|41.85|83,Cicero|NY|-76.12|43.18|31,Cihuatlan|MX14|-104.57|19.24|40,Cincinnati|OH|-84.51|39.13|311,Cintalapa de Figueroa|MX05|-93.72|16.68|42,Citrus Heights|CA|-121.28|38.71|87,City of Milford (balance)|CT|-73.06|41.22|51,City of Sammamish|WA|-122.04|47.6|45,Ciudad Acuna|MX07|-100.95|29.32|216,Ciudad Altamirano|MX12|-100.67|18.36|25,Ciudad Apodaca|MX19|-100.19|25.78|467,Ciudad Benito Juarez|MX19|-100.09|25.65|308,Ciudad Constitucion|MX03|-111.66|25.03|40,Ciudad de Allende|MX19|-100.02|25.28|26,Ciudad de Huajuapan de Leon|MX20|-97.78|17.81|53,Ciudad de Villa de Alvarez|MX08|-103.74|19.27|117,Ciudad del Carmen|MX04|-91.83|18.65|191,Ciudad Delicias|MX06|-105.47|28.19|148,Ciudad Fernandez|MX24|-100.01|21.94|32,Ciudad General Escobedo|MX19|-100.32|25.8|454,Ciudad Guzman|MX14|-103.46|19.7|111,Ciudad Hidalgo|MX16|-100.56|19.69|60,Ciudad Juarez|MX06|-106.46|31.72|1512,Ciudad Lazaro Cardenas|MX16|-102.2|17.96|196,Ciudad Lopez Mateos|MX15|-99.26|19.56|489,Ciudad Madero|MX28|-97.84|22.25|197,Ciudad Mante|MX28|-98.97|22.74|84,Ciudad Melchor Muzquiz|MX07|-101.52|27.88|35,Ciudad Mendoza|MX30|-97.18|18.81|35,Ciudad Nezahualcoyotl|MX15|-99.01|19.4|1077,Ciudad Obregon|MX26|-109.94|27.49|329,Ciudad Rio Bravo|MX28|-98.09|25.99|95,Ciudad Sabinas Hidalgo|MX19|-100.18|26.5|33,Ciudad Sahagun|MX13|-98.57|19.78|28,Ciudad Valles|MX24|-99.01|22|124,Ciudad Victoria|MX28|-99.14|23.74|332,Clairlea-Birchmount|ON|-79.28|43.71|26,Claremont|CA|-117.72|34.1|36,Clarksville|TN|-87.36|36.53|166,Clay|NY|-76.17|43.19|58,Clearfield|UT|-112.03|41.11|30,Clearwater|FL|-82.8|27.97|117,Cleburne|TX|-97.39|32.35|30,Clermont|FL|-81.77|28.55|32,Cleveland|OH|-81.7|41.5|365,Cleveland|TN|-84.88|35.16|43,Cleveland Heights|OH|-81.56|41.52|44,Clifton|NJ|-74.16|40.86|86,Clifton Park|NY|-73.77|42.87|36,Clinton|MD|-76.9|38.77|35,Clinton|IA|-90.19|41.84|26,Clinton|MS|-90.32|32.34|25,Clinton Township|MI|-82.92|42.59|99,Cloverdale|BC|-122.73|49.11|73,Clovis|CA|-119.7|36.83|104,Clovis|NM|-103.21|34.4|39,Coacalco|MX15|-99.11|19.63|277,Coachella|CA|-116.17|33.68|44,Coatepec|MX30|-96.96|19.45|53,Coatzacoalcos|MX30|-94.44|18.15|310,Coatzintla|MX30|-97.47|20.49|34,Cobbs Creek|PA|-75.24|39.95|33,Cochrane|AB|-114.47|51.18|32,Coconut Creek|FL|-80.18|26.25|59,Cocula|MX14|-103.82|20.37|29,Coeur d'Alene|ID|-116.78|47.68|49,Colima|MX08|-103.71|19.24|146,College Park|MD|-76.94|38.98|32,College Point|NY|-73.85|40.79|27,College Station|TX|-96.33|30.63|107,Colleyville|TX|-97.16|32.88|25,Collierville|TN|-89.66|35.04|48,Collinwood|OH|-81.57|41.56|34,Colonia del Sol|MX03|-109.93|22.91|48,Colonia del Valle|MX09|-99.16|19.39|250,Colonia Lindavista|MX15|-99.12|19.49|100,Colonia Nativitas|MX09|-99.14|19.38|60,Colorado Springs|CO|-104.82|38.83|456,Colton|CA|-117.31|34.07|54,Columbia|SC|-81.03|34|142,Columbia|MO|-92.33|38.95|129,Columbia|MD|-76.84|39.24|99,Columbia|TN|-87.04|35.62|36,Columbia Heights|DC|-77.03|38.93|38,Columbus|OH|-83|39.96|913,Columbus|GA|-84.99|32.46|206,Columbus|IN|-85.92|39.2|46,Comalcalco|MX27|-93.22|18.26|41,Comitan|MX05|-92.14|16.24|166,Commack|NY|-73.29|40.84|36,Commerce City|CO|-104.93|39.81|53,Compton|CA|-118.22|33.9|98,Conception Bay South|NL|-53|47.5|27,Concord|CA|-122.03|37.98|128,Concord|NC|-80.58|35.41|87,Concord|NH|-71.54|43.21|43,Coney Island|NY|-73.99|40.58|60,Confederation|SK|-106.7|52.14|70,Conroe|TX|-95.46|30.31|68,Contla|MX29|-98.17|19.33|27,Conway|AR|-92.44|35.09|64,Cookeville|TN|-85.5|36.16|32,Coon Rapids|MN|-93.29|45.12|62,Cooper City|FL|-80.27|26.06|35,Coppell|TX|-97.02|32.95|41,Copperas Cove|TX|-97.9|31.12|33,Coquitlam|BC|-122.78|49.28|148,Coquitlam West|BC|-122.88|49.26|25,Coral Gables|FL|-80.27|25.72|51,Coral Springs|FL|-80.27|26.27|129,Coram|NY|-73|40.87|39,Cordoba|MX30|-96.93|18.88|204,Cordova|TN|-89.78|35.16|68,Core Neighbourhoods|SK|-106.67|52.13|36,Cornelius|NC|-80.86|35.49|28,Cornwall|ON|-74.73|45.02|48,Corona|CA|-117.57|33.88|164,Corona|NY|-73.86|40.75|109,Corpus Christi|TX|-97.4|27.8|316,Cortazar|MX11|-100.96|20.48|61,Corvallis|OR|-123.26|44.56|55,Cosamaloapan|MX30|-95.8|18.37|30,Costa Mesa|CA|-117.92|33.64|113,Cote-Saint-Luc|QC|-73.67|45.47|31,Cottage Grove|MN|-92.94|44.83|35,Cottonwood Heights|UT|-111.81|40.62|34,Council Bluffs|IA|-95.86|41.26|62,Country Club|FL|-80.32|25.95|47,Courtenay|BC|-124.99|49.69|28,Coventry|RI|-71.68|41.7|35,Covina|CA|-117.89|34.09|48,Covington|KY|-84.51|39.08|40,Coyoacan|MX09|-99.16|19.35|614,Coyotepec|MX15|-99.21|19.78|35,Coyula|MX14|-103.23|20.66|29,Cozumel|MX23|-86.94|20.5|77,Cranberry Township|PA|-80.11|40.68|28,Cranston|RI|-71.44|41.78|81,Crofton|MD|-76.69|39|27,Crown Point|IN|-87.37|41.42|28,Crystal Lake|IL|-88.32|42.24|40,Cuauhtemoc|MX09|-99.15|19.45|531,Cuauhtemoc|MX06|-106.87|28.41|168,Cuautitlan|MX15|-99.18|19.67|178,Cuautitlan Izcalli|MX15|-99.22|19.64|555,Cuautla|MX17|-98.94|18.81|157,Cuautlancingo|MX21|-98.27|19.09|25,Cuernavaca|MX17|-99.23|18.93|338,Culiacan|MX25|-107.39|24.8|808,Culver City|CA|-118.4|34.02|39,Cumberland|RI|-71.43|41.97|34,Cupertino|CA|-122.03|37.32|60,Cutler Bay|FL|-80.34|25.58|45,Cutler Ridge|FL|-80.35|25.58|26,Cuyahoga Falls|OH|-81.48|41.13|49,Cypress|TX|-95.7|29.97|200,Cypress|CA|-118.04|33.82|49,Cypress Hills|NY|-73.89|40.68|54,Dakota Ridge|CO|-105.14|39.62|33,Dale City|VA|-77.31|38.64|65,Dallas|TX|-96.81|32.78|1326,Dalton|GA|-84.97|34.77|33,Daly City|CA|-122.46|37.71|106,Dana Point|CA|-117.7|33.47|34,Danbury|CT|-73.45|41.39|84,Dania Beach|FL|-80.14|26.05|31,Danvers|MA|-70.93|42.58|26,Danville|CA|-122|37.82|44,Danville|VA|-79.4|36.59|42,Danville|IL|-87.63|40.12|32,Dartmouth|NS|-63.58|44.67|101,Davenport|IA|-90.58|41.52|102,Davie|FL|-80.23|26.06|100,Davis|CA|-121.74|38.54|67,Dayton|OH|-84.19|39.76|135,Daytona Beach|FL|-81.02|29.21|72,Dearborn|MI|-83.18|42.32|95,Dearborn Heights|MI|-83.27|42.34|56,Decatur|IL|-88.95|39.84|73,Decatur|AL|-86.98|34.61|55,Deer Park|TX|-95.12|29.71|33,Deer Park|NY|-73.33|40.76|27,Deer Valley|AZ|-112.13|33.68|165,Deerfield Beach|FL|-80.1|26.32|79,DeKalb|IL|-88.75|41.93|43,Del Rio|TX|-100.9|29.36|36,DeLand|FL|-81.3|29.03|30,Delano|CA|-119.25|35.77|52,Delaware|OH|-83.07|40.3|37,Delegacion Cuajimalpa de Morelos|MX09|-99.29|19.37|160,Delray Beach|FL|-80.07|26.46|66,Delta|BC|-123.05|49.09|101,Deltona|FL|-81.26|28.9|88,Denton|TX|-97.13|33.21|131,Denver|CO|-104.98|39.74|729,Derry Village|NH|-71.31|42.89|34,Des Moines|IA|-93.61|41.6|214,Des Moines|WA|-122.32|47.4|31,Des Plaines|IL|-87.88|42.03|58,Desert Hot Springs|CA|-116.5|33.96|28,Desmarchais-Crawford|QC|-73.58|45.45|29,DeSoto|TX|-96.86|32.59|52,Detroit|MI|-83.05|42.33|645,Diamond Bar|CA|-117.81|34.03|56,Dieppe|NB|-64.69|46.08|27,Dix Hills|NY|-73.34|40.8|26,Dodge City|KS|-100.02|37.75|27,Dollard-Des Ormeaux|QC|-73.82|45.49|48,Dolores Hidalgo|MX11|-100.93|21.16|67,Don Valley Village|ON|-79.35|43.78|27,Doral|FL|-80.36|25.82|75,Dorchester|MA|-71.07|42.3|97,Dorset Park|ON|-79.28|43.75|25,Dothan|AL|-85.39|31.22|68,Douglasville|GA|-84.75|33.75|32,Dover|DE|-75.52|39.16|39,Dover|NH|-70.87|43.2|30,Dovercourt-Wallace Emerson-Junction|ON|-79.44|43.67|36,Downers Grove|IL|-88.01|41.81|49,Downey|CA|-118.13|33.94|114,Downsview-Roding-CFB|ON|-79.49|43.73|35,Downtown DC|DC|-77.02|38.89|52,Downtown Halifax|NS|-63.58|44.65|25,Downtown Vancouver|BC|-123.12|49.28|62,Dracut|MA|-71.3|42.67|28,Draper|UT|-111.86|40.52|46,Drexel Heights|AZ|-111.03|32.14|27,Drexel Hill|PA|-75.29|39.95|28,Drummondville|QC|-72.48|45.88|59,Dublin|CA|-121.94|37.7|57,Dublin|OH|-83.11|40.1|45,Dubuque|IA|-90.66|42.5|58,Duluth|MN|-92.11|46.78|86,Duluth|GA|-84.14|34|29,Duncanville|TX|-96.91|32.65|39,Dundalk|MD|-76.52|39.25|63,Dunedin|FL|-82.77|28.02|36,Dunwoody|GA|-84.33|33.95|48,Durham|NC|-78.9|35.99|257,Duvernay|QC|-73.67|45.58|37,Dyker Heights|NY|-74.01|40.62|34,Eagan|MN|-93.17|44.8|66,Eagle Mountain|UT|-112.01|40.31|27,Eagle Pass|TX|-100.5|28.71|28,East Boston|MA|-71.04|42.38|43,East Brunswick|NJ|-74.42|40.43|48,East Chattanooga|TN|-85.25|35.07|154,East Chicago|IN|-87.45|41.64|28,East Concord|NH|-71.54|43.24|42,East Flatbush|NY|-73.93|40.65|178,East Florence|AL|-87.65|34.81|35,East Hampton|VA|-76.33|37.04|147,East Harlem|NY|-73.94|40.79|115,East Hartford|CT|-72.61|41.78|51,East Hastings|BC|-123.06|49.27|80,East Haven|CT|-72.87|41.28|29,East Hill-Meridian|WA|-122.17|47.41|29,East Honolulu|HI|-157.72|21.29|49,East Independence|MO|-94.36|39.1|110,East Lake|FL|-82.69|28.11|30,East Lansing|MI|-84.48|42.74|48,East Los Angeles|CA|-118.17|34.02|126,East Meadow|NY|-73.56|40.71|38,East New York|NY|-73.88|40.67|173,East Norwalk|CT|-73.4|41.11|84,East Orange|NJ|-74.2|40.77|64,East Palo Alto|CA|-122.14|37.47|29,East Pensacola Heights|FL|-87.18|30.43|54,East Point|GA|-84.44|33.68|35,East Providence|RI|-71.37|41.81|47,East Saint Louis|IL|-90.15|38.62|27,East Village|NY|-73.99|40.73|62,Easton|PA|-75.22|40.69|26,Eastpointe|MI|-82.96|42.47|32,Eastvale|CA|-117.56|33.96|59,Eau Claire|WI|-91.5|44.81|67,Ecatepec de Morelos|MX15|-99.06|19.6|1645,Echo Park|CA|-118.26|34.08|43,Eden Prairie|MN|-93.47|44.85|63,Edgewater|IL|-87.66|41.98|54,Edgewood|MD|-76.29|39.42|25,Edina|MN|-93.35|44.89|50,Edinburg|TX|-98.16|26.3|84,Edison|NJ|-74.41|40.52|102,Edmond|OK|-97.48|35.65|90,Edmonds|WA|-122.38|47.81|41,Edmonton|AB|-113.47|53.55|1010,Egypt Lake-Leto|FL|-82.51|28.02|35,El Cajon|CA|-116.96|32.79|103,El Centro|CA|-115.56|32.79|43,El Dorado Hills|CA|-121.08|38.69|42,El Mirage|AZ|-112.32|33.61|33,El Monte|CA|-118.03|34.07|116,El Paso|TX|-106.49|31.76|678,El Pueblito|MX22|-100.44|20.54|71,Eldersburg|MD|-76.95|39.4|30,Elgin|IL|-88.28|42.04|112,Elizabeth|NJ|-74.21|40.66|129,Elizabethtown|KY|-85.86|37.69|29,Elk Grove|CA|-121.37|38.41|166,Elk Grove Village|IL|-87.97|42|33,Elkhart|IN|-85.98|41.68|52,Ellendale|TN|-89.83|35.23|25,Ellicott City|MD|-76.8|39.27|65,Elmhurst|NY|-73.88|40.74|113,Elmhurst|IL|-87.94|41.9|45,Elmira|NY|-76.81|42.09|28,Elmont|NY|-73.71|40.7|33,Elyria|OH|-82.11|41.37|53,Emiliano Zapata|MX17|-99.18|18.84|49,Emiliano Zapata|MX15|-98.97|19.34|25,Empalme|MX26|-110.81|27.96|42,Encanto|AZ|-112.08|33.48|54,Encarnacion de Diaz|MX14|-102.24|21.53|25,Enchanted Hills|NM|-106.59|35.34|87,Encinitas|CA|-117.29|33.04|62,Encino|CA|-118.5|34.16|44,Enfield|CT|-72.59|41.98|45,Englewood|CO|-104.99|39.65|33,Englewood|NJ|-73.97|40.89|28,Englewood|IL|-87.65|41.78|26,Enid|OK|-97.88|36.4|51,Ensenada|MX02|-116.6|31.87|443,Enterprise|NV|-115.24|36.03|108,Enterprise|AL|-85.86|31.32|27,Erie|PA|-80.09|42.13|99,Escarcega|MX04|-90.75|18.61|29,Escondido|CA|-117.09|33.12|151,Escuinapa de Hidalgo|MX25|-105.78|22.83|30,Esperanza|MX26|-109.93|27.58|38,Essex|MD|-76.47|39.31|39,Estero|FL|-81.81|26.44|30,Etobicoke|ON|-79.57|43.64|365,Euclid|OH|-81.53|41.59|47,Eugene|OR|-123.09|44.05|176,Euless|TX|-97.08|32.84|54,Eureka|CA|-124.16|40.8|27,Evans|GA|-82.13|33.53|29,Evanston|IL|-87.69|42.04|75,Evansville|IN|-87.56|37.97|119,Everett|WA|-122.2|47.98|108,Everett|MA|-71.05|42.41|46,'Ewa Gentry-West Loch|HI|-158.03|21.35|35,Ewing|NJ|-74.8|40.27|36,Fair Lawn|NJ|-74.13|40.94|33,Fair Oaks|CA|-121.27|38.64|30,Fairbanks|AK|-147.72|64.84|32,Fairborn|OH|-84.02|39.82|33,Fairfield|CA|-122.04|38.25|112,Fairfield|CT|-73.26|41.14|59,Fairfield|OH|-84.56|39.35|42,Fairview|BC|-123.13|49.26|33,Fall River|MA|-71.16|41.7|94,Fallbrook|CA|-117.25|33.38|30,Fallingbrook|ON|-75.48|45.48|25,Far Rockaway|NY|-73.76|40.61|39,Fargo|ND|-96.79|46.88|118,Farmers Branch|TX|-96.9|32.93|32,Farmington|NM|-108.22|36.73|42,Farmington|CT|-72.83|41.72|25,Farmington Hills|MI|-83.38|42.49|81,Fayetteville|NC|-78.88|35.05|201,Fayetteville|AR|-94.16|36.06|82,Federal Way|WA|-122.31|47.32|95,Felipe Carrillo Puerto|MX23|-88.05|19.58|25,Fenway/Kenmore|MA|-71.1|42.34|37,Ferry Pass|FL|-87.21|30.51|28,Financial District|NY|-74.01|40.71|60,Findlay|OH|-83.65|41.04|41,Fishers|IN|-86.01|39.96|76,Fitchburg|MA|-71.8|42.58|40,Fitchburg|WI|-89.47|42.96|27,Flagami|FL|-80.32|25.76|50,Flagstaff|AZ|-111.65|35.2|70,Flatbush|NY|-73.96|40.65|93,Flatlands|NY|-73.93|40.62|63,Fleetwood|BC|-122.8|49.17|65,Fleming Island|FL|-81.72|30.09|27,Flint|MI|-83.69|43.01|98,Florence|AL|-87.68|34.8|40,Florence|SC|-79.76|34.2|38,Florence|KY|-84.63|39|32,Florence|AZ|-111.39|33.03|31,Florence-Graham|CA|-118.24|33.97|63,Florin|CA|-121.41|38.5|47,Florissant|MO|-90.32|38.79|52,Flower Mound|TX|-97.1|33.01|71,Folsom|CA|-121.18|38.68|76,Fond du Lac|WI|-88.44|43.77|42,Fontana|CA|-117.44|34.09|212,Foothill Farms|CA|-121.35|38.68|33,Fordham|NY|-73.9|40.86|94,Forest Hills|NY|-73.85|40.72|67,Forest Hills|MI|-85.49|42.96|25,Fort Bragg|NC|-79.01|35.14|29,Fort Cavazos|TX|-97.78|31.13|29,Fort Collins|CO|-105.08|40.59|170,Fort Erie|ON|-78.93|42.9|30,Fort Garry South|MB|-97.16|49.79|65,Fort Hamilton|NY|-74.03|40.62|28,Fort Lauderdale|FL|-80.14|26.12|183,Fort Lee|NJ|-73.97|40.85|36,Fort McMurray|AB|-111.38|56.73|66,Fort Myers|FL|-81.84|26.62|74,Fort Pierce|FL|-80.33|27.45|44,Fort Smith|AR|-94.4|35.39|88,Fort Wayne|IN|-85.13|41.13|260,Fort Worth|TX|-97.32|32.73|1008,Fortuna Foothills|AZ|-114.41|32.66|26,Foster City|CA|-122.27|37.56|33,Fountain|CO|-104.7|38.68|27,Fountain Valley|CA|-117.95|33.71|56,Fountainebleau|FL|-80.35|25.77|59,Four Corners|FL|-81.65|28.33|26,Fraccionamiento Real Palmas|MX19|-100.16|25.9|34,Framingham|MA|-71.42|42.28|68,Framingham Center|MA|-71.44|42.3|65,Francisco I. Madero|MX07|-103.27|25.77|26,Frankfort|KY|-84.87|38.2|28,Franklin|TN|-86.87|35.93|72,Franklin|WI|-88.04|42.89|36,Franklin|MA|-71.4|42.08|30,Franklin Square|NY|-73.68|40.71|29,Fraser Heights|BC|-122.78|49.2|25,Frederick|MD|-77.41|39.41|69,Fredericksburg|VA|-77.46|38.3|28,Fredericton|NB|-66.67|45.95|63,Fredericton Northside|NB|-66.64|45.98|28,Freeport|NY|-73.58|40.66|43,Fremont|CA|-121.99|37.55|232,Fremont|NE|-96.5|41.43|26,Fresh Meadows|NY|-73.79|40.73|28,Fresnillo|MX32|-102.87|23.17|143,Fresno|CA|-119.77|36.75|542,Fridley|MN|-93.26|45.09|27,Friendswood|TX|-95.2|29.53|38,Frisco|TX|-96.82|33.15|154,Frontera|MX07|-101.45|26.93|69,Fruit Cove|FL|-81.64|30.11|29,Fuentes del Valle|MX15|-99.14|19.63|74,Fullerton|CA|-117.93|33.87|140,Gabriel Leyva Solano|MX25|-108.64|25.66|27,Gadsden|AL|-86.01|34.01|36,Gage Park|IL|-87.7|41.8|41,Gahanna|OH|-82.88|40.02|34,Gainesville|FL|-82.32|29.65|145,Gainesville|GA|-83.82|34.3|38,Gaithersburg|MD|-77.2|39.14|67,Galesburg|IL|-90.37|40.95|31,Gallatin|TN|-86.45|36.39|34,Galt|CA|-121.3|38.25|25,Galveston|TX|-94.8|29.3|50,Garcia|MX19|-100.59|25.8|93,Garden City|KS|-100.87|37.97|27,Garden City|MI|-83.33|42.33|26,Garden Grove|CA|-117.94|33.77|175,Gardena|CA|-118.31|33.89|60,Garfield|NJ|-74.11|40.88|31,Garfield Heights|OH|-81.61|41.42|28,Garland|TX|-96.64|32.91|236,Garner|NC|-78.61|35.71|28,Gary|IN|-87.35|41.59|77,Gastonia|NC|-81.19|35.26|74,Gatineau|QC|-75.7|45.48|300,Georgetown|TX|-97.68|30.63|63,Georgetown|ON|-79.92|43.65|44,Georgetown|KY|-84.56|38.21|32,Germantown|MD|-77.27|39.17|86,Germantown|TN|-89.81|35.09|39,Gilbert|AZ|-111.79|33.35|247,Gillette|WY|-105.5|44.29|32,Gilroy|CA|-121.57|37.01|53,Gladstone|MO|-94.55|39.2|26,Glastonbury|CT|-72.61|41.71|31,Glen Burnie|MD|-76.62|39.16|67,Glen Cove|NY|-73.63|40.86|27,Glen Ellyn|IL|-88.07|41.88|28,Glendale|AZ|-112.19|33.54|240,Glendale|CA|-118.26|34.14|201,Glendale|NY|-73.89|40.7|34,Glendale Heights|IL|-88.06|41.91|34,Glendora|CA|-117.87|34.14|52,Glenfield-Jane Heights|ON|-79.51|43.75|30,Glenview|IL|-87.79|42.07|47,Glenville|NY|-74.05|42.93|29,Gloucester|ON|-75.63|45.35|150,Gloucester|MA|-70.66|42.61|29,Golden Glades|FL|-80.2|25.91|33,Goldsboro|NC|-77.99|35.38|35,Goleta|CA|-119.83|34.44|30,Gomez Palacio|MX10|-103.5|25.57|257,Goodyear|AZ|-112.36|33.44|79,Goose Creek|SC|-80.03|32.98|40,Goshen|IN|-85.83|41.58|32,Gramercy Park|NY|-73.99|40.74|27,Granby|QC|-72.73|45.4|66,Grand Forks|ND|-97.03|47.93|57,Grand Island|NE|-98.34|40.93|51,Grand Junction|CO|-108.55|39.06|60,Grand Prairie|TX|-97|32.75|187,Grand Rapids|MI|-85.67|42.96|195,Grande Prairie|AB|-118.8|55.17|70,Grandview|MO|-94.53|38.89|25,Grandview-Woodlands|BC|-123.07|49.28|29,Granger|IN|-86.11|41.75|30,Granite City|IL|-90.15|38.7|29,Grants Pass|OR|-123.33|42.44|37,Grapevine|TX|-97.08|32.93|51,Gravesend|NY|-73.97|40.6|112,Great Falls|MT|-111.3|47.5|59,Greater Grand Crossing|IL|-87.61|41.76|32,Greater Sudbury|ON|-80.99|46.49|166,Greeley|CO|-104.71|40.42|108,Green|OH|-81.48|40.95|25,Green Bay|WI|-88.02|44.52|105,Greenacres City|FL|-80.13|26.62|32,Greenburgh|NY|-73.84|41.03|86,Greenfield|WI|-88.01|42.96|37,Greenpoint|NY|-73.95|40.72|34,Greensboro|NC|-79.79|36.07|285,Greenville|NC|-77.37|35.61|90,Greenville|SC|-82.39|34.85|64,Greenville|MS|-91.06|33.41|32,Greenville|TX|-96.11|33.14|26,Greenwood|IN|-86.11|39.61|55,Greer|SC|-82.23|34.94|28,Gresham|OR|-122.43|45.5|110,Grimsby|ON|-79.57|43.2|27,Grove City|OH|-83.09|39.88|39,Guadalajara|MX14|-103.35|20.68|1385,Guadalupe|MX19|-100.26|25.68|673,Guadalupe|MX32|-102.52|22.75|170,Guamuchil|MX25|-108.08|25.46|72,Guanajuato|MX11|-101.26|21.02|72,Guasave|MX25|-108.47|25.57|71,Guelph|ON|-80.26|43.55|143,Guildford|BC|-122.8|49.19|64,Gulfport|MS|-89.09|30.37|71,Gurnee|IL|-87.9|42.37|31,Gustavo Adolfo Madero|MX09|-99.11|19.49|1185,Gwynn Oak|MD|-76.69|39.33|47,Hacienda Heights|CA|-117.97|33.99|54,Hacienda Santa Fe|MX14|-103.38|20.52|86,Hackensack|NJ|-74.04|40.89|44,Hagerstown|MD|-77.72|39.64|40,Halifax|NS|-63.58|44.64|471,Hallandale Beach|FL|-80.15|25.98|39,Haltom City|TX|-97.27|32.8|44,Halton Hills|ON|-79.93|43.64|62,Hamden|CT|-72.9|41.4|59,Hamilton|ON|-79.85|43.25|569,Hamilton|OH|-84.56|39.4|62,Hammond|IN|-87.5|41.58|77,Hampton|VA|-76.35|37.03|137,Hanford|CA|-119.65|36.33|55,Hanover|MD|-76.72|39.19|38,Hanover Park|IL|-88.15|42|38,Harker Heights|TX|-97.66|31.08|29,Harlem|NY|-73.95|40.81|116,Harlingen|TX|-97.7|26.19|65,Harrisburg|PA|-76.88|40.27|50,Harrison|NY|-73.71|40.97|28,Harrisonburg|VA|-78.87|38.45|52,Hartford|CT|-72.69|41.76|121,Harvey|IL|-87.65|41.61|25,Hastings-Sunrise|BC|-123.03|49.28|34,Hattiesburg|MS|-89.29|31.33|46,Haverhill|MA|-71.08|42.78|62,Havertown|PA|-75.31|39.98|50,Hawai'i Kai|HI|-157.7|21.3|30,Hawthorne|CA|-118.35|33.92|88,Hayward|CA|-122.08|37.67|158,Hazelwood|MO|-90.37|38.77|25,Helena|MT|-112.04|46.59|32,Hell's Kitchen|NY|-73.99|40.76|45,Hemet|CA|-116.97|33.75|83,Hempstead|NY|-73.62|40.71|55,Henderson|NV|-114.98|36.04|285,Henderson|KY|-87.59|37.84|28,Hendersonville|TN|-86.62|36.3|56,Henrietta|NY|-77.61|43.06|42,Hercules|CA|-122.29|38.02|25,Hermitage|TN|-86.62|36.2|37,Hermosillo|MX26|-110.97|29.09|812,Heroica Caborca|MX26|-112.16|30.72|59,Heroica Ciudad de Juchitan de Zaragoza|MX20|-95.02|16.43|88,Heroica Guaymas|MX26|-110.9|27.92|117,Heroica Matamoros|MX28|-97.5|25.88|510,Heroica Zitacuaro|MX16|-100.36|19.44|84,Herriman|UT|-112.03|40.51|30,Hesperia|CA|-117.3|34.43|93,Hialeah|FL|-80.28|25.86|237,Hickory|NC|-81.34|35.73|40,Hicksville|NY|-73.53|40.77|41,Hidalgo del Parral|MX06|-105.67|26.93|104,High Point|NC|-80.01|35.96|110,Highland|CA|-117.21|34.13|54,Highland Park|IL|-87.8|42.18|29,Highlands Ranch|CO|-104.97|39.55|96,Hilliard|OH|-83.16|40.03|33,Hillsboro|OR|-122.99|45.52|102,Hillsborough|NJ|-74.63|40.48|38,Hilo|HI|-155.09|19.73|43,Hilton Head|SC|-80.75|32.22|37,Hilton Head Island|SC|-80.74|32.19|40,Hinesville|GA|-81.6|31.85|33,Hobart|IN|-87.26|41.53|28,Hobbs|NM|-103.14|32.7|38,Hoboken|NJ|-74.03|40.74|53,Hoffman Estates|IL|-88.08|42.04|52,Holbrook|NY|-73.08|40.81|27,Holladay|UT|-111.82|40.67|30,Holland|MI|-86.11|42.79|33,Hollister|CA|-121.4|36.85|37,Holly Springs|NC|-78.83|35.65|31,Hollywood|CA|-118.33|34.1|167,Hollywood|FL|-80.15|26.01|149,Holmesburg|PA|-75.03|40.04|28,Holyoke|MA|-72.62|42.2|40,Homestead|FL|-80.48|25.47|80,Homewood|AL|-86.8|33.47|25,Honolulu|HI|-157.86|21.31|350,Hoover|AL|-86.81|33.41|84,Hopkinsville|KY|-87.49|36.87|32,Horn Lake|MS|-90.03|34.96|26,Hot Springs|AR|-93.06|34.5|35,Houma|LA|-90.72|29.6|34,Houston|TX|-95.36|29.76|2314,Howard Beach|NY|-73.84|40.66|26,Huamantla|MX29|-97.93|19.31|51,Huatabampo|MX26|-109.64|26.83|30,Huatusco|MX30|-96.97|19.15|31,Huauchinango|MX21|-98.05|20.17|56,Huber Heights|OH|-84.12|39.84|38,Huejotzingo|MX21|-98.41|19.16|25,Huejutla de Reyes|MX13|-98.42|21.14|40,Huimanguillo|MX27|-93.39|17.83|27,Huixquilucan|MX15|-99.35|19.36|124,Huixtla|MX05|-92.46|15.14|32,Hull|QC|-75.71|45.43|63,Huntersville|NC|-80.84|35.41|52,Huntington|WV|-82.45|38.42|48,Huntington Beach|CA|-118|33.66|201,Huntington Park|CA|-118.23|33.98|59,Huntington Station|NY|-73.41|40.85|33,Huntley|IL|-88.43|42.17|26,Hunts Point|NY|-73.88|40.81|27,Huntsville|AL|-86.59|34.73|215,Huntsville|TX|-95.55|30.72|40,Hurst|TX|-97.17|32.82|39,Hutchinson|KS|-97.93|38.06|41,Hyde Park|MA|-71.12|42.26|31,Hyde Park|IL|-87.59|41.79|26,Idaho Falls|ID|-112.03|43.47|59,Iguala de la Independencia|MX12|-99.54|18.35|118,Imperial Beach|CA|-117.11|32.58|27,Independence|MO|-94.42|39.09|117,Independence|KY|-84.54|38.94|26,Indian Trail|NC|-80.67|35.08|37,Indianapolis|IN|-86.16|39.77|887,Indio|CA|-116.22|33.72|87,Inglewood|CA|-118.35|33.96|111,Innisfil|ON|-79.65|44.3|43,Inver Grove Heights|MN|-93.04|44.85|34,Iowa City|IA|-91.53|41.66|74,Irapuato|MX11|-101.36|20.67|380,Irondequoit|NY|-77.58|43.21|51,Irvine|CA|-117.82|33.67|256,Irving|TX|-96.95|32.81|236,Irving Park|IL|-87.74|41.95|56,Irvington|NJ|-74.23|40.73|61,Isla|MX30|-95.53|18.03|26,Islington-City Centre West|ON|-79.54|43.63|43,Issaquah|WA|-122.03|47.53|36,Ithaca|NY|-76.5|42.44|30,Ixmiquilpan|MX13|-99.22|20.48|34,Ixtaczoquitlan|MX30|-97.06|18.85|26,Ixtapa|MX14|-105.21|20.72|29,Ixtapa-Zihuatanejo|MX12|-101.55|17.64|67,Ixtapaluca|MX15|-98.88|19.32|322,Ixtepec|MX20|-95.1|16.56|25,Iztacalco|MX09|-99.1|19.4|384,Iztapalapa|MX09|-99.06|19.36|1835,Izucar de Matamoros|MX21|-98.46|18.6|42,Jackson|MS|-90.18|32.3|153,Jackson|TN|-88.81|35.61|66,Jackson|NJ|-74.86|39.78|54,Jackson|MI|-84.4|42.25|33,Jackson Heights|NY|-73.89|40.76|67,Jacksonville|FL|-81.66|30.33|1009,Jacksonville|NC|-77.43|34.75|67,Jacksonville|AR|-92.11|34.87|28,Jacona de Plancarte|MX16|-102.31|19.96|53,Jaltipan de Morelos|MX30|-94.71|17.97|32,Jamaica|NY|-73.81|40.69|216,Jamaica Plain|MA|-71.12|42.31|37,Jamestown|NY|-79.24|42.1|30,Janesville|WI|-89.02|42.68|64,Jardines de la Silla|MX19|-100.19|25.63|53,Jefferson City|MO|-92.17|38.58|42,Jeffersontown|KY|-85.56|38.19|26,Jeffersonville|IN|-85.74|38.28|46,Jerez de Garcia Salinas|MX32|-102.99|22.65|43,Jersey City|NJ|-74.08|40.73|264,Jesus Maria|MX01|-102.34|21.96|43,Jiutepec|MX17|-99.18|18.88|215,Johns Creek|GA|-84.2|34.03|83,Johnson City|TN|-82.35|36.31|66,Johnston|RI|-71.51|41.82|29,Joint Base Pearl Harbor Hickam|HI|-157.95|21.35|42,Joliet|IL|-88.08|41.53|147,Joliette|QC|-73.42|46.02|34,Jonesboro|AR|-90.7|35.84|73,Jonquiere|QC|-71.25|48.42|54,Joplin|MO|-94.51|37.08|51,Jose Mariano Jimenez|MX06|-104.92|27.13|34,Juneau|AK|-134.42|58.3|31,Jupiter|FL|-80.09|26.93|62,Juventino Rosas|MX11|-101|20.64|42,Kahului|HI|-156.47|20.89|26,Kailua|HI|-157.74|21.4|38,Kalamazoo|MI|-85.59|42.29|76,Kalihi-Palama|HI|-157.88|21.33|43,Kamloops|BC|-120.32|50.67|104,Kanasin|MX31|-89.56|20.93|77,Kanata|ON|-75.92|45.3|90,Kaneohe|HI|-157.8|21.4|34,Kankakee|IL|-87.86|41.12|26,Kannapolis|NC|-80.62|35.49|46,Kansas City|MO|-94.58|39.1|475,Kansas City|KS|-94.63|39.11|152,Kaysville|UT|-111.94|41.04|30,Kearney|NE|-99.08|40.7|33,Kearns|UT|-112|40.66|35,Kearny|NJ|-74.15|40.77|42,Keizer|OR|-123.03|44.99|37,Keller|TX|-97.25|32.93|45,Kelowna|BC|-119.49|49.88|144,Ken Caryl|CO|-105.11|39.58|32,Kendale Lakes|FL|-80.41|25.71|56,Kendall|FL|-80.32|25.68|80,Kendall West|FL|-80.44|25.71|36,Kenner|LA|-90.24|29.99|67,Kennesaw|GA|-84.62|34.02|33,Kennewick|WA|-119.14|46.21|78,Kenosha|WI|-87.82|42.58|99,Kensington|NY|-73.97|40.65|39,Kensington-Cedar Cottage|BC|-123.07|49.25|49,Kent|WA|-122.23|47.38|126,Kent|OH|-81.36|41.15|29,Kentwood|MI|-85.64|42.87|51,Kettering|OH|-84.17|39.69|55,Kew Gardens Hills|NY|-73.82|40.73|37,Key West|FL|-81.78|24.56|25,Killarney|BC|-123.04|49.23|29,Killeen|TX|-97.73|31.12|140,Kingman|AZ|-114.05|35.19|28,Kings Bridge|NY|-73.91|40.88|75,Kingsport|TN|-82.56|36.55|53,Kingston|ON|-76.48|44.23|132,Kingsville|TX|-97.86|27.52|26,Kirkland|WA|-122.21|47.68|87,Kirkwood|MO|-90.41|38.58|27,Kiryas Joel|NY|-74.17|41.34|32,Kissimmee|FL|-81.42|28.3|69,Kitchener|ON|-80.51|43.43|256,Kitsilano|BC|-123.17|49.27|43,Knoxville|TN|-83.92|35.96|190,Kokomo|IN|-86.13|40.49|57,Koreatown|CA|-118.3|34.06|124,Kyle|TX|-97.88|29.99|35,L'Amoreaux|ON|-79.31|43.8|43,La Barca|MX14|-102.54|20.29|35,La Cite-Limoilou|QC|-71.23|46.83|108,La Crosse|WI|-91.24|43.8|52,La Habra|CA|-117.95|33.93|62,La Haute-Saint-Charles|QC|-71.37|46.89|88,La Jolla|CA|-117.27|32.85|42,La Joya|MX02|-117|32.44|26,La Mesa|CA|-117.02|32.77|60,La Mirada|CA|-118.01|33.92|49,La Paz|MX03|-110.31|24.14|250,La Piedad de Cabadas|MX16|-102.02|20.34|83,La Porte|TX|-95.02|29.67|35,La Presa|CA|-117|32.71|34,La Puente|CA|-117.95|34.02|40,La Quinta|CA|-116.31|33.66|40,La Vergne|TN|-86.58|36.02|34,La Verne|CA|-117.77|34.1|32,Lacey|WA|-122.82|47.03|46,Lafayette|LA|-92.02|30.22|121,Lafayette|IN|-86.88|40.42|71,Lafayette|CO|-105.09|39.99|27,Lafayette|CA|-122.12|37.89|25,Lagos de Moreno|MX14|-101.93|21.36|98,LaGrange|GA|-85.03|33.04|29,Laguna|CA|-121.42|38.42|46,Laguna Hills|CA|-117.71|33.61|31,Laguna Niguel|CA|-117.71|33.52|65,Lake Charles|LA|-93.2|30.21|76,Lake Elsinore|CA|-117.33|33.67|61,Lake Forest|CA|-117.69|33.65|82,Lake Havasu City|AZ|-114.32|34.48|53,Lake in the Hills|IL|-88.33|42.18|29,Lake Jackson|TX|-95.43|29.03|27,Lake Magdalene|FL|-82.47|28.07|28,Lake Oswego|OR|-122.67|45.42|38,Lake Ridge|VA|-77.3|38.69|41,Lake Stevens|WA|-122.06|48.02|30,Lake Worth Beach|FL|-80.07|26.62|37,Lakeland|FL|-81.95|28.04|104,Lakeside|FL|-81.77|30.13|30,Lakeville|MN|-93.24|44.65|60,Lakewood|CO|-105.08|39.7|152,Lakewood|CA|-118.13|33.85|81,Lakewood|WA|-122.52|47.17|59,Lakewood|NJ|-74.22|40.1|53,Lakewood|SK|-106.59|52.11|51,Lakewood|OH|-81.8|41.48|50,Lancaster|CA|-118.14|34.7|161,Lancaster|PA|-76.31|40.04|59,Lancaster|OH|-82.6|39.71|39,Lancaster|TX|-96.76|32.59|38,Land O' Lakes|FL|-82.46|28.22|31,Langford|BC|-123.5|48.45|46,Langley|BC|-122.66|49.1|132,Lansing|MI|-84.56|42.73|112,Lansing|IL|-87.54|41.56|28,Laplace|LA|-90.48|30.07|29,Laramie|WY|-105.59|41.31|32,Laredo|TX|-99.51|27.51|256,Largo|FL|-82.79|27.91|81,Las Choapas|MX30|-94.1|17.91|42,Las Cruces|NM|-106.78|32.31|101,Las Guacamayas|MX16|-102.21|18.02|37,Las Pintitas|MX14|-103.3|20.56|26,Las Vegas|NV|-115.14|36.17|641,LaSalle|ON|-83.06|42.24|32,Lauderdale Lakes|FL|-80.21|26.17|34,Lauderhill|FL|-80.21|26.14|71,Laurel|MD|-76.85|39.1|26,Laval|QC|-73.69|45.57|438,Laval-des-Rapides|QC|-73.7|45.56|36,Lawndale|CA|-118.35|33.89|33,Lawrence|KS|-95.24|38.97|93,Lawrence|MA|-71.16|42.71|80,Lawrence|IN|-86.03|39.84|47,Lawrenceville|GA|-83.99|33.96|30,Lawson|SK|-106.64|52.16|34,Lawton|OK|-98.39|34.61|96,Layton|UT|-111.97|41.06|74,Le Plateau-Mont-Royal|QC|-73.58|45.53|105,Le Sud-Ouest|QC|-73.59|45.47|86,Le Vieux-Longueuil|QC|-73.51|45.54|135,League City|TX|-95.09|29.51|98,Leamington|ON|-82.6|42.05|35,Leander|TX|-97.85|30.58|59,Leavenworth|KS|-94.92|39.31|35,Leawood|KS|-94.62|38.97|34,Lebanon|TN|-86.29|36.21|30,Lebanon|PA|-76.41|40.34|25,Lee's Summit|MO|-94.38|38.91|95,Leesburg|VA|-77.56|39.12|51,Lehi|UT|-111.85|40.39|58,Lehigh Acres|FL|-81.62|26.63|86,Leisure City|FL|-80.43|25.5|26,Lemon Grove|CA|-117.03|32.74|26,Lemoore|CA|-119.78|36.3|25,Lenexa|KS|-94.73|38.95|52,Leominster|MA|-71.76|42.53|41,Leon de los Aldama|MX11|-101.68|21.12|1579,Lerdo|MX10|-103.52|25.54|79,Les Rivieres|QC|-71.27|46.82|77,Lethbridge|AB|-112.82|49.7|103,Levis|QC|-71.18|46.8|143,Levittown|PA|-74.83|40.16|52,Levittown|NY|-73.51|40.73|51,Lewiston|ME|-70.21|44.1|36,Lewiston|ID|-117.02|46.42|32,Lewiston Orchards|ID|-116.98|46.38|31,Lewisville|TX|-96.99|33.05|104,Lexington|KY|-84.48|37.99|320,Lexington|MA|-71.22|42.45|31,Lexington-Fayette|KY|-84.46|38.05|314,Liberty|MO|-94.42|39.25|30,Lima|OH|-84.11|40.74|37,Linares|MX19|-99.57|24.86|70,Lincoln|NE|-96.67|40.8|294,Lincoln|CA|-121.29|38.89|49,Lincoln Park|IL|-87.65|41.92|66,Lincoln Park|MI|-83.18|42.25|37,Lincoln Square|IL|-87.69|41.98|40,Linden|NJ|-74.24|40.62|42,Lindenhurst|NY|-73.37|40.69|27,Linton Hall|VA|-77.57|38.76|35,Little Elm|TX|-96.94|33.16|38,Little Havana|FL|-80.23|25.77|53,Little Rock|AR|-92.29|34.75|202,Littleton|CO|-105.02|39.61|46,Livermore|CA|-121.77|37.68|88,Livingston|NJ|-74.31|40.8|27,Livonia|MI|-83.35|42.37|94,Lloydminster|AB|-110.02|53.27|31,Lochearn|MD|-76.72|39.34|25,Lockport|IL|-88.06|41.59|25,Lodi|CA|-121.27|38.13|64,Logan|UT|-111.83|41.74|50,Logan Square|IL|-87.7|41.92|73,Loma Bonita|MX20|-95.88|18.11|40,Lombard|IL|-88.01|41.88|43,Lompoc|CA|-120.46|34.64|44,London|ON|-81.23|42.98|422,Long Beach|CA|-118.19|33.77|474,Long Beach|NY|-73.66|40.59|33,Long Branch|NJ|-73.99|40.3|30,Long Island City|NY|-73.95|40.74|25,Longfellow Community|MN|-93.23|44.94|29,Longmont|CO|-105.1|40.17|92,Longueuil|QC|-73.47|45.52|229,Longview|TX|-94.74|32.5|82,Longview|WA|-122.94|46.14|36,Lorain|OH|-82.18|41.45|63,Los Altos|CA|-122.11|37.39|30,Los Angeles|CA|-118.24|34.05|3820,Los Banos|CA|-120.85|37.06|37,Los Gatos|CA|-121.97|37.23|30,Los Mochis|MX25|-109|25.79|256,Los Reyes Acaquilpan|MX15|-98.98|19.36|85,Los Reyes de Salgado|MX16|-102.47|19.59|39,Louisville|KY|-85.76|38.25|624,Loveland|CO|-105.07|40.4|75,Lowell|MA|-71.32|42.63|110,Lower Sackville|NS|-63.68|44.78|51,Lower West Side|IL|-87.67|41.85|34,Lubbock|TX|-101.86|33.58|249,Lufkin|TX|-94.73|31.34|36,Lynchburg|VA|-79.14|37.41|79,Lynn|MA|-70.95|42.47|92,Lynnwood|WA|-122.32|47.82|36,Lynwood|CA|-118.21|33.93|71,Mableton|GA|-84.58|33.82|37,Macon|GA|-83.63|32.84|91,Macuspana|MX27|-92.6|17.76|32,Madera|CA|-120.06|36.96|64,Madison|WI|-89.4|43.07|280,Madison|AL|-86.75|34.7|46,Madison|MS|-90.12|32.46|25,Madison Heights|MI|-83.11|42.49|30,Magdalena Atlicpac|MX15|-98.95|19.37|26,Magdalena Contreras|MX09|-99.21|19.33|238,Magdalena de Kino|MX26|-110.96|30.63|26,Magna|UT|-112.1|40.71|26,Makakilo / Kapolei / Honokai Hale|HI|-158.1|21.34|46,Makiki / Lower Punchbowl / Tantalus|HI|-157.83|21.32|31,Malden|MA|-71.07|42.43|61,Malvern|ON|-79.22|43.81|43,Manassas|VA|-77.48|38.75|41,Manchester|NH|-71.45|43|110,Manchester|CT|-72.52|41.78|30,Manhattan|NY|-73.97|40.78|1487,Manhattan|KS|-96.57|39.18|56,Manhattan Beach|CA|-118.41|33.88|35,Manhattan Valley|NY|-73.97|40.79|38,Manitowoc|WI|-87.66|44.09|33,Mankato|MN|-94.01|44.16|41,Mansfield|TX|-97.14|32.56|64,Mansfield|OH|-82.52|40.76|46,Mansfield City|CT|-72.23|41.77|26,Manteca|CA|-121.22|37.8|75,Manzanillo|MX08|-104.34|19.12|159,Maple Grove|MN|-93.46|45.07|68,Maple Ridge|BC|-122.6|49.22|82,Maple Valley|WA|-122.05|47.39|25,Maplewood|MN|-93|44.95|40,Maplewood|NJ|-74.27|40.73|25,Marana|AZ|-111.23|32.44|41,Maravatio de Ocampo|MX16|-100.44|19.89|34,Marfil|MX11|-101.29|21|29,Margate|FL|-80.21|26.24|57,Maricopa|AZ|-112.05|33.06|48,Marietta|GA|-84.55|33.95|59,Marion|IA|-91.6|42.03|37,Marion|OH|-83.13|40.59|36,Marion|IN|-85.66|40.56|29,Markham|ON|-79.27|43.87|338,Marlboro|NJ|-74.25|40.32|40,Marlborough|MA|-71.55|42.35|39,Marpole|BC|-123.13|49.21|27,Marrero|LA|-90.1|29.9|33,Marshalltown|IA|-92.91|42.05|27,Martinez|CA|-122.13|38.02|38,Martinez|GA|-82.08|33.52|35,Martinez de la Torre|MX30|-97.06|20.07|60,Maryland Heights|MO|-90.43|38.71|27,Marysville|WA|-122.18|48.05|66,Maryvale|AZ|-112.18|33.5|208,Maryville|TN|-83.97|35.76|28,Mascouche|QC|-73.6|45.75|34,Mason|OH|-84.31|39.36|32,Mason City|IA|-93.2|43.15|27,Maspeth|NY|-73.91|40.72|48,Massillon|OH|-81.52|40.8|32,Matamoros|MX07|-103.23|25.53|52,Matehuala|MX24|-100.64|23.65|77,Mattapan|MA|-71.09|42.27|36,Matthews|NC|-80.72|35.12|30,Mauldin|SC|-82.31|34.78|25,Maywood|CA|-118.19|33.99|27,Mazatlan|MX25|-106.42|23.22|381,McAllen|TX|-98.23|26.2|140,McCully - Moiliili|HI|-157.83|21.29|28,McHenry|IL|-88.27|42.33|26,McKinney|TX|-96.62|33.2|162,McLean|VA|-77.18|38.93|48,McMinnville|OR|-123.2|45.21|33,Meadow Woods|FL|-81.37|28.39|25,Meads|KY|-82.71|38.41|288,Mechanicsville|VA|-77.37|37.61|36,Medford|OR|-122.88|42.33|79,Medford|MA|-71.11|42.42|57,Medicine Hat|AB|-110.68|50.04|63,Medina|OH|-81.86|41.14|26,Mehlville|MO|-90.32|38.51|28,Melbourne|FL|-80.61|28.08|84,Melchor Ocampo|MX15|-99.14|19.7|38,Melrose|MA|-71.07|42.46|27,Melrose Park|IL|-87.86|41.9|25,Memphis|TN|-90.05|35.15|633,Menifee|CA|-117.15|33.73|87,Menlo Park|CA|-122.18|37.45|33,Menomonee Falls|WI|-88.12|43.18|36,Mentor|OH|-81.34|41.67|46,Merced|CA|-120.48|37.3|82,Mercer Island|WA|-122.22|47.57|25,Mercerville-Hamilton Square|NJ|-74.67|40.23|26,Mercier-Hochelaga-Maisonneuve|QC|-73.55|45.57|142,Merida|MX31|-89.62|20.97|1201,Meriden|CT|-72.81|41.54|59,Meridian|ID|-116.39|43.61|90,Meridian|MS|-88.7|32.36|39,Merrillville|IN|-87.33|41.48|35,Merrimack|NH|-71.49|42.87|26,Merritt Island|FL|-80.69|28.36|34,Mesa|AZ|-111.82|33.42|471,Mesquite|TX|-96.6|32.77|144,Metairie|LA|-90.15|29.98|138,Metairie Terrace|LA|-90.16|29.98|142,Metepec|MX15|-99.6|19.26|28,Methuen|MA|-71.19|42.73|52,Metrotown|BC|-123|49.23|52,Mexicali|MX02|-115.45|32.63|1032,Mexico City|MX09|-99.13|19.43|12294,Miami|FL|-80.19|25.77|487,Miami Beach|FL|-80.13|25.79|92,Miami Gardens|FL|-80.25|25.94|113,Miami Lakes|FL|-80.31|25.91|30,Michigan City|IN|-86.9|41.71|31,Mid-City|CA|-118.36|34.04|83,Middle River|MD|-76.44|39.33|25,Middle Village|NY|-73.88|40.72|29,Middletown|NJ|-74.12|40.39|65,Middletown|OH|-84.4|39.52|48,Middletown|CT|-72.65|41.56|46,Middletown|NY|-74.42|41.45|27,Midland|TX|-102.08|32|132,Midland|MI|-84.25|43.62|42,Midvale|UT|-111.9|40.61|32,Midwest City|OK|-97.4|35.45|57,Miguel Aleman (La Doce)|MX26|-111.48|28.84|30,Miguel Hidalgo|MX09|-99.2|19.43|372,Milford|CT|-73.06|41.22|52,Milford|MA|-71.52|42.14|25,Milford Mill|MD|-76.77|39.35|29,Mililani Town|HI|-158.02|21.45|27,Millcreek|UT|-111.88|40.69|62,Milliken|ON|-79.3|43.82|26,Millville|NJ|-75.04|39.4|28,Milpitas|CA|-121.91|37.43|77,Milton|ON|-79.88|43.52|132,Milton|GA|-84.3|34.13|37,Milton|MA|-71.07|42.25|27,Milwaukee|WI|-87.91|43.04|563,Mimico|ON|-79.51|43.62|33,Minatitlan|MX30|-94.56|18|112,Minneapolis|MN|-93.26|44.98|410,Minnetonka|MN|-93.5|44.91|51,Minnetonka Mills|MN|-93.44|44.94|50,Minot|ND|-101.3|48.23|49,Mint Hill|NC|-80.65|35.18|25,Mira Mesa|CA|-117.14|32.92|70,Mirabel|QC|-74.08|45.65|34,Miramar|FL|-80.23|25.99|137,Miramar|MX28|-97.9|22.36|118,Misantla|MX30|-96.85|19.93|26,Mishawaka|IN|-86.16|41.66|48,Mission|TX|-98.33|26.22|83,Mission|BC|-122.3|49.13|41,Mission Bend|TX|-95.66|29.69|36,Mission District|CA|-122.42|37.76|47,Mission Viejo|CA|-117.67|33.6|97,Mississauga|ON|-79.66|43.58|717,Missoula|MT|-113.99|46.87|71,Missouri City|TX|-95.54|29.62|74,Mixquiahuala de Juarez|MX13|-99.21|20.23|25,Mobile|AL|-88.04|30.69|183,Modesto|CA|-121|37.64|211,Moline|IL|-90.52|41.51|42,Monclova|MX07|-101.42|26.91|215,Moncton|NB|-64.8|46.09|86,Monroe|LA|-92.12|32.51|49,Monroe|NC|-80.55|34.99|34,Monroeville|PA|-79.79|40.42|28,Monrovia|CA|-118|34.15|37,Montclair|NJ|-74.21|40.83|39,Montclair|CA|-117.69|34.08|38,Montebello|CA|-118.11|34.01|63,Montemorelos|MX19|-99.83|25.19|67,Monterey|CA|-121.89|36.6|28,Monterey Park|CA|-118.12|34.06|61,Monterrey|MX19|-100.32|25.68|1135,Montgomery|AL|-86.3|32.37|195,Montgomery Village|MD|-77.2|39.18|32,Montreal|QC|-73.59|45.51|1762,Montreal-Nord|QC|-73.63|45.6|86,Moore|OK|-97.49|35.34|60,Mooresville|NC|-80.81|35.58|36,Moorhead|MN|-96.77|46.87|42,Moorpark|CA|-118.88|34.29|36,Moose Jaw|SK|-105.53|50.4|33,Morelia|MX16|-101.18|19.7|743,Moreno Valley|CA|-117.23|33.94|204,Morgan Hill|CA|-121.65|37.13|42,Morgantown|WV|-79.96|39.63|30,Morningside Heights|NY|-73.96|40.81|55,Moroleon|MX11|-101.19|20.13|43,Morris Heights|NY|-73.92|40.85|40,Morristown|TN|-83.29|36.21|29,Moscow|ID|-117|46.73|25,Mott Haven|NY|-73.92|40.81|51,Mount Juliet|TN|-86.52|36.2|31,Mount Laurel|NJ|-74.89|39.93|41,Mount Lebanon|PA|-80.05|40.36|32,Mount Olive-Silverstone-Jamestown|ON|-79.59|43.75|32,Mount Pleasant|SC|-79.86|32.79|81,Mount Pleasant|DC|-77.04|38.93|35,Mount Pleasant|BC|-123.1|49.27|33,Mount Pleasant|WI|-87.86|42.7|26,Mount Pleasant|MI|-84.77|43.6|26,Mount Pleasant West|ON|-79.39|43.7|29,Mount Prospect|IL|-87.94|42.07|54,Mount Vernon|NY|-73.84|40.91|68,Mount Vernon|WA|-122.33|48.42|34,Mountain View|CA|-122.08|37.39|80,Moyotzingo|MX21|-98.4|19.25|27,Mt Pleasant|BC|-123.1|49.26|32,Muncie|IN|-85.39|40.19|70,Mundelein|IL|-88|42.26|31,Murfreesboro|TN|-86.39|35.85|165,Murray|UT|-111.89|40.67|49,Murrieta|CA|-117.21|33.55|109,Muskegon|MI|-86.25|43.23|38,Muskogee|OK|-95.37|35.75|38,Myrtle Beach|SC|-78.89|33.69|31,Nacogdoches|TX|-94.66|31.6|33,Nampa|ID|-116.56|43.54|89,Nanaimo|BC|-123.94|49.17|90,Nanchital de Lazaro Cardenas del Rio|MX30|-94.41|18.07|25,Napa|CA|-122.29|38.3|80,Naperville|IL|-88.15|41.79|147,Nashua|NH|-71.47|42.77|87,Nashville|TN|-86.78|36.17|689,Natick|MA|-71.35|42.28|32,National City|CA|-117.1|32.68|61,Naucalpan de Juarez|MX15|-99.24|19.48|834,Naugatuck|CT|-73.05|41.49|31,Navarre|FL|-86.86|30.4|31,Navojoa|MX26|-109.44|27.07|113,Navolato|MX25|-107.7|24.77|29,Near North Side|IL|-87.63|41.9|85,Needham|MA|-71.23|42.28|28,Neenah|WI|-88.46|44.19|25,Nepean|ON|-75.72|45.34|180,Neufchatel-Est-Lebourgneuf|QC|-71.34|46.85|37,New Albany|IN|-85.82|38.29|36,New Bedford|MA|-70.93|41.64|101,New Berlin|WI|-88.11|42.98|39,New Bern|NC|-77.04|35.11|30,New Braunfels|TX|-98.12|29.7|70,New Britain|CT|-72.78|41.66|72,New Brunswick|NJ|-74.45|40.49|57,New City|IL|-87.66|41.81|40,New City|NY|-73.99|41.15|33,New Haven|CT|-72.93|41.31|130,New Iberia|LA|-91.82|30|30,New Lenox|IL|-87.97|41.51|25,New London|CT|-72.1|41.36|27,New Orleans|LA|-90.08|29.95|362,New Rochelle|NY|-73.78|40.91|79,New South Memphis|TN|-90.06|35.09|641,New Westminster|BC|-122.91|49.21|78,New York City|NY|-74.01|40.71|8804,Newark|NJ|-74.17|40.74|281,Newark|OH|-82.4|40.06|47,Newark|CA|-122.04|37.53|45,Newark|DE|-75.75|39.68|33,Newburgh|NY|-74.01|41.5|28,Newington|CT|-72.72|41.7|30,Newmarket|ON|-79.47|44.05|91,Newnan|GA|-84.8|33.38|37,Newport Beach|CA|-117.93|33.62|87,Newport News|VA|-76.43|36.98|186,Newton|BC|-122.85|49.13|159,Newton|MA|-71.21|42.34|88,Niagara|ON|-79.41|43.64|31,Niagara Falls|ON|-79.07|43.1|99,Niagara Falls|NY|-79.06|43.09|48,Nicholasville|KY|-84.57|37.88|29,Nicolas Romero|MX15|-99.31|19.64|281,Niles|IL|-87.8|42.02|29,Noblesville|IN|-86.01|40.05|59,Nogales|MX26|-110.94|31.31|264,Norco|CA|-117.55|33.93|26,Norfolk|VA|-76.29|36.85|238,Norfolk County|ON|-80.38|42.83|60,Normal|IL|-88.99|40.51|54,Norman|OK|-97.44|35.22|128,Norristown|PA|-75.34|40.12|34,North Andover|MA|-71.14|42.7|28,North Bay|ON|-79.47|46.32|50,North Bel Air|MD|-76.35|39.54|33,North Bergen|NJ|-74.01|40.8|63,North Bethesda|MD|-77.12|39.04|43,North Brunswick|NJ|-74.48|40.45|43,North Center|IL|-87.68|41.95|34,North Charleston|SC|-79.97|32.85|108,North Chicago|IL|-87.84|42.33|29,North Chicopee|MA|-72.6|42.18|55,North Cowichan|BC|-123.69|48.84|29,North Creek|WA|-122.18|47.82|26,North Delta|BC|-122.92|49.17|60,North Fort Myers|FL|-81.88|26.67|39,North Highlands|CA|-121.37|38.69|42,North Hills|CA|-118.48|34.24|56,North Hollywood|CA|-118.38|34.17|64,North Kingstown|RI|-71.47|41.55|28,North La Crosse|WI|-91.25|43.85|50,North Las Vegas|NV|-115.12|36.2|234,North Lauderdale|FL|-80.23|26.22|43,North Lawndale|IL|-87.72|41.86|35,North Little Rock|AR|-92.27|34.77|66,North Miami|FL|-80.19|25.89|62,North Miami Beach|FL|-80.16|25.93|43,North Olmsted|OH|-81.92|41.42|32,North Peoria|IL|-89.58|40.72|113,North Port|FL|-82.24|27.04|62,North Providence|RI|-71.47|41.85|33,North Richland Hills|TX|-97.23|32.83|69,North Ridgeville|OH|-82.02|41.39|32,North Royalton|OH|-81.72|41.31|30,North Stamford|CT|-73.54|41.14|121,North Tonawanda|NY|-78.86|43.04|30,North Vancouver|BC|-123.07|49.32|88,Northampton|MA|-72.64|42.33|28,Northbrook|IL|-87.83|42.13|33,Northglenn|CO|-104.99|39.89|39,Northridge|CA|-118.54|34.23|68,Norwalk|CA|-118.08|33.9|107,Norwalk|CT|-73.41|41.12|88,Norwich|CT|-72.08|41.52|39,Norwood|MA|-71.2|42.19|28,Notre-Dame-de-Grace|QC|-73.61|45.48|68,Novato|CA|-122.57|38.11|55,Novi|MI|-83.48|42.48|58,Nueva Italia de Ruiz|MX16|-102.09|19.02|32,Nueva Rosita|MX07|-101.22|27.94|38,Nuevo Casas Grandes|MX06|-107.91|30.42|55,Nuevo Laredo|MX28|-99.52|27.48|416,Nuevo Mexico|MX14|-103.44|20.76|28,Nutana Sector|SK|-106.64|52.1|72,Nutley|NJ|-74.16|40.82|27,O'Fallon|MO|-90.7|38.81|85,O'Fallon|IL|-89.91|38.59|29,Oak Creek|WI|-87.86|42.89|35,Oak Forest|IL|-87.74|41.6|28,Oak Hill|VA|-77.4|38.93|33,Oak Lawn|IL|-87.76|41.71|56,Oak Park|IL|-87.78|41.89|52,Oak Park|MI|-83.18|42.46|29,Oak Ridge|TN|-84.27|36.01|29,Oakdale|MN|-92.96|44.96|28,Oakland|CA|-122.27|37.8|419,Oakland Park|FL|-80.13|26.17|44,Oakley|CA|-121.71|38|39,Oakton|VA|-77.3|38.88|34,Oakville|ON|-79.68|43.45|213,Oakville|MO|-90.3|38.47|36,Oaxaca|MX20|-96.73|17.06|255,Ocala|FL|-82.14|29.19|58,Oceanside|CA|-117.38|33.2|175,Oceanside|NY|-73.64|40.64|32,Ocoee|FL|-81.54|28.57|43,Ocosingo|MX05|-92.09|16.91|41,Ocotlan|MX14|-102.77|20.35|83,Ocoyoacac|MX15|-99.46|19.27|26,Ocozocoautla de Espinosa|MX05|-93.37|16.76|39,Odenton|MD|-76.7|39.08|37,Odessa|TX|-102.37|31.85|114,Ogden|UT|-111.97|41.22|85,Oildale|CA|-119.02|35.42|32,Ojinaga|MX06|-104.54|29.57|28,Ojo de Agua|MX15|-99.01|19.68|386,Okanagan Mission|BC|-119.48|49.82|38,Oklahoma City|OK|-97.52|35.47|681,Okotoks|AB|-113.98|50.73|30,Olathe|KS|-94.82|38.88|134,Olive Branch|MS|-89.83|34.96|36,Olney|PA|-75.12|40.04|39,Olney|MD|-77.07|39.15|33,Olympia|WA|-122.9|47.04|55,Omaha|NE|-95.94|41.26|486,Ontario|CA|-117.65|34.06|171,Opelika|AL|-85.38|32.65|29,Opportunity|WA|-117.24|47.65|25,Orange|CA|-117.85|33.79|140,Orange|NJ|-74.23|40.77|34,Orangevale|CA|-121.23|38.68|33,Orangeville|ON|-80.1|43.92|30,Orcutt|CA|-120.44|34.87|28,Oregon City|OR|-122.61|45.36|35,Orem|UT|-111.69|40.3|94,Orillia|ON|-79.42|44.61|31,Orizaba|MX30|-97.1|18.85|123,Orland Park|IL|-87.85|41.63|58,Orlando|FL|-81.38|28.54|334,Orleans|ON|-75.5|45.46|125,Ormond Beach|FL|-81.06|29.29|40,Oro Valley|AZ|-110.97|32.39|45,Oshawa|ON|-78.85|43.9|175,Oshkosh|WI|-88.54|44.02|66,Ossining|NY|-73.86|41.16|25,Oswego|IL|-88.35|41.68|33,Ottawa|ON|-75.7|45.41|1017,Ottawa South|ON|-75.69|45.39|125,Overbrook|PA|-75.24|39.99|32,Overland Park|KS|-94.67|38.98|186,Oviedo|FL|-81.21|28.67|38,Owasso|OK|-95.85|36.27|34,Owatonna|MN|-93.23|44.08|25,Owensboro|KY|-87.11|37.77|59,Owings Mills|MD|-76.78|39.42|30,Oxford Circle|PA|-75.07|40.05|48,Oxnard|CA|-119.18|34.2|207,Oxon Hill-Glassmanor|MD|-76.97|38.8|35,Ozone Park|NY|-73.84|40.68|53,Pabellon de Arteaga|MX01|-102.28|22.15|28,Pachuca de Soto|MX13|-98.73|20.12|256,Pacifica|CA|-122.49|37.61|39,Pahrump|NV|-115.98|36.21|36,Palatine|IL|-88.03|42.11|69,Palenque|MX05|-91.98|17.51|42,Palm Bay|FL|-80.59|28.03|119,Palm Beach Gardens|FL|-80.14|26.82|52,Palm Coast|FL|-81.21|29.58|82,Palm Desert|CA|-116.38|33.72|51,Palm Harbor|FL|-82.76|28.08|57,Palm Springs|CA|-116.55|33.83|47,Palmdale|CA|-118.12|34.58|158,Palo Alto|CA|-122.14|37.44|66,Panama City|FL|-85.66|30.16|38,Panorama Hills|AB|-114.08|51.15|25,Panuco|MX30|-98.18|22.05|40,Papantla de Olarte|MX30|-97.32|20.45|53,Paracho de Verduzco|MX16|-102.05|19.65|37,Paradise|NV|-115.15|36.1|223,Paradise|CA|-121.62|39.76|26,Paragould|AR|-90.5|36.06|27,Paraiso|MX27|-93.21|18.4|86,Paramount|CA|-118.16|33.89|55,Paramus|NJ|-74.08|40.94|26,Parc-Extension|QC|-73.63|45.53|33,Park Ridge|IL|-87.84|42.01|37,Park Slope|NY|-73.99|40.67|65,Parkchester|NY|-73.86|40.84|65,Parker|CO|-104.76|39.52|49,Parkersburg|WV|-81.56|39.27|30,Parkland|WA|-122.43|47.16|35,Parkland|FL|-80.24|26.31|30,Parkville|MD|-76.54|39.38|30,Parkwoods-Donalda|ON|-79.33|43.76|34,Parma|OH|-81.72|41.4|79,Parras de la Fuente|MX07|-102.18|25.44|33,Parsippany|NJ|-74.43|40.86|51,Pasadena|TX|-95.21|29.69|153,Pasadena|CA|-118.14|34.15|142,Pasco|WA|-119.1|46.24|69,Paso Robles|CA|-120.69|35.63|27,Passaic|NJ|-74.13|40.86|71,Paterson|NJ|-74.17|40.92|147,Patzcuaro|MX16|-101.61|19.51|55,Pawtucket|RI|-71.38|41.88|71,Peabody|MA|-70.93|42.53|52,Peachtree City|GA|-84.6|33.4|35,Peachtree Corners|GA|-84.22|33.97|40,Pearl|MS|-90.13|32.27|26,Pearl City|HI|-157.98|21.4|47,Pearland|TX|-95.29|29.56|108,Pekin|IL|-89.64|40.57|33,Pembroke Pines|FL|-80.22|26|166,Penjamo|MX11|-101.72|20.43|40,Penn Hills|PA|-79.84|40.5|44,Pennsauken|NJ|-75.06|39.96|36,Pennsport|PA|-75.15|39.93|26,Pensacola|FL|-87.22|30.42|53,Penticton|BC|-119.59|49.48|33,Peoria|AZ|-112.24|33.58|190,Peoria|IL|-89.59|40.69|115,Perote|MX30|-97.24|19.56|37,Perris|CA|-117.23|33.78|74,Perry Hall|MD|-76.46|39.41|28,Perth Amboy|NJ|-74.27|40.51|52,Petaluma|CA|-122.64|38.23|60,Peterborough|ON|-78.32|44.3|85,Petersburg|VA|-77.4|37.23|32,Pflugerville|TX|-97.62|30.44|57,Pharr|TX|-98.18|26.19|76,Phenix City|AL|-85|32.47|37,Philadelphia|PA|-75.16|39.95|1573,Phoenix|AZ|-112.07|33.45|1650,Pickering|ON|-79.13|43.9|91,Pico Rivera|CA|-118.1|33.98|64,Piedras Negras|MX07|-100.52|28.7|150,Pierrefonds|QC|-73.89|45.46|59,Pierrefonds-Roxboro|QC|-73.84|45.5|73,Pijijiapan|MX05|-93.21|15.69|50,Pikesville|MD|-76.72|39.37|30,Pine Bluff|AR|-92|34.23|44,Pine Hills|FL|-81.45|28.56|60,Pinellas Park|FL|-82.7|27.84|51,Pinotepa Nacional|MX20|-98.05|16.34|29,Piscataway|NJ|-74.4|40.5|56,Pittsburg|CA|-121.88|38.03|69,Pittsburgh|PA|-80|40.44|304,Pittsfield|MA|-73.25|42.45|43,Placentia|CA|-117.87|33.87|52,Plainfield|NJ|-74.41|40.63|51,Plainfield|IL|-88.2|41.63|42,Plainfield|IN|-86.4|39.7|30,Plainview|NY|-73.47|40.78|26,Plano|TX|-96.7|33.02|283,Plant City|FL|-82.11|28.02|37,Plantation|FL|-80.23|26.13|92,Playa del Carmen|MX23|-87.08|20.63|149,Pleasant Grove|UT|-111.74|40.36|38,Pleasant Hill|CA|-122.06|37.95|34,Pleasanton|CA|-121.87|37.66|79,Pleasure Ridge Park|KY|-85.86|38.15|25,Plum|PA|-79.75|40.5|27,Plymouth|MN|-93.46|45.01|75,Pocatello|ID|-112.45|42.87|54,Poinciana|FL|-81.46|28.14|53,Pointe-Claire|QC|-73.82|45.45|30,Polanco|MX09|-99.2|19.43|50,Pomona|CA|-117.75|34.06|153,Pompano Beach|FL|-80.12|26.24|107,Ponte Vedra Beach|FL|-81.39|30.24|35,Pontiac|MI|-83.29|42.64|59,Port Arthur|TX|-93.94|29.89|55,Port Charlotte|FL|-82.09|26.98|54,Port Chester|NY|-73.67|41|29,Port Coquitlam|BC|-122.77|49.27|58,Port Huron|MI|-82.42|42.97|29,Port Moody|BC|-122.82|49.28|27,Port Orange|FL|-81|29.14|59,Port Richmond|PA|-75.1|39.99|27,Port Saint Lucie|FL|-80.35|27.29|164,Portage|MI|-85.58|42.2|48,Portage|IN|-87.18|41.58|36,Portage Park|IL|-87.77|41.96|64,Porterville|CA|-119.02|36.07|56,Porticos de San Antonio|MX02|-117.03|32.44|34,Portland|OR|-122.68|45.52|652,Portland|ME|-70.26|43.66|66,Portsmouth|VA|-76.3|36.84|96,Portsmouth Heights|VA|-76.37|36.82|99,Post Falls|ID|-116.95|47.72|30,Potomac|MD|-77.21|39.02|44,Poughkeepsie|NY|-73.92|41.7|30,Poway|CA|-117.04|32.96|50,Poza Rica de Hidalgo|MX30|-97.46|20.53|185,Prairieville|LA|-90.97|30.3|26,Prattville|AL|-86.46|32.46|35,Prescott|AZ|-112.47|34.54|41,Prescott Valley|AZ|-112.32|34.61|42,Prince Albert|SK|-105.77|53.2|35,Prince Edward|ON|-77.25|44|25,Prince George|BC|-122.75|53.92|78,Princeton|FL|-80.41|25.54|39,Princeton|NJ|-74.66|40.35|29,Prior Lake|MN|-93.42|44.71|25,Progreso|MX31|-89.66|21.28|37,Providence|RI|-71.41|41.82|190,Provo|UT|-111.66|40.23|115,Puebla|MX21|-98.21|19.05|1692,Pueblo|CO|-104.61|38.25|109,Pueblo West|CO|-104.72|38.35|29,Puerto Escondido|MX20|-97.07|15.86|29,Puerto Penasco|MX26|-113.54|31.32|62,Puerto Vallarta|MX14|-105.23|20.62|224,Pullman|WA|-117.18|46.73|32,Purisima de Bustos|MX11|-101.88|21.03|43,Puruandiro|MX16|-101.52|20.09|30,Puyallup|WA|-122.29|47.19|39,Quebec|QC|-71.21|46.81|531,Queen Creek|AZ|-111.63|33.25|34,Queens|NY|-73.84|40.68|2316,Queens Village|NY|-73.74|40.73|51,Queensbury|NY|-73.61|43.38|27,Quincy|MA|-71|42.25|93,Quincy|IL|-91.41|39.94|40,Quinte West|ON|-77.57|44.18|43,Racine|WI|-87.78|42.73|77,Radnor|PA|-75.36|40.05|30,Rahway|NJ|-74.28|40.61|29,Raleigh|NC|-78.64|35.77|482,Ramos Arizpe|MX07|-100.95|25.54|66,Ramsey|MN|-93.45|45.26|25,Rancho Cordova|CA|-121.3|38.59|71,Rancho Cucamonga|CA|-117.59|34.11|175,Rancho Palos Verdes|CA|-118.39|33.74|42,Rancho Penasquitos|CA|-117.12|32.96|60,Rancho Santa Margarita|CA|-117.6|33.64|49,Randallstown|MD|-76.8|39.37|32,Randolph|MA|-71.04|42.16|32,Randolph|NJ|-74.58|40.85|25,Rapid City|SD|-103.23|44.08|73,Raytown|MO|-94.46|39.01|29,Reading|PA|-75.93|40.34|87,Red Deer|AB|-113.8|52.27|100,Redan|GA|-84.13|33.75|33,Redding|CA|-122.39|40.59|91,Redford|MI|-83.3|42.38|49,Redlands|CA|-117.18|34.06|71,Redmond|WA|-122.12|47.67|60,Redmond|OR|-121.17|44.27|28,Redondo Beach|CA|-118.39|33.85|68,Redwood City|CA|-122.24|37.49|85,Reedley|CA|-119.45|36.6|25,Reforma|MX05|-93.15|17.86|26,Regina|SK|-104.62|50.45|226,Rego Park|NY|-73.85|40.73|43,Reisterstown|MD|-76.83|39.47|25,Renfrew-Collingwood|BC|-123.04|49.24|51,Reno|NV|-119.81|39.53|264,Renton|WA|-122.22|47.48|100,Repentigny|QC|-73.45|45.74|84,Reseda|CA|-118.54|34.2|65,Reston|VA|-77.34|38.97|58,Revere|MA|-71.01|42.41|53,Rexburg|ID|-111.79|43.83|27,Reynoldsburg|OH|-82.81|39.95|37,Reynosa|MX28|-98.28|26.08|589,Rhawnhurst|PA|-75.06|40.06|25,Rialto|CA|-117.37|34.11|103,Richardson|TX|-96.73|32.95|110,Richfield|MN|-93.28|44.88|36,Richland|WA|-119.28|46.29|54,Richmond|VA|-77.46|37.55|226,Richmond|BC|-123.14|49.17|209,Richmond|CA|-122.35|37.94|109,Richmond|IN|-84.89|39.83|35,Richmond|KY|-84.29|37.75|33,Richmond Hill|ON|-79.44|43.87|202,Richmond Hill|NY|-73.83|40.7|98,Richmond West|FL|-80.43|25.61|35,Ridgecrest|CA|-117.67|35.62|28,Ridgewood|NY|-73.91|40.7|69,Ridgewood|NJ|-74.12|40.98|25,Rimouski|QC|-68.52|48.45|42,Rincon de Romos|MX01|-102.32|22.23|27,Rio Blanco|MX30|-97.16|18.83|40,Rio de Teapa|MX27|-92.9|17.77|26,Rio Grande|MX32|-103.03|23.83|32,Rio Rancho|NM|-106.66|35.23|87,Rioverde|MX24|-99.99|21.93|53,Riverside|CA|-117.4|33.95|317,Riverton|UT|-111.94|40.52|41,Riverview|FL|-82.33|27.87|71,Riviera Beach|FL|-80.06|26.78|34,Riviere-des-Prairies-Pointe-aux-Trembles|QC|-73.58|45.64|113,Roanoke|VA|-79.94|37.27|100,Rochester|NY|-77.62|43.15|209,Rochester|MN|-92.47|44.02|112,Rochester|NH|-70.98|43.3|30,Rochester Hills|MI|-83.15|42.66|73,Rock Forest|QC|-72|45.36|35,Rock Hill|SC|-81.03|34.92|71,Rock Island|IL|-90.58|41.51|38,Rockford|IL|-89.09|42.27|148,Rocklin|CA|-121.24|38.79|61,Rockville|MD|-77.15|39.08|66,Rockwall|TX|-96.46|32.93|42,Rocky Mount|NC|-77.79|35.94|55,Rogers|AR|-94.12|36.33|63,Rogers Park|IL|-87.67|42.01|54,Rohnert Park|CA|-122.7|38.34|42,Rome|GA|-85.16|34.26|36,Rome|NY|-75.46|43.21|32,Romeoville|IL|-88.09|41.65|39,Rosarito|MX02|-117.05|32.36|100,Rosedale|NY|-73.74|40.66|25,Rosemead|CA|-118.07|34.08|54,Rosemont-La Petite-Patrie|QC|-73.61|45.54|146,Rosenberg|TX|-95.81|29.56|35,Roseville|CA|-121.29|38.75|130,Roseville|MI|-82.94|42.5|47,Roseville|MN|-93.16|45.01|35,Roslindale|MA|-71.12|42.29|27,Roswell|GA|-84.36|34.02|94,Roswell|NM|-104.52|33.39|48,Rouge|ON|-79.17|43.8|46,Round Lake Beach|IL|-88.09|42.37|27,Round Rock|TX|-97.68|30.51|115,Rowland Heights|CA|-117.91|33.98|48,Rowlett|TX|-96.56|32.9|60,Roy|UT|-112.03|41.16|37,Royal Oak|MI|-83.14|42.49|59,Royal Palm Beach|FL|-80.23|26.71|37,Rubidoux|CA|-117.41|34|34,Russellville|AR|-93.13|35.28|29,Rutland|BC|-119.39|49.9|34,Saanich|BC|-123.37|48.55|117,Sabinas|MX07|-101.12|27.86|54,Sacramento|CA|-121.49|38.58|524,Saginaw|MI|-83.95|43.42|49,Saguenay|QC|-71.07|48.42|148,Sahuarita|AZ|-110.96|31.96|25,Sahuayo de Morelos|MX16|-102.72|20.06|64,Saint Charles|MO|-90.48|38.78|65,Saint Charles|MD|-76.94|38.6|36,Saint Clair Shores|MI|-82.89|42.5|59,Saint Cloud|MN|-94.16|45.56|65,Saint Cloud|FL|-81.28|28.25|35,Saint-Constant|QC|-73.57|45.37|27,Saint-Eustache|QC|-73.91|45.56|42,Saint George|UT|-113.58|37.1|72,Saint-Georges|QC|-70.67|46.11|31,Saint-Hubert|QC|-73.42|45.5|82,Saint-Hyacinthe|QC|-72.96|45.63|50,Saint-Jean-sur-Richelieu|QC|-73.26|45.31|98,Saint-Jerome|QC|-74|45.78|54,Saint John|NB|-66.06|45.27|71,Saint Joseph|MO|-94.85|39.77|76,Saint-Laurent|QC|-73.67|45.5|98,Saint-Leonard|QC|-73.6|45.59|79,Saint-Louis-de-Terrebonne|QC|-73.79|45.7|119,Saint Louis Park|MN|-93.35|44.95|45,Saint-Michel|QC|-73.62|45.57|56,Saint Paul|MN|-93.09|44.94|303,Saint Peters|MO|-90.63|38.8|52,Sainte-Foy|QC|-71.29|46.78|111,Sainte-Julie|QC|-73.33|45.58|29,Sainte-Therese|QC|-73.83|45.64|25,Salaberry-de-Valleyfield|QC|-74.13|45.25|42,Salamanca|MX11|-101.2|20.57|160,Salem|OR|-123.04|44.94|175,Salem|MA|-70.9|42.52|42,Salem|NH|-71.2|42.79|29,Salem|VA|-80.05|37.29|25,Salina|KS|-97.61|38.84|47,Salina Cruz|MX20|-95.19|16.18|84,Salinas|CA|-121.66|36.68|157,Salisbury|NC|-80.47|35.67|34,Salisbury|MD|-75.6|38.36|32,Salt Lake City|UT|-111.89|40.76|215,Saltillo|MX07|-100.98|25.43|709,Salvatierra|MX11|-100.88|20.22|37,Sammamish|WA|-122.08|47.64|52,San Agustin|MX14|-103.47|20.55|30,San Andres Cholula|MX21|-98.3|19.05|39,San Andres Tuxtla|MX30|-95.21|18.45|61,San Angelo|TX|-100.44|31.46|99,San Antonio|TX|-98.49|29.42|1526,San Bernardino|CA|-117.29|34.11|216,San Bernardino Tlaxcalancingo|MX21|-98.28|19.03|54,San Bruno|CA|-122.41|37.63|43,San Buenaventura|MX15|-98.86|19.3|44,San Carlos|CA|-122.26|37.51|29,San Clemente|CA|-117.61|33.43|65,San Cristobal de las Casas|MX05|-92.64|16.73|215,San Diego|CA|-117.16|32.72|1404,San Dimas|CA|-117.81|34.11|34,San Felipe|MX11|-101.22|21.48|28,San Fernando|MX28|-98.15|24.85|29,San Francisco|CA|-122.42|37.77|827,San Francisco Acuautla|MX15|-98.86|19.35|27,San Francisco del Rincon|MX11|-101.86|21.02|71,San Francisco Tesistan|MX14|-103.48|20.8|62,San Gabriel|CA|-118.11|34.1|40,San Isidro|MX15|-98.95|19.34|33,San Jacinto|CA|-116.96|33.78|46,San Jeronimo|MX15|-99.59|19.28|26,San Jeronimo Cuatro Vientos|MX15|-98.84|19.29|36,San Jose|CA|-121.89|37.34|997,San Jose del Cabo|MX03|-109.7|23.05|136,San Jose del Castillo|MX14|-103.24|20.51|39,San Jose Guadalupe Otzacatipan|MX15|-99.59|19.34|31,San Juan|TX|-98.16|26.19|36,San Juan Capistrano|CA|-117.66|33.5|36,San Juan de los Lagos|MX14|-102.33|21.25|48,San Juan del Rio|MX22|-100|20.39|138,San Leandro|CA|-122.16|37.72|90,San Luis|AZ|-114.78|32.49|31,San Luis de la Paz|MX11|-100.51|21.3|49,San Luis Obispo|CA|-120.66|35.28|47,San Luis Potosi|MX24|-100.97|22.15|722,San Luis Rio Colorado|MX26|-114.77|32.46|176,San Marcos|CA|-117.17|33.14|92,San Marcos|TX|-97.94|29.88|60,San Martin Azcatepec|MX15|-98.97|19.69|35,San Martin Texmelucan de Labastida|MX21|-98.44|19.28|155,San Mateo|CA|-122.33|37.56|103,San Mateo Atenco|MX15|-99.53|19.27|67,San Miguel Ajusco|MX09|-99.2|19.22|29,San Miguel de Allende|MX11|-100.74|20.92|174,San Miguel Topilejo|MX09|-99.14|19.2|34,San Nicolas de los Garza|MX19|-100.3|25.74|412,San Pablo|CA|-122.35|37.96|30,San Pablo Autopan|MX15|-99.66|19.36|35,San Pablo de las Salinas|MX15|-99.09|19.67|156,San Pedro|CA|-118.29|33.74|83,San Pedro|MX07|-102.98|25.76|48,San Pedro|MX07|-103.22|25.43|41,San Pedro Garza Garcia|MX19|-100.4|25.66|132,San Rafael|CA|-122.53|37.97|59,San Ramon|CA|-121.98|37.78|76,San Salvador Tizatlalli|MX15|-99.59|19.26|61,San Sebastian el Grande|MX14|-103.43|20.53|28,San Tan Valley|AZ|-111.53|33.19|81,Sanctorum|MX21|-98.25|19.1|27,Sandusky|OH|-82.71|41.45|25,Sandy|UT|-111.88|40.59|87,Sandy Hills|UT|-111.85|40.58|89,Sandy Springs|GA|-84.38|33.92|105,Sanford|FL|-81.27|28.8|58,Sanford|NC|-79.18|35.48|29,Santa Ana|CA|-117.87|33.75|310,Santa Ana Chiautempan|MX29|-98.19|19.31|48,Santa Barbara|CA|-119.7|34.42|91,Santa Catarina|MX19|-100.46|25.67|304,Santa Clara|CA|-121.96|37.35|126,Santa Clarita|CA|-118.54|34.39|182,Santa Cruz|CA|-122.03|36.97|64,Santa Cruz Xoxocotlan|MX20|-96.74|17.03|67,Santa Fe|NM|-105.94|35.69|87,Santa Isabel|MX02|-115.57|32.63|29,Santa Maria|CA|-120.44|34.95|105,Santa Maria Chimalhuacan|MX15|-98.95|19.42|70,Santa Monica|CA|-118.49|34.02|93,Santa Paula|CA|-119.06|34.35|30,Santa Rosa|CA|-122.71|38.44|178,Santa Rosa Beach|FL|-86.23|30.4|32,Santa Rosalia de Camargo|MX06|-105.17|27.68|40,Santa Teresa|MX15|-99.23|19.84|36,Santee|CA|-116.97|32.84|57,Santiago|MX19|-100.15|25.43|36,Santiago de Queretaro|MX22|-100.39|20.59|1594,Santiago Papasquiaro|MX10|-105.42|25.04|26,Santiago Teyahualco|MX15|-99.12|19.66|53,Santo Domingo Tehuantepec|MX20|-95.24|16.32|67,Sarasota|FL|-82.53|27.34|55,Saratoga|CA|-122.02|37.26|30,Saratoga Springs|NY|-73.78|43.08|27,Saratoga Springs|UT|-111.9|40.35|25,Sarnia|ON|-82.4|42.98|72,Saskatoon|SK|-106.67|52.13|266,Saugus|MA|-71.01|42.46|26,Sault Ste. Marie|ON|-84.33|46.52|72,Savage|MN|-93.34|44.78|30,Savannah|GA|-81.1|32.08|147,Sawtelle|CA|-118.45|34.04|39,Sayreville|NJ|-74.36|40.46|44,Sayreville Junction|NJ|-74.33|40.47|42,Sayula|MX14|-103.6|19.88|26,Schaumburg|IL|-88.08|42.03|74,Schenectady|NY|-73.94|42.81|65,Schererville|IN|-87.45|41.48|28,Schertz|TX|-98.27|29.55|43,Scottsdale|AZ|-111.9|33.51|236,Scranton|PA|-75.66|41.41|77,Seaside|CA|-121.85|36.61|33,SeaTac|WA|-122.29|47.45|28,Seattle|WA|-122.33|47.61|780,Security-Widefield|CO|-104.71|38.75|32,Seguin|TX|-97.96|29.57|27,Sept-Iles|QC|-66.38|50.2|28,Severn|MD|-76.7|39.14|44,Severna Park|MD|-76.55|39.07|37,Sewell|NJ|-75.14|39.77|37,Shaker Heights|OH|-81.54|41.47|27,Shakopee|MN|-93.53|44.8|39,Shawinigan|QC|-72.75|46.57|38,Shawnee|KS|-94.72|39.04|65,Shawnee|OK|-96.93|35.33|31,Sheboygan|WI|-87.71|43.75|48,Sheepshead Bay|NY|-73.94|40.59|122,Shelby|MI|-83.03|42.67|74,Shelton|CT|-73.09|41.32|41,Sherbrooke|QC|-71.9|45.4|129,Sherman|TX|-96.61|33.64|40,Sherman Oaks|CA|-118.45|34.15|52,Sherwood|AR|-92.22|34.82|30,Sherwood Park|AB|-113.32|53.52|70,Shirley|NY|-72.87|40.8|27,Shoreline|WA|-122.34|47.76|55,Shoreview|MN|-93.15|45.08|26,Shreveport|LA|-93.75|32.53|187,Shrewsbury|MA|-71.71|42.3|33,Sicklerville|NJ|-74.97|39.72|42,Sierra Vista|AZ|-110.3|31.55|43,Silao de la Victoria|MX11|-101.43|20.94|74,Silver Lake|CA|-118.27|34.09|32,Silver Spring|MD|-77.03|38.99|71,Simi Valley|CA|-118.78|34.27|126,Sioux City|IA|-96.4|42.5|82,Sioux Falls|SD|-96.73|43.54|171,Skokie|IL|-87.73|42.03|64,Slidell|LA|-89.78|30.28|27,Smithtown|NY|-73.2|40.86|26,Smyrna|GA|-84.51|33.88|56,Smyrna|TN|-86.52|35.98|46,Snowdon|QC|-73.63|45.49|32,Socorro|TX|-106.3|31.65|33,Socorro Mission Number 1 Colonia|TX|-106.29|31.64|28,Soledad|CA|-121.33|36.42|25,Soledad de Graciano Sanchez|MX24|-100.94|22.19|332,Somerton|PA|-75.01|40.12|33,Somerville|MA|-71.1|42.39|80,Sorel-Tracy|QC|-73.11|46.04|41,South Bel Air|MD|-76.34|39.53|48,South Bend|IN|-86.25|41.68|101,South Boston|MA|-71.05|42.33|571,South Chicago|IL|-87.55|41.74|28,South Fulton|GA|-84.67|33.59|107,South Gate|CA|-118.21|33.95|96,South Gate|MD|-76.63|39.13|29,South Hill|WA|-122.27|47.14|52,South Jordan|UT|-111.93|40.56|66,South Jordan Heights|UT|-111.95|40.56|37,South Kingstown|RI|-71.52|41.45|30,South Laurel|MD|-76.85|39.07|26,South Lawndale|IL|-87.71|41.84|73,South Miami Heights|FL|-80.38|25.6|36,South Ozone Park|NY|-73.82|40.67|75,South Pasadena|CA|-118.15|34.12|26,South Peabody|MA|-70.95|42.51|50,South Portland|ME|-70.24|43.64|25,South Riverdale|ON|-79.34|43.65|27,South San Francisco|CA|-122.41|37.65|67,South Shore|IL|-87.58|41.76|51,South Suffolk|VA|-76.59|36.72|80,South Surrey|BC|-122.79|49.05|77,South Valley|NM|-106.68|35.01|40,South Vineland|NJ|-75.03|39.45|58,South Whittier|CA|-118.04|33.95|57,Southaven|MS|-90.01|34.99|52,Southfield|MI|-83.22|42.47|73,Southgate|MI|-83.19|42.21|29,Southglenn|CO|-104.95|39.59|42,Southington|CT|-72.88|41.6|43,Southlake|TX|-97.13|32.94|29,Spanaway|WA|-122.43|47.1|27,Spanish Fork|UT|-111.65|40.11|37,Sparks|NV|-119.75|39.53|96,Spartanburg|SC|-81.93|34.95|37,Spokane|WA|-117.43|47.66|229,Spokane Valley|WA|-117.24|47.67|94,Spring|TX|-95.42|30.08|54,Spring Hill|FL|-82.53|28.48|98,Spring Hill|TN|-86.93|35.75|36,Spring Valley|NV|-115.25|36.11|178,Spring Valley|NY|-74.04|41.11|32,Spring Valley|CA|-117|32.74|28,Springdale|AR|-94.13|36.19|77,Springfield|MO|-93.3|37.22|170,Springfield|MA|-72.59|42.1|154,Springfield|IL|-89.64|39.8|114,Springfield|OR|-123.02|44.05|60,Springfield|OH|-83.81|39.92|59,Springfield|VA|-77.19|38.79|30,Springfield Gardens|NY|-73.76|40.66|30,Springville|UT|-111.61|40.17|32,Spruce Grove|AB|-113.92|53.53|38,St. Albert|AB|-113.64|53.63|57,St. Catharines|ON|-79.24|43.17|136,St. Charles|MD|-76.92|38.61|33,St. Charles|IL|-88.31|41.91|32,St. James-Assiniboia East|MB|-97.23|49.89|27,St. John's|NL|-52.71|47.56|110,St. Johns|FL|-81.55|30.08|40,St. Louis|MO|-90.2|38.63|279,St. Petersburg|FL|-82.68|27.77|257,St. Thomas|ON|-81.18|42.77|38,Stamford|CT|-73.54|41.05|128,Stanton|CA|-117.99|33.8|38,Starkville|MS|-88.82|33.45|25,State College|PA|-77.86|40.79|42,Staten Island|NY|-74.14|40.56|468,Statesboro|GA|-81.78|32.45|30,Statesville|NC|-80.89|35.78|26,Sterling|VA|-77.43|39.01|27,Sterling Heights|MI|-83.03|42.58|132,Stevens Point|WI|-89.57|44.52|26,Steveston|BC|-123.18|49.13|25,Stillwater|OK|-97.06|36.12|48,Stittsville|ON|-75.92|45.25|40,Stockbridge|GA|-84.23|33.54|28,Stockton|CA|-121.29|37.96|305,Stonecrest|GA|-84.13|33.71|50,Stonegate-Queensway|ON|-79.5|43.64|25,Stoney Creek|ON|-79.77|43.22|76,Stouffville|ON|-79.25|43.97|36,Stoughton|MA|-71.1|42.13|26,Stow|OH|-81.44|41.16|34,Stratford|CT|-73.13|41.18|51,Stratford|ON|-80.95|43.37|31,Strawberry Hill|BC|-122.88|49.13|41,Streamwood|IL|-88.18|42.03|40,Strongsville|OH|-81.84|41.31|44,Studio City|CA|-118.4|34.15|34,Suffolk|VA|-76.58|36.73|88,Sugar Land|TX|-95.63|29.62|88,Suisun|CA|-122.04|38.24|28,Suitland|MD|-76.92|38.85|25,Suitland-Silver Hill|MD|-76.93|38.85|33,Summerville|SC|-80.18|33.02|48,Sumter|SC|-80.34|33.92|40,Sun City|AZ|-112.27|33.6|37,Sun Prairie|WI|-89.21|43.18|32,Sunnyside|NY|-73.94|40.74|49,Sunnyvale|CA|-122.04|37.37|155,Sunrise|FL|-80.11|26.13|84,Sunrise Manor|NV|-115.07|36.21|189,Sunset|BC|-123.1|49.22|36,Sunset Park|NY|-74.01|40.65|126,Superior|WI|-92.1|46.72|26,Surprise|AZ|-112.33|33.63|143,Surrey|BC|-122.83|49.11|568,Surrey City Centre|BC|-122.85|49.19|33,Sydney|NS|-60.18|46.14|105,Sylmar|CA|-118.45|34.31|79,Syracuse|NY|-76.15|43.05|144,Syracuse|UT|-112.06|41.09|27,Tacambaro de Codallos|MX16|-101.46|19.24|25,Tacoma|WA|-122.44|47.25|222,Tala|MX14|-103.7|20.65|35,Tallahassee|FL|-84.28|30.44|201,Tam O'Shanter-Sullivan|ON|-79.3|43.78|27,Tamarac|FL|-80.25|26.21|64,Tamiami|FL|-80.4|25.76|55,Tampa|FL|-82.46|27.95|414,Tampico|MX28|-97.88|22.29|309,Tantoyuca|MX30|-98.23|21.35|30,Tapachula|MX05|-92.26|14.91|353,Taunton|MA|-71.09|41.9|56,Taxco de Alarcon|MX12|-99.61|18.55|50,Taylor|MI|-83.27|42.24|61,Taylorsville|UT|-111.94|40.67|60,Teaneck|NJ|-74.02|40.9|40,Teapa|MX27|-92.95|17.56|26,Tecamachalco|MX21|-97.73|18.88|28,Tecate|MX02|-116.63|32.57|64,Tecax|MX31|-89.29|20.21|25,Tecoman|MX08|-103.88|18.92|85,Tehuacan|MX21|-97.4|18.46|248,Tejupilco de Hidalgo|MX15|-100.15|18.9|25,Temecula|CA|-117.15|33.49|110,Temixco|MX17|-99.23|18.85|97,Tempe|AZ|-111.91|33.41|175,Tempe Junction|AZ|-111.94|33.41|158,Temple|TX|-97.34|31.1|72,Temple City|CA|-118.06|34.11|36,Temple Terrace|FL|-82.39|28.04|25,Tenosique|MX27|-91.42|17.47|32,Teoloyucan|MX15|-99.18|19.74|51,Tepatitlan de Morelos|MX14|-102.76|20.82|91,Tepeaca|MX21|-97.9|18.97|27,Tepeji del Rio de Ocampo|MX13|-99.34|19.9|80,Tepexpan|MX15|-98.94|19.61|102,Tepic|MX18|-104.89|21.51|332,Tepotzotlan|MX15|-99.22|19.72|38,Tequila|MX14|-103.84|20.88|29,Tequisquiapan|MX22|-99.89|20.52|29,Terre Haute|IN|-87.41|39.47|60,Terrebonne|QC|-73.65|45.7|111,Tewksbury|MA|-71.23|42.61|29,Texarkana|TX|-94.05|33.43|37,Texarkana|AR|-94.04|33.44|30,Texas City|TX|-94.9|29.38|47,Texcoco de Mora|MX15|-98.88|19.51|105,Teziutlan|MX21|-97.36|19.82|103,The Acreage|FL|-80.27|26.79|38,The Bronx|NY|-73.87|40.85|1385,The Colony|TX|-96.89|33.09|41,The Hammocks|FL|-80.44|25.67|51,The Trails of Frisco|TX|-96.87|33.16|51,The Villages|FL|-81.96|28.93|51,The Woodlands|TX|-95.49|30.16|93,Thetford-Mines|QC|-71.31|46.09|25,Thomasville|NC|-80.08|35.88|27,Thornton|CO|-104.97|39.87|133,Thousand Oaks|CA|-118.84|34.17|129,Throgs Neck|NY|-73.82|40.82|33,Thunder Bay|ON|-89.25|48.38|108,Ticul|MX31|-89.53|20.4|32,Tierra Blanca|MX30|-96.36|18.45|47,Tigard|OR|-122.77|45.43|51,Tijuana|MX02|-117|32.5|1922,Timmins|ON|-81.33|48.47|41,Tinley Park|IL|-87.78|41.57|57,Titusville|FL|-80.81|28.61|45,Tizayuca|MX13|-98.98|19.84|43,Tizimin|MX31|-88.15|21.14|46,Tlahuac|MX09|-99.01|19.29|305,Tlajomulco de Zuniga|MX14|-103.45|20.47|30,Tlalnepantla|MX15|-99.2|19.54|653,Tlalpan|MX09|-99.16|19.3|574,Tlapa de Comonfort|MX12|-98.58|17.55|46,Tlapacoyan|MX30|-97.21|19.96|35,Tlaquepaque|MX14|-103.29|20.64|650,Tlaxcala|MX29|-98.24|19.32|84,Toledo|OH|-83.56|41.66|265,Toluca|MX15|-99.65|19.29|489,Toms River|NJ|-74.2|39.95|88,Tonala|MX14|-103.24|20.62|408,Tonala|MX05|-93.75|16.09|35,Tooele|UT|-112.3|40.53|33,Topeka|KS|-95.68|39.05|125,Toronto|ON|-79.4|43.71|2794,Torrance|CA|-118.34|33.84|143,Torreon|MX07|-103.42|25.54|735,Torrington|CT|-73.12|41.8|34,Town 'n' Country|FL|-82.58|28.01|78,Towson|MD|-76.6|39.4|55,Tracy|CA|-121.43|37.74|87,Trenton|NJ|-74.74|40.22|89,Tri-Cities|WA|-119.2|46.25|244,Trois-Rivieres|QC|-72.55|46.35|144,Troy|MI|-83.15|42.61|83,Troy|NY|-73.69|42.73|49,Troy|OH|-84.2|40.04|25,Trumbull|CT|-73.2|41.24|36,Tualatin|OR|-122.76|45.38|27,Tuckahoe|VA|-77.56|37.59|44,Tucker|GA|-84.22|33.85|27,Tucson|AZ|-110.93|32.22|542,Tujunga|CA|-118.29|34.25|26,Tula de Allende|MX13|-99.34|20.05|28,Tulancingo|MX13|-98.36|20.08|102,Tulare|CA|-119.35|36.21|62,Tulsa|OK|-95.99|36.15|413,Tultepec|MX15|-99.13|19.68|65,Tultitlan de Mariano Escobedo|MX15|-99.17|19.65|28,Tupelo|MS|-88.7|34.26|35,Turlock|CA|-120.85|37.49|72,Tuscaloosa|AL|-87.57|33.21|111,Tustin|CA|-117.83|33.75|80,Tuxpam de Rodriguez Cano|MX30|-97.41|20.96|84,Tuxpan|MX14|-103.38|19.56|27,Tuxtepec|MX20|-96.13|18.09|159,Tuxtla|MX05|-93.12|16.75|604,Twentynine Palms|CA|-116.05|34.14|26,Twin Falls|ID|-114.46|42.56|47,Tyler|TX|-95.3|32.35|103,Uman|MX31|-89.75|20.88|39,Union|NJ|-74.26|40.7|56,Union City|CA|-122.02|37.6|74,Union City|NJ|-74.02|40.78|69,Universal City|CA|-118.35|34.14|105,University|FL|-82.44|28.07|41,University City|MO|-90.31|38.66|35,University Heights|SK|-106.58|52.14|51,University Heights|NY|-73.91|40.86|27,University of Texas|TX|-97.74|30.29|53,University Park|FL|-80.37|25.75|26,University Place|WA|-122.55|47.24|32,Upland|CA|-117.65|34.1|76,Upper Alton|IL|-90.15|38.91|29,Upper Arlington|OH|-83.06|39.99|34,Upper West Side|NY|-73.98|40.79|226,Uptown|IL|-87.65|41.97|55,Urbana|IL|-88.21|40.11|42,Urbandale|IA|-93.71|41.63|44,Uriangato|MX11|-101.18|20.14|51,Uruapan|MX16|-102.06|19.42|299,Utica|NY|-75.23|43.1|61,Vacaville|CA|-121.99|38.36|96,Val-d'Or|QC|-77.8|48.1|25,Val-des-Arbres|QC|-73.68|45.6|40,Valdosta|GA|-83.28|30.83|55,Valencia|CA|-118.61|34.44|148,Valente Diaz|MX30|-96.22|19.16|25,Valladolid|MX31|-88.2|20.69|48,Valle de Bravo|MX15|-100.13|19.2|25,Valle de Santiago|MX11|-101.19|20.39|68,Valle Hermoso|MX28|-97.81|25.67|48,Vallejo|CA|-122.26|38.1|121,Valley Glen|CA|-118.42|34.19|60,Valley Stream|NY|-73.71|40.66|37,Valparaiso|IN|-87.06|41.47|32,Valrico|FL|-82.24|27.94|35,Van Nuys|CA|-118.45|34.19|136,Vancouver|BC|-123.12|49.25|662,Vancouver|WA|-122.66|45.64|196,Vaudreuil-Dorion|QC|-74.03|45.4|25,Vaughan|ON|-79.5|43.84|323,Venice|CA|-118.46|33.99|40,Ventura|CA|-119.29|34.28|96,Venustiano Carranza|MX09|-99.1|19.44|430,Veracruz|MX30|-96.14|19.18|428,Verdun|QC|-73.57|45.46|69,Vermont Square|CA|-118.3|34|47,Vernon|BC|-119.27|50.27|40,Vernon Hills|IL|-87.98|42.22|26,Vestal|NY|-76.05|42.09|28,Vestavia Hills|AL|-86.79|33.45|34,Victor Rosales|MX32|-102.7|22.95|32,Victoria|BC|-123.35|48.44|289,Victoria|TX|-97|28.81|67,Victoria de Durango|MX10|-104.66|24.02|518,Victoria-Downtown|BC|-123.36|48.43|46,Victoria-Fraserview|BC|-123.07|49.22|31,Victoriaville|QC|-71.97|46.05|34,Victorville|CA|-117.29|34.54|122,Villa Vicente Guerrero|MX29|-98.17|19.12|60,Villaflores|MX05|-93.27|16.23|37,Villagran|MX11|-101|20.51|27,Villahermosa|MX27|-92.94|17.99|353,Ville-Emard|QC|-73.6|45.45|30,Ville-Marie|QC|-73.57|45.5|104,Villeray-Saint-Michel-Parc-Extension|QC|-73.61|45.56|144,Vineland|NJ|-75.03|39.49|60,Virginia Beach|VA|-75.98|36.85|454,Visalia|CA|-119.29|36.33|130,Vista|CA|-117.24|33.2|100,Waco|TX|-97.15|31.55|132,Waiau-Pacific Palisades|HI|-157.95|21.4|47,Waipahu|HI|-158.01|21.39|38,Wake Forest|NC|-78.51|35.98|38,Wakefield|NY|-73.85|40.9|52,Waldorf|MD|-76.94|38.62|67,Walla Walla|WA|-118.34|46.06|32,Walnut|CA|-117.87|34.02|30,Walnut Creek|CA|-122.06|37.91|68,Walnut Grove|BC|-122.64|49.16|25,Waltham|MA|-71.24|42.38|63,Warner Robins|GA|-83.63|32.62|73,Warren|MI|-83.01|42.49|134,Warren|OH|-80.82|41.24|40,Warwick|RI|-71.42|41.7|81,Wasco|CA|-119.34|35.59|26,Washington|DC|-77.04|38.9|689,Washington Heights|NY|-73.94|40.85|152,Waterbury|CT|-73.05|41.56|108,Waterford|MI|-83.41|42.69|75,Waterfront Communities-The Island|ON|-79.38|43.63|65,Waterloo|ON|-80.52|43.47|104,Waterloo|IA|-92.34|42.49|68,Watertown|MA|-71.18|42.37|31,Watertown|NY|-75.91|43.97|26,Watsonville|CA|-121.76|36.91|53,Waukegan|IL|-87.84|42.36|88,Waukesha|WI|-88.23|43.01|71,Wausau|WI|-89.63|44.96|39,Wauwatosa|WI|-88.01|43.05|47,Waxahachie|TX|-96.85|32.39|33,Wayne|NJ|-74.28|40.93|57,Wayne|PA|-75.39|40.04|30,Weatherford|TX|-97.8|32.76|28,Welland|ON|-79.25|42.98|52,Wellesley|MA|-71.29|42.3|27,Wellington|FL|-80.24|26.66|62,Wenatchee|WA|-120.31|47.42|33,Wentzville|MO|-90.85|38.81|35,Weslaco|TX|-97.99|26.16|39,Wesley Chapel|FL|-82.33|28.24|44,West Albany|NY|-73.78|42.68|93,West Allis|WI|-88.01|43.02|60,West Babylon|NY|-73.35|40.72|43,West Bend|WI|-88.18|43.43|31,West Bloomfield Township|MI|-83.38|42.57|64,West Chicago|IL|-88.2|41.88|27,West Coon Rapids|MN|-93.35|45.16|62,West Covina|CA|-117.94|34.07|108,West Des Moines|IA|-93.71|41.58|64,West Elkridge|MD|-76.73|39.21|28,West End|BC|-123.13|49.28|47,West Englewood|IL|-87.67|41.78|32,West Falls Church|VA|-77.19|38.86|29,West Fargo|ND|-96.9|46.87|33,West Gulfport|MS|-89.09|30.4|71,West Hartford|CT|-72.74|41.76|63,West Haven|CT|-72.95|41.27|54,West Hill|ON|-79.18|43.77|27,West Hills|CA|-118.64|34.2|41,West Hollywood|FL|-80.18|26.02|60,West Hollywood|CA|-118.36|34.09|36,West Humber-Clairville|ON|-79.6|43.72|33,West Islip|NY|-73.31|40.71|28,West Jordan|UT|-111.94|40.61|111,West Kelowna|BC|-119.58|49.86|28,West Lafayette|IN|-86.91|40.43|45,West Lake Sammamish|WA|-122.1|47.58|33,West Lawn|IL|-87.72|41.77|32,West Linn|OR|-122.61|45.37|26,West Little River|FL|-80.24|25.86|34,West Lynchburg|VA|-79.18|37.4|65,West Memphis|AR|-90.18|35.15|25,West Milford|NJ|-74.37|41.13|26,West New York|NJ|-74.01|40.79|53,West Oak Lane|PA|-75.17|40.07|38,West Orange|NJ|-74.24|40.8|48,West Palm Beach|FL|-80.05|26.72|120,West Raleigh|NC|-78.66|35.79|338,West Ridge|IL|-87.69|42|72,West Roxbury|MA|-71.15|42.28|30,West Sacramento|CA|-121.53|38.58|52,West Scarborough|ME|-70.39|43.57|27,West Seneca|NY|-78.8|42.85|44,West Springfield|MA|-72.62|42.11|27,West Torrington|CT|-73.14|41.82|36,West Town|IL|-87.67|41.89|86,West Valley City|UT|-112|40.69|136,West Vancouver|BC|-123.16|49.33|45,West Village|NY|-74.01|40.73|32,West Warwick|RI|-71.52|41.7|30,West Whittier-Los Nietos|CA|-118.07|33.98|25,Westchester|FL|-80.33|25.75|29,Westerville|OH|-82.93|40.13|38,Westfield|MA|-72.75|42.13|41,Westfield|IN|-86.13|40.04|36,Westfield|NJ|-74.35|40.66|30,Westlake|OH|-81.92|41.46|32,Westland|MI|-83.4|42.32|82,Westminster|CO|-105.04|39.84|116,Westminster|CA|-118.01|33.76|92,Westminster-Branson|ON|-79.45|43.78|26,Westmont|CA|-118.3|33.94|31,Weston|FL|-80.4|26.1|69,Westport|CT|-73.36|41.14|26,Wethersfield|CT|-72.65|41.71|26,Wexford/Maryvale|ON|-79.3|43.75|27,Weymouth|MA|-70.94|42.22|54,Whalley|BC|-122.87|49.18|102,Wharton|PA|-75.16|39.93|49,Wheat Ridge|CO|-105.08|39.77|31,Wheaton|IL|-88.11|41.87|53,Wheaton|MD|-77.06|39.04|48,Wheeling|IL|-87.93|42.14|38,Wheeling|WV|-80.72|40.06|27,Whitby|ON|-78.93|43.88|138,Whitchurch-Stouffville|ON|-79.32|44|49,White Bear Lake|MN|-93.01|45.08|25,White Plains|NY|-73.76|41.03|58,Whitehorse|YT|-135.05|60.72|28,Whitestone|NY|-73.82|40.79|36,Whitman|PA|-75.16|39.92|49,Whitney|NV|-115.04|36.1|38,Whittier|CA|-118.03|33.98|87,Wichita|KS|-97.34|37.69|396,Wichita Falls|TX|-98.49|33.91|104,Wildomar|CA|-117.28|33.6|35,Wildwood|MO|-90.66|38.58|35,Wilkes-Barre|PA|-75.88|41.25|40,Williamsburg|NY|-73.95|40.71|33,Williamsport|PA|-77|41.24|29,Willingboro|NJ|-74.87|40.03|31,Williston|ND|-103.62|48.15|26,Willoughby|BC|-122.68|49.13|31,Willowbrook|CA|-118.26|33.92|35,Willowdale|ON|-79.4|43.77|79,Willowdale East|ON|-79.4|43.77|50,Wilmette|IL|-87.72|42.07|27,Wilmington|NC|-77.95|34.24|115,Wilmington|DE|-75.55|39.75|70,Wilmington|CA|-118.26|33.78|52,Wilson|NC|-77.92|35.72|49,Winchester|NV|-115.12|36.13|27,Winchester|VA|-78.16|39.19|27,Windsor|ON|-83.02|42.3|229,Windsor|CO|-104.9|40.48|32,Windsor|CT|-72.64|41.85|28,Windsor|CA|-122.82|38.55|27,Winnetka|CA|-118.57|34.21|47,Winnipeg|MB|-97.15|49.88|749,Winona|MN|-91.64|44.05|27,Winston-Salem|NC|-80.24|36.1|241,Winter Garden|FL|-81.59|28.57|40,Winter Haven|FL|-81.73|28.02|37,Winter Park|FL|-81.34|28.6|29,Winter Springs|FL|-81.31|28.7|34,Woburn|ON|-79.23|43.77|53,Woburn|MA|-71.15|42.48|39,Woodburn|OR|-122.86|45.14|25,Woodbury|MN|-92.96|44.92|67,Woodhaven|NY|-73.86|40.69|36,Woodland|CA|-121.77|38.68|58,Woodland Hills|CA|-118.61|34.17|70,Woodlawn|MD|-76.73|39.32|37,Woodridge|IL|-88.05|41.75|33,Woodside|NY|-73.91|40.75|41,Woodstock|ON|-80.75|43.13|40,Woodstock|GA|-84.52|34.1|29,Woodstock|IL|-88.45|42.31|25,Woonsocket|RI|-71.51|42|41,Wooster|OH|-81.94|40.81|26,Worcester|MA|-71.8|42.26|206,Wyandotte|MI|-83.15|42.21|25,Wylie|TX|-96.54|33.02|46,Wyoming|MI|-85.71|42.91|75,Xalapa de Enriquez|MX30|-96.92|19.53|424,Xalisco|MX18|-104.9|21.44|35,Xenia|OH|-83.93|39.68|25,Xico|MX15|-98.95|19.27|384,Xicotepec de Juarez|MX21|-97.96|20.28|39,Xochimilco|MX09|-99.1|19.25|442,Yakima|WA|-120.51|46.6|93,Yarmouth|MA|-70.23|41.71|25,Yautepec|MX17|-99.07|18.88|105,Yonkers|NY|-73.9|40.93|201,Yorba Linda|CA|-117.81|33.89|67,York|PA|-76.73|39.96|43,York University Heights|ON|-79.49|43.77|27,Youngstown|OH|-80.65|41.1|64,Yuba City|CA|-121.62|39.14|66,Yucaipa|CA|-117.04|34.03|53,Yukon|OK|-97.76|35.51|25,Yuma|AZ|-114.62|32.73|95,Yuriria|MX11|-101.13|20.21|25,Zacapu|MX16|-101.79|19.82|50,Zacatecas|MX32|-102.58|22.77|129,Zacatelco|MX29|-98.24|19.22|42,Zacatlan|MX21|-97.96|19.94|33,Zamora de Hidalgo|MX16|-102.29|19.98|186,Zanesville|OH|-82.01|39.94|25,Zapopan|MX14|-103.39|20.72|1476,Zapotlanejo|MX14|-103.07|20.62|32,Zinacantepec|MX15|-99.73|19.28|54,Zionsville|IN|-86.26|39.95|26,Zumpango|MX15|-99.1|19.8|280\";\n\nexport const ADMIN1_PACKED = \"AB|CA|Alberta|-113.75|52.18,AK|US|Alaska|-148.32|61.29,AL|US|Alabama|-86.73|33.08,AR|US|Arkansas|-92.85|35.29,AZ|US|Arizona|-111.99|33.32,BC|CA|British Columbia|-122.73|49.27,CA|US|California|-119.21|35.2,CO|US|Colorado|-105|39.63,CT|US|Connecticut|-73.01|41.42,DC|US|District of Columbia|-77.04|38.9,DE|US|Delaware|-75.59|39.57,FL|US|Florida|-81.29|27.53,GA|US|Georgia|-84.02|33.43,HI|US|Hawaii|-157.73|21.26,IA|US|Iowa|-92.79|41.88,ID|US|Idaho|-115.65|44.2,IL|US|Illinois|-88.05|41.67,IN|US|Indiana|-86.27|40.23,KS|US|Kansas|-96|38.59,KY|US|Kentucky|-84.91|38.13,LA|US|Louisiana|-91.28|30.57,MA|US|Massachusetts|-71.28|42.32,MB|CA|Manitoba|-97.3|49.88,MD|US|Maryland|-76.79|39.18,ME|US|Maine|-70.01|43.92,MI|US|Michigan|-83.69|42.56,MN|US|Minnesota|-93.26|44.98,MO|US|Missouri|-92.66|38.73,MS|US|Mississippi|-89.62|32.36,MT|US|Montana|-111.07|46.37,MX01|MX|Aguascalientes|-102.29|21.91,MX02|MX|Baja California|-116.5|32.46,MX03|MX|Baja California Sur|-110.12|23.52,MX04|MX|Campeche|-91.08|19.25,MX05|MX|Chiapas|-92.77|16.28,MX06|MX|Chihuahua|-106.27|30.1,MX07|MX|Coahuila|-101.9|26.31,MX08|MX|Colima|-103.94|19.15,MX09|MX|Mexico City|-99.13|19.41,MX10|MX|Durango|-104.24|24.64,MX11|MX|Guanajuato|-101.37|20.87,MX12|MX|Guerrero|-99.83|17.31,MX13|MX|Hidalgo|-98.81|20.13,MX14|MX|Jalisco|-103.39|20.64,MX15|MX|M\\u00e9xico|-99.14|19.51,MX16|MX|Michoac\\u00e1n|-101.68|19.49,MX17|MX|Morelos|-99.15|18.88,MX18|MX|Nayarit|-104.89|21.5,MX19|MX|Nuevo Le\\u00f3n|-100.26|25.69,MX20|MX|Oaxaca|-96.23|17.05,MX21|MX|Puebla|-98.12|19.07,MX22|MX|Quer\\u00e9taro|-100.35|20.57,MX23|MX|Quintana Roo|-87.09|20.7,MX24|MX|San Luis Potos\\u00ed|-100.7|22.22,MX25|MX|Sinaloa|-107.49|24.63,MX26|MX|Sonora|-111.05|29.37,MX27|MX|Tabasco|-92.98|17.99,MX28|MX|Tamaulipas|-98.33|24.92,MX29|MX|Tlaxcala|-98.19|19.31,MX30|MX|Veracruz|-96.31|19.2,MX31|MX|Yucat\\u00e1n|-89.52|20.94,MX32|MX|Zacatecas|-102.7|22.93,NB|CA|New Brunswick|-65.73|45.83,NC|US|North Carolina|-79.61|35.57,ND|US|North Dakota|-98.8|47.31,NE|US|Nebraska|-96.42|41.07,NH|US|New Hampshire|-71.37|43,NJ|US|New Jersey|-74.34|40.51,NL|CA|Newfoundland and Labrador|-52.77|47.55,NM|US|New Mexico|-106.28|34.69,NS|CA|Nova Scotia|-63.11|44.86,NV|US|Nevada|-115.95|36.72,NY|US|New York|-74.13|40.89,OH|US|Ohio|-82.75|40.53,OK|US|Oklahoma|-97.01|35.66,ON|CA|Ontario|-79.27|43.94,OR|US|Oregon|-122.75|44.96,PA|US|Pennsylvania|-76.12|40.23,PE|CA|Prince Edward Island|-63.13|46.23,QC|CA|Quebec|-73.14|45.89,RI|US|Rhode Island|-71.45|41.79,SC|US|South Carolina|-80.69|33.6,SD|US|South Dakota|-98.66|43.89,SK|CA|Saskatchewan|-105.99|51.63,TN|US|Tennessee|-87.53|35.63,TX|US|Texas|-97.51|31.01,UT|US|Utah|-111.96|40.5,VA|US|Virginia|-77.08|37.61,VT|US|Vermont|-73.21|44.48,WA|US|Washington|-121.46|47.29,WI|US|Wisconsin|-88.6|43.36,WV|US|West Virginia|-81.42|38.99,WY|US|Wyoming|-105.54|42.26,YT|CA|Yukon|-135.05|60.72\";\n\nexport const ZIP3_PACKED = \"006|-66.84|18.26,007|-66.15|18.15,008|-64.74|17.74,009|-66.12|18.37,010|-72.6|42.26,011|-72.53|42.07,012|-73.21|42.37,013|-72.51|42.58,014|-71.81|42.59,015|-71.85|42.23,016|-71.84|42.28,017|-71.44|42.33,018|-71.21|42.64,019|-70.89|42.66,020|-71.28|42.12,021|-71.05|42.31,022|-71.04|42.35,023|-70.86|41.98,024|-71.22|42.37,025|-70.6|41.71,026|-70.2|41.73,027|-71.07|41.77,028|-71.62|41.69,029|-71.47|41.82,030|-71.51|42.9,031|-71.48|43,032|-71.65|43.57,033|-71.58|43.26,034|-72.19|42.91,035|-71.31|44.69,036|-72.32|43.17,037|-72.13|43.69,038|-71.1|43.56,039|-70.76|43.24,040|-70.6|43.78,041|-70.27|43.7,042|-70.49|44.38,043|-69.75|44.3,044|-68.83|45.45,045|-69.57|43.99,046|-67.8|44.79,047|-68.34|46.55,048|-69.17|44.2,049|-69.97|44.99,050|-72.43|43.85,051|-72.65|43.28,052|-73.14|43.03,053|-72.77|42.94,054|-72.98|44.58,056|-72.6|44.35,057|-73.04|43.69,058|-72.12|44.68,059|-71.7|44.79,060|-72.81|41.88,061|-72.69|41.74,062|-72.12|41.81,063|-72.03|41.5,064|-72.61|41.46,065|-72.94|41.36,066|-73.22|41.24,067|-73.24|41.68,068|-73.42|41.28,069|-73.57|41.14,070|-74.28|40.76,071|-74.2|40.74,072|-74.2|40.68,073|-74.08|40.71,074|-74.4|41.09,075|-74.17|40.92,076|-74.01|40.94,077|-74.17|40.28,078|-74.81|40.97,079|-74.55|40.74,080|-74.98|39.78,081|-75.07|39.94,082|-74.68|39.32,083|-75.04|39.45,084|-74.48|39.35,085|-74.62|40.25,086|-74.68|40.23,087|-74.22|39.96,088|-74.72|40.54,089|-74.48|40.45,100|-73.96|40.77,101|-73.95|40.78,102|-74.02|40.72,103|-74.15|40.58,104|-73.85|40.9,105|-73.75|41.27,106|-73.74|41.02,107|-73.84|40.96,108|-73.8|40.95,109|-74.27|41.3,110|-73.69|40.74,112|-73.94|40.66,113|-73.84|40.73,114|-73.77|40.69,115|-73.62|40.71,116|-73.88|40.57,117|-73.23|40.82,118|-73.5|40.77,119|-72.53|40.95,120|-74.22|42.92,121|-74.4|43.46,122|-73.81|42.7,123|-73.96|42.82,124|-74.27|42.09,125|-73.7|41.89,126|-73.88|41.69,127|-74.75|41.74,128|-73.91|43.61,129|-74.03|44.53,130|-76.16|43.08,131|-76.46|43.27,132|-76.2|43.03,133|-75.07|43.45,134|-75.35|43.03,135|-75.15|43.14,136|-75.39|44.33,137|-75.2|42.21,138|-75.46|42.43,139|-75.88|42.12,140|-78.48|43.01,141|-78.7|42.49,142|-78.8|42.91,143|-78.96|43.12,144|-77.71|42.96,145|-77.17|43.14,146|-77.65|43.17,147|-78.84|42.21,148|-77.18|42.29,149|-76.78|42.09,150|-80.23|40.56,151|-80.18|40.53,152|-79.97|40.46,153|-80.24|40.01,154|-79.65|39.92,155|-78.8|39.95,156|-79.47|40.33,157|-79.02|40.71,158|-78.66|41.32,159|-78.84|40.35,160|-79.88|40.93,161|-80.3|41.19,162|-79.35|41.08,163|-79.58|41.59,164|-80|41.87,165|-80.04|42.1,166|-78.28|40.48,167|-78.47|41.76,168|-78|40.97,169|-77.37|41.8,170|-77.29|40.44,171|-76.78|40.33,172|-77.81|39.98,173|-76.93|39.89,174|-76.72|39.97,175|-76.24|40.05,176|-76.34|40.05,177|-77.22|41.34,178|-76.77|40.93,179|-76.27|40.69,180|-75.45|40.67,181|-75.47|40.6,182|-75.88|40.9,183|-75.19|41.12,184|-75.38|41.53,185|-75.68|41.37,186|-76.17|41.4,187|-75.86|41.22,188|-76.12|41.82,189|-75.14|40.36,190|-75.37|39.97,191|-75.15|40.01,193|-75.78|39.92,194|-75.46|40.19,195|-75.9|40.42,196|-75.95|40.34,197|-75.68|39.53,198|-75.58|39.77,199|-75.46|38.83,200|-77.02|38.9,201|-77.68|38.9,203|-77.01|38.86,206|-76.76|38.45,207|-76.78|38.88,208|-77.23|39.15,209|-76.99|39.05,210|-76.32|39.53,211|-76.73|39.58,212|-76.58|39.31,214|-76.55|39,215|-79.08|39.56,216|-76.02|38.86,217|-77.5|39.52,218|-75.57|38.27,219|-75.94|39.57,220|-77.25|38.78,221|-77.25|38.92,222|-77.11|38.88,223|-77.13|38.79,224|-77.25|38.22,225|-77.51|38.09,226|-78.25|39.03,227|-78.05|38.49,228|-78.79|38.57,229|-78.55|38.02,230|-78.09|37.66,231|-76.96|37.55,232|-77.46|37.51,233|-76.3|36.68,234|-76.66|36.77,235|-76.26|36.87,236|-76.44|37.12,237|-76.38|36.85,238|-77.41|36.94,239|-78.39|37.03,240|-80.1|37.23,241|-79.89|36.9,242|-82.49|36.83,243|-81.13|36.86,244|-79.51|38.05,245|-79.11|37.15,246|-81.8|37.19,247|-81.14|37.39,248|-81.64|37.47,249|-80.32|37.95,250|-81.45|38.27,251|-81.89|38.64,252|-81.45|38.77,253|-81.64|38.42,254|-78.11|39.45,255|-82.19|38.25,256|-82.08|37.76,257|-82.41|38.4,258|-81.21|37.72,259|-80.89|37.83,260|-80.63|40.05,261|-81.27|39.2,262|-80.02|38.75,263|-80.23|39.27,264|-80.65|39.21,265|-80.05|39.59,266|-80.8|38.48,267|-78.98|39.33,268|-79.12|38.88,270|-80.45|36.24,271|-80.24|36.06,272|-79.82|35.86,273|-79.52|35.55,274|-79.81|36.09,275|-78.54|35.93,276|-78.65|35.81,277|-78.9|35.99,278|-77.32|35.88,279|-76.5|36.19,280|-81.2|35.4,281|-80.41|35.21,282|-80.83|35.22,283|-78.94|34.96,284|-78.23|34.39,285|-77.23|35.06,286|-81.36|36.06,287|-82.78|35.51,288|-82.55|35.6,289|-83.97|35.11,290|-80.88|34.19,291|-80.62|33.71,292|-80.96|34.04,293|-81.86|34.78,294|-80.08|33.05,295|-79.49|34.07,296|-82.57|34.61,297|-80.92|34.83,298|-81.74|33.51,299|-80.96|32.55,300|-84.08|33.85,301|-84.91|34.04,302|-84.55|33.25,303|-84.42|33.77,304|-82.11|32.48,305|-83.81|34.58,306|-83.14|33.86,307|-85.1|34.71,308|-82.34|33.35,309|-82.01|33.4,310|-83.42|32.64,312|-83.64|32.83,313|-81.49|31.68,314|-81.15|32.03,315|-82.21|31.4,316|-83.21|31.02,317|-83.87|31.48,318|-84.69|32.45,319|-84.95|32.47,320|-82.58|30.27,321|-81.48|29.39,322|-81.69|30.3,323|-84.25|30.29,324|-85.58|30.55,325|-87.03|30.76,326|-82.6|29.58,327|-81.28|28.79,328|-81.32|28.47,329|-80.67|27.96,330|-80.58|25.31,331|-80.38|25.74,333|-80.25|26.13,334|-81.01|26.64,335|-82.22|28.19,336|-82.47|28.01,337|-82.73|27.85,338|-81.64|27.72,339|-81.71|26.77,341|-81.32|26.11,342|-82.16|27.29,344|-82.35|29.01,346|-82.52|28.41,347|-81.4|28.29,349|-80.72|27.34,350|-86.8|34.05,351|-86.38|33.43,352|-86.77|33.5,354|-87.86|33.07,355|-87.71|33.96,356|-87.4|34.67,357|-86.26|34.75,358|-86.59|34.69,359|-85.94|34.27,360|-86.09|32.09,361|-86.27|32.31,362|-85.65|33.53,363|-85.56|31.34,364|-86.97|31.38,365|-88.14|31.32,366|-88.19|30.68,367|-87.34|32.33,368|-85.41|32.61,369|-88.25|32.12,370|-87.02|36.12,371|-86.23|35.75,372|-86.83|36.15,373|-85.36|35.32,374|-85.33|35.06,376|-82.3|36.36,377|-84.31|36.27,378|-83.35|36.3,379|-83.97|35.97,380|-89.43|35.55,381|-89.96|35.1,382|-88.68|36.29,383|-88.5|35.61,384|-87.36|35.34,385|-85.34|36.18,386|-89.76|34.57,387|-90.81|33.54,388|-88.63|34.36,389|-89.9|33.77,390|-90|32.53,391|-90.67|32.51,392|-90.22|32.32,393|-88.8|32.42,394|-89.26|31.23,395|-89|30.59,396|-90.67|31.32,397|-88.87|33.53,398|-84.67|31.32,400|-85.29|38.07,401|-86.2|37.85,402|-85.66|38.19,403|-84.2|38.06,404|-84.42|37.53,405|-84.45|38.04,406|-84.87|38.22,407|-84.12|36.95,408|-83.26|36.9,409|-83.74|36.95,410|-84.32|38.64,411|-83.06|38.38,412|-82.67|37.89,413|-83.48|37.58,414|-83.15|37.84,415|-82.39|37.46,416|-82.78|37.54,417|-83.25|37.23,418|-82.88|37.21,420|-88.57|36.9,421|-86.09|36.89,422|-87.11|36.95,423|-87.02|37.52,424|-87.7|37.47,425|-84.71|37.15,426|-84.82|36.81,427|-85.73|37.32,430|-82.9|40.21,431|-82.99|39.69,432|-82.97|39.98,433|-83.38|40.55,434|-83.3|41.42,435|-84.22|41.46,436|-83.54|41.67,437|-81.66|39.85,438|-81.88|40.27,439|-80.9|40.25,440|-80.97|41.64,441|-81.66|41.42,442|-81.6|41.13,443|-81.54|41.07,444|-80.77|41.09,445|-80.64|41.05,446|-81.53|40.65,447|-81.39|40.83,448|-82.77|41.02,449|-82.55|40.74,450|-84.48|39.43,451|-83.84|39.16,452|-84.48|39.18,453|-84.3|39.97,454|-84.18|39.74,455|-83.81|39.94,456|-82.86|38.99,457|-81.78|39.35,458|-84.23|40.83,460|-86.13|40.17,461|-86.18|39.62,462|-86.14|39.79,463|-87.05|41.41,464|-87.34|41.55,465|-86.08|41.44,466|-86.28|41.66,467|-85.21|41.17,468|-85.17|41.09,469|-86.14|40.76,470|-85.08|39.16,471|-86.02|38.43,472|-85.74|39.05,473|-85.15|40.09,474|-86.63|38.99,475|-87.03|38.46,476|-87.53|38.13,477|-87.58|38.03,478|-87.29|39.45,479|-87.11|40.5,480|-82.79|42.83,481|-83.62|42.2,482|-83.1|42.38,483|-83.35|42.66,484|-83.22|43.29,485|-83.69|43.03,486|-84.36|43.99,487|-83.67|44.45,488|-84.67|43.02,489|-84.59|42.74,490|-85.54|42.2,491|-86.36|41.87,492|-84.36|42.06,493|-85.57|43.33,494|-86.14|43.39,495|-85.66|42.96,496|-85.52|44.44,497|-84.37|45.17,498|-86.94|46.17,499|-88.93|46.6,500|-94.77|41.63,501|-92.79|41.84,502|-93.67|42.12,503|-93.63|41.59,504|-93.37|43.17,505|-94.45|42.77,506|-92.45|42.65,507|-92.31|42.49,508|-94.56|40.96,510|-95.91|42.6,511|-96.39|42.52,512|-96.06|43.28,513|-95.16|43.27,514|-95.08|42.11,515|-95.55|41.44,516|-95.37|40.74,520|-90.98|42.46,521|-91.73|43.2,522|-92.31|41.9,523|-91.66|41.87,524|-91.69|41.96,525|-92.46|40.95,526|-91.44|40.88,527|-90.88|41.64,528|-90.61|41.56,530|-88.24|43.51,531|-88.33|42.75,532|-87.98|43.06,534|-87.86|42.74,535|-89.66|42.95,537|-89.38|43.08,538|-90.73|42.88,539|-89.55|43.64,540|-92.46|45.08,541|-88.23|45.09,542|-87.6|44.52,543|-88.01|44.53,544|-89.85|44.93,545|-89.86|45.89,546|-90.87|43.93,547|-91.52|44.85,548|-91.63|46.04,549|-88.96|44.19,550|-92.98|45.17,551|-93.08|44.96,553|-94.06|44.99,554|-93.31|45.01,556|-91.03|47.67,557|-92.68|47.39,558|-92.08|46.96,559|-92.28|43.89,560|-94.04|44.02,561|-95.65|43.92,562|-95.75|45.08,563|-94.59|45.83,564|-94.5|46.63,565|-96.08|46.86,566|-94.31|47.88,567|-96.22|48.39,570|-97.07|43.48,571|-96.76|43.54,572|-97.18|45.04,573|-98.53|43.91,574|-98.93|45.36,575|-100.61|43.86,576|-101.59|45.43,577|-103.11|44.26,580|-97.39|46.66,581|-96.83|46.86,582|-97.72|48.25,583|-99.34|48.32,584|-99.12|46.88,585|-100.98|46.76,586|-102.99|46.75,587|-101.7|48.34,588|-103.48|48.2,590|-108.62|45.98,591|-108.45|45.69,592|-105.6|48.2,593|-105.58|46.4,594|-111.24|47.79,595|-109.12|48.38,596|-111.74|46.65,597|-112.31|45.48,598|-114.25|46.97,599|-114.57|48.37,600|-88.15|42.29,601|-88.47|42.01,602|-87.69|42.04,603|-87.79|41.89,604|-88.09|41.39,605|-88.55|41.69,606|-87.69|41.83,607|-87.82|42.03,608|-87.76|41.84,609|-87.92|40.8,610|-89.56|42.18,611|-89.08|42.28,612|-90.29|41.5,613|-89.16|41.34,614|-90.43|40.82,615|-89.68|40.71,616|-89.61|40.72,617|-88.89|40.52,618|-88.15|40.11,619|-88.19|39.66,620|-90.01|39.13,622|-89.72|38.41,623|-91.02|39.99,624|-88.19|39.04,625|-89.21|39.63,626|-90|39.86,627|-89.66|39.8,628|-88.65|38.35,629|-88.89|37.55,630|-90.83|38.35,631|-90.32|38.64,633|-91.11|39.02,634|-91.78|39.93,635|-92.61|40.2,636|-90.64|37.61,637|-89.81|37.37,638|-89.73|36.55,639|-90.62|36.84,640|-94.1|39.07,641|-94.58|39.14,644|-94.69|40.13,645|-94.83|39.75,646|-93.55|39.89,647|-94.15|38.06,648|-94.35|36.93,650|-92.23|38.45,651|-92.19|38.51,652|-92.38|39.21,653|-93.26|38.71,654|-92.03|37.93,655|-91.92|37.67,656|-93.08|36.76,657|-92.43|37.03,658|-93.3|37.22,660|-95.12|38.84,661|-94.73|39.11,662|-94.72|38.95,664|-96.15|39.64,665|-96.34|39.4,666|-95.76|39.06,667|-95.23|37.64,668|-96.39|38.36,669|-97.86|39.77,670|-98.49|37.33,671|-98.4|37.44,672|-97.33|37.7,673|-95.81|37.23,674|-97.79|38.98,675|-98.99|38.25,676|-99.47|39.34,677|-101.15|39.35,678|-100.82|37.91,679|-101.43|37.18,680|-96.46|41.55,681|-96.05|41.24,683|-96.93|40.39,684|-97.26|40.89,685|-96.69|40.82,686|-97.68|41.55,687|-98.16|42.43,688|-99.15|41.34,689|-98.9|40.34,690|-100.99|40.37,691|-101.59|41.42,692|-100.64|42.67,693|-102.84|42.29,700|-89.93|29.63,701|-89.93|30.02,703|-90.65|29.62,704|-90.27|30.66,705|-92.25|30.2,706|-93.21|30.34,707|-91.2|30.57,708|-91.1|30.45,710|-93.4|32.45,711|-93.75|32.47,712|-91.95|32.56,713|-91.86|31.43,714|-92.84|31.57,716|-91.75|33.61,717|-92.79|33.48,718|-93.84|33.66,719|-93.61|34.4,720|-91.9|34.92,721|-92.56|34.93,722|-92.37|34.7,723|-90.59|35.07,724|-90.76|36.04,725|-91.81|36.01,726|-92.93|36.15,727|-94.05|36.11,728|-93.33|35.3,729|-94.08|35.29,730|-97.83|35.31,731|-97.5|35.43,734|-97.14|34.21,735|-98.81|34.53,736|-99.33|35.52,737|-98.2|36.42,738|-99.54|36.5,739|-101.67|36.74,740|-96.19|36.34,741|-95.96|36.15,743|-95.02|36.55,744|-95.44|35.64,745|-95.65|34.66,746|-97.05|36.71,747|-95.41|34.02,748|-96.66|35.17,749|-94.69|35.23,750|-96.76|33.06,751|-96.32|32.48,752|-96.78|32.79,754|-95.67|33.37,755|-94.45|33.3,756|-94.58|32.49,757|-95.37|32.24,758|-95.68|31.44,759|-94.29|31.29,760|-97.54|32.59,761|-97.38|32.77,762|-97.44|33.52,763|-98.87|33.73,764|-98.59|32.61,765|-97.62|31.11,766|-97.06|31.7,767|-97.15|31.57,768|-99.37|31.23,769|-100.88|31.03,770|-95.39|29.79,772|-95.37|29.77,773|-95.24|30.48,774|-96.02|29.38,775|-94.98|29.68,776|-94.16|30.27,777|-94.19|29.96,778|-96.32|30.67,779|-96.95|28.92,780|-99.01|28.67,781|-97.93|29.08,782|-98.53|29.47,783|-98.14|27.63,784|-97.44|27.73,785|-98.19|26.39,786|-98.1|30.27,787|-97.78|30.3,788|-100.26|29.44,789|-96.87|29.92,790|-101.63|35.48,791|-101.85|35.21,792|-100.57|34.25,793|-102.15|33.37,794|-101.88|33.58,795|-100.31|32.7,796|-99.72|32.46,797|-102.22|31.57,798|-104.17|30.53,799|-106.16|31.8,800|-105.11|39.91,801|-104.47|39.51,802|-104.93|39.78,803|-105.31|40.04,804|-106.16|39.98,805|-105.35|40.57,806|-104.49|40.38,807|-103.09|40.4,808|-103.16|39.12,809|-104.67|38.79,810|-103.58|37.77,811|-106.39|37.54,812|-106.39|38.46,813|-108.42|37.49,814|-108.02|38.43,815|-108.7|38.92,816|-107.86|39.99,820|-105.27|41.31,821|-110.51|44.57,822|-104.67|42.39,823|-107.03|41.75,824|-108.65|44.32,825|-108.91|43.14,826|-106.43|43.1,827|-105|44.2,828|-106.71|44.55,829|-109.71|42.82,830|-110.51|43.7,831|-110.53|42.6,832|-112.17|42.66,833|-114.23|42.81,834|-111.63|43.86,835|-115.77|46.07,836|-115.99|44,837|-116.04|43.58,838|-116.47|47.77,840|-110.86|40.53,841|-111.81|40.71,843|-112.18|41.67,844|-112.01|41.23,845|-109.72|38.09,846|-111.68|39.63,847|-112.82|37.61,850|-112.09|33.63,851|-111.41|33,852|-111.69|33.6,853|-113.44|33.49,855|-110.3|33.43,856|-110.59|32,857|-111.09|32.18,859|-109.81|34.23,860|-111.15|35.89,863|-112.57|34.85,864|-113.79|35.66,865|-109.55|36.03,870|-106.9|35.22,871|-106.72|35.12,873|-108.56|35.44,874|-108.23|36.59,875|-106.04|36.27,877|-104.68|36.06,878|-107.94|34,879|-107.48|33.2,880|-108.03|32.37,881|-103.81|34.26,882|-104.07|32.87,883|-105.48|33.62,884|-103.79|35.74,890|-114.84|37,891|-115.42|36.33,893|-116.96|39.5,894|-118.06|41.29,895|-119.72|39.9,897|-119.8|39.18,898|-115.71|40.95,900|-118.32|34.04,902|-118.76|34.07,903|-118.34|33.95,904|-118.48|34.02,905|-118.33|33.83,906|-118.03|33.93,907|-118.45|33.39,908|-118.18|33.8,910|-118.25|34.28,911|-118.13|34.15,912|-118.25|34.19,913|-118.55|34.36,914|-118.47|34.18,915|-118.33|34.19,916|-118.38|34.18,917|-117.78|34.14,918|-118.13|34.09,919|-116.62|32.73,920|-116.7|33.16,921|-117.14|32.85,922|-115.5|33.59,923|-117.14|34.66,924|-117.33|34.17,925|-117.06|33.68,926|-117.73|33.63,927|-117.85|33.74,928|-117.71|33.85,930|-119.12|34.39,931|-119.97|34.48,932|-119.07|35.98,933|-119.01|35.35,934|-120.5|35.38,935|-118.36|37.37,936|-119.72|36.95,937|-119.85|36.71,939|-121.42|36.34,940|-122.31|37.41,941|-122.44|37.77,943|-122.13|37.44,944|-122.32|37.53,945|-122.05|38.09,946|-122.19|37.78,947|-122.26|37.89,948|-122.3|37.97,949|-122.74|38.14,950|-121.42|36.85,951|-121.76|37.33,952|-120.79|38.14,953|-120.56|37.62,954|-123.15|39.08,955|-123.75|40.5,956|-121.34|38.62,957|-120.86|39.13,958|-121.44|38.58,959|-121.73|39.47,960|-122.42|41.06,961|-120.33|40.52,967|-155.53|19.6,968|-157.83|21.34,969|144.77|13.45,970|-121.45|45.3,971|-123.79|45.64,972|-122.74|45.57,973|-123.26|44.68,974|-123.46|43.61,975|-123.06|42.34,976|-120.4|42.67,977|-120.77|44.32,978|-118.59|45.21,979|-117.63|43.34,980|-121.78|47.42,981|-122.32|47.6,982|-121.79|48.39,983|-123.87|47.96,984|-122.46|47.18,985|-123.27|46.94,986|-122.15|45.99,988|-119.91|48,989|-120.57|46.71,990|-117.56|47.62,991|-118.13|47.91,992|-117.4|47.67,993|-118.82|46.4,994|-117.21|46.2,995|-156.93|60.89,996|-151.66|60.82,997|-146.69|65.19,998|-136.2|58.94,999|-131.24|55.86\";\n", "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// POINT GEOCODING \u2014 resolve a lat/lon for a row that has no coordinates\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Genesis (2026-07-25): \"add city names to lat/long mappings ... allow someone to\n// drop in ONLY city NAMES (no need for lat/long)\", then the cascade:\n// City+State -> Lat/Long OR Zip5 or Zip3 -> Lat/Long OR Just State -> Lat/Long\n// and \"when I say 'state' - include non-US provinces.\"\n//\n// Sibling of geoDetector.ts, deliberately separate:\n// geoDetector answers \"what REGION is this value?\" -> a polygon join key (__geoIso__),\n// strictly SINGLE-column.\n// geoPoint answers \"WHERE is this row?\" -> a coordinate (__geoLat__/__geoLon__),\n// inherently CROSS-column (City needs State to disambiguate).\n// Keeping them apart means the choropleth join path is untouched by any of this.\n//\n// PRECISION is part of the contract, not an afterthought. Each tier resolves to a\n// genuinely different scale, and callers must be able to say so rather than implying\n// a street address:\n// \"latlon\" real coordinates from the data (exact)\n// \"city\" the city's point (~city scale)\n// \"zip3\" centroid of the 3-digit ZIP prefix (~metro/sub-state; ZIP-5 reduces\n// to its prefix \u2014 the honest limit of the geometry we bundle)\n// \"state\" population-weighted centroid of the state/province (very coarse; every row\n// in a state lands on ONE point)\n//\n// AMBIGUITY is reported, never hidden. 9% of North American city names collide (8\n// Springfields, 6 Burlingtons). With a state column they disambiguate exactly; without\n// one, the largest city wins the name and `ambiguous` counts it, so the chart can\n// annotate instead of silently asserting a location.\n//\n// Data: geoPointTables.generated.ts (GeoNames CC BY 4.0 \u2014 attribution ships in the EULA;\n// ZIP-3 + admin1 centroids derive from geometry the visual already bundles).\n\nimport { CITY_PACKED, ADMIN1_PACKED, ZIP3_PACKED } from \"./geoPointTables.generated\";\n\nexport type GeoPointPrecision = \"latlon\" | \"city\" | \"zip3\" | \"state\";\n\nexport type GeoPointResult = {\n lat: number;\n lon: number;\n precision: GeoPointPrecision;\n /** The city name was ambiguous and the largest match was chosen. */\n ambiguous?: boolean;\n};\n\n// \u2500\u2500 Normalizers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// MUST match tools/geo_build_points.py norm() or lookups silently miss:\n// strip diacritics, lowercase, non-alphanumerics to space, collapse whitespace.\n// Folding diacritics on BOTH sides is why no alternate-spelling table is needed \u2014\n// \"Montr\u00E9al\" and \"Montreal\" land on the same key.\nexport function normalizePlaceName(s: string): string {\n return s\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\s]/gu, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\ntype CityRow = { a1: string; lon: number; lat: number; pop: number };\ntype Admin1Row = { key: string; cc: string; name: string; lon: number; lat: number };\n\n// Lazily parsed once (the monthNames.ts / countryNameMap pattern) \u2014 the packed\n// strings are module constants, so parsing is deferred until something geocodes.\nlet _cities: Map<string, CityRow[]> | null = null;\nfunction cityIndex(): Map<string, CityRow[]> {\n if (_cities) return _cities;\n const m = new Map<string, CityRow[]>();\n for (const rec of CITY_PACKED.split(\",\")) {\n const p = rec.split(\"|\");\n if (p.length < 5) continue;\n const key = normalizePlaceName(p[0]);\n if (!key) continue;\n const row: CityRow = { a1: p[1], lon: +p[2], lat: +p[3], pop: +p[4] };\n const cur = m.get(key);\n if (cur) cur.push(row); else m.set(key, [row]);\n }\n // The generator emits population-descending within a name, so index 0 is already\n // the largest match; sort defensively so the tie-break can't depend on file order.\n for (const rows of m.values()) rows.sort((a, b) => b.pop - a.pop);\n _cities = m;\n return m;\n}\n\nlet _admins: Map<string, Admin1Row> | null = null; // BOTH code and name -> row\nfunction adminIndex(): Map<string, Admin1Row> {\n if (_admins) return _admins;\n const m = new Map<string, Admin1Row>();\n for (const rec of ADMIN1_PACKED.split(\",\")) {\n const p = rec.split(\"|\");\n if (p.length < 5) continue;\n const row: Admin1Row = { key: p[0], cc: p[1], name: p[2], lon: +p[3], lat: +p[4] };\n // Users type either form: \"ON\" or \"Ontario\", \"TX\" or \"Texas\", \"Jalisco\".\n m.set(normalizePlaceName(row.key), row);\n const nk = normalizePlaceName(row.name);\n if (nk === \"mexico\" && row.cc === \"MX\") {\n // The one state whose name IS a country name in our scope. A \"state\" column\n // that actually holds COUNTRIES would otherwise resolve \"Mexico\" to the State\n // of M\u00E9xico \u2014 contradicting every city in the rest of the country, knocking\n // those rows down to the state tier, and degrading the whole map's reported\n // precision. The state stays reachable by its code and unambiguous long forms;\n // bare \"Mexico\" now reads as \"not an admin1\", so city rows resolve cleanly.\n m.set(\"estado de mexico\", row);\n m.set(\"edomex\", row);\n } else {\n m.set(nk, row);\n }\n }\n _admins = m;\n return m;\n}\n\nlet _zip3: Map<string, { lon: number; lat: number }> | null = null;\nfunction zip3Index(): Map<string, { lon: number; lat: number }> {\n if (_zip3) return _zip3;\n const m = new Map<string, { lon: number; lat: number }>();\n for (const rec of ZIP3_PACKED.split(\",\")) {\n const p = rec.split(\"|\");\n if (p.length < 3) continue;\n m.set(p[0], { lon: +p[1], lat: +p[2] });\n }\n _zip3 = m;\n return m;\n}\n\n/** Resolve a state/province token (\"TX\", \"Texas\", \"ON\", \"Ontario\", \"Jalisco\") to its\n * canonical admin1 key. Null when unrecognized. */\nexport function resolveAdmin1(value: string | null | undefined): string | null {\n if (value === null || value === undefined) return null;\n const k = normalizePlaceName(String(value));\n if (!k) return null;\n return adminIndex().get(k)?.key ?? null;\n}\n\n/**\n * Normalize a ZIP to its 3-digit prefix.\n *\n * 5 digits (or ZIP+4) -> first 3.\n * 4 digits -> Excel/Power BI ate ONE leading zero (\"1001\" -> \"01001\" -> \"010\").\n * 3 digits -> ALREADY a prefix (\"331\" = Miami, \"606\" = Chicago).\n *\n * The 3-digit case is genuinely ambiguous \u2014 it could also be a 5-digit ZIP that lost TWO\n * leading zeros \u2014 and reading it as zero-stripped is actively wrong: only 006-009 exist as\n * \"00x\" prefixes (all Puerto Rico / USVI), so \"606\" would land Chicago data in Puerto Rico.\n * A literal 3-digit value in a data column is overwhelmingly a prefix (it is exactly what\n * our own ZIP-3 choropleth joins on), so the direct reading wins. `zipPrefixCandidates`\n * keeps the zero-stripped reading as a documented fallback for the rare 00xxx originals.\n */\nexport function zipToPrefix3(value: string | null | undefined): string | null {\n return zipPrefixCandidates(value)[0] ?? null;\n}\n\n/** Ordered prefix interpretations, most likely first. Callers with the lookup table\n * available should try each and take the first that exists. */\nexport function zipPrefixCandidates(value: string | null | undefined): string[] {\n if (value === null || value === undefined) return [];\n // \"60614.0\" \u2014 a ZIP that took a round trip through a float (CSV export, a DAX\n // FORMAT, etc.). A real ZIP never contains a dot, so stripping \".0\u2026\" is safe and\n // recovers the value instead of dropping the row to a coarser tier.\n const raw = String(value).trim().replace(/\\.0+$/, \"\");\n if (!raw) return [];\n const m = /^(\\d{5})(-\\d{4})?$/.exec(raw);\n if (m) return [m[1].slice(0, 3)];\n if (/^\\d{4}$/.test(raw)) return [raw.padStart(5, \"0\").slice(0, 3)];\n if (/^\\d{3}$/.test(raw)) {\n const stripped = raw.padStart(5, \"0\").slice(0, 3); // the 00xxx reading\n return stripped === raw ? [raw] : [raw, stripped];\n }\n return [];\n}\n\n/**\n * The cascade, most precise tier first. Every argument is optional \u2014 pass whatever the\n * row actually has and the best available tier wins.\n *\n * Order is deliberate: an explicit coordinate always beats a lookup; a city beats a ZIP\n * prefix (a city is a point, a ZIP-3 is a region); a ZIP prefix beats a whole state.\n */\nexport function resolveGeoPoint(args: {\n lat?: number | string | null;\n lon?: number | string | null;\n city?: string | null;\n state?: string | null;\n zip?: string | null;\n}): GeoPointResult | null {\n // 1. Explicit coordinates \u2014 trust the data over any table. TRIMMED first: a\n // whitespace-only string is \"no value\", but `+\" \"` coerces to 0, and an untrimmed\n // check would fabricate a point at (0,0) \u2014 the null-as-zero trap wearing a\n // coordinate costume. Blank/junk coords fall through to the name tiers instead.\n //\n // EXACT (0,0) is the one coordinate we don't take at face value when the row also\n // carries place NAMES: \"missing = 0\" is a common storage convention, and Null\n // Island contradicting a resolvable city/ZIP/state is overwhelmingly that\n // convention, not a data point in the Gulf of Guinea. Bare (0,0) with nothing\n // else to go on is still honored \u2014 rejecting it outright would fabricate a MISS.\n let zeroZero: GeoPointResult | null = null;\n const la0 = typeof args.lat === \"string\" ? args.lat.trim() : args.lat;\n const lo0 = typeof args.lon === \"string\" ? args.lon.trim() : args.lon;\n if (la0 !== undefined && la0 !== null && la0 !== \"\"\n && lo0 !== undefined && lo0 !== null && lo0 !== \"\") {\n const la = +la0, lo = +lo0;\n if (isFinite(la) && isFinite(lo) && la >= -90 && la <= 90 && lo >= -180 && lo <= 180) {\n if (la === 0 && lo === 0) zeroZero = { lat: 0, lon: 0, precision: \"latlon\" };\n else return { lat: la, lon: lo, precision: \"latlon\" };\n }\n }\n\n const a1 = resolveAdmin1(args.state);\n\n // 2. City (+ state when present to disambiguate).\n if (args.city !== undefined && args.city !== null) {\n const key = normalizePlaceName(String(args.city));\n const rows = key ? cityIndex().get(key) : undefined;\n if (rows && rows.length > 0) {\n if (a1) {\n const exact = rows.find(r => r.a1 === a1);\n // A state that disagrees with every match of this city name is a real\n // conflict (\"Paris, TX\" is fine; \"Plano, ON\" is not) \u2014 fall THROUGH to the\n // coarser tiers rather than silently placing it in the wrong state.\n if (exact) return { lat: exact.lat, lon: exact.lon, precision: \"city\" };\n } else if (rows.length === 1) {\n return { lat: rows[0].lat, lon: rows[0].lon, precision: \"city\" };\n } else {\n // Ambiguous bare name: the largest city wins, and we SAY so.\n return { lat: rows[0].lat, lon: rows[0].lon, precision: \"city\", ambiguous: true };\n }\n }\n }\n\n // 3. ZIP-5 / ZIP-3 -> the 3-digit prefix centroid. Try each interpretation in\n // likelihood order so a 3-digit value that ISN'T a real prefix can still fall\n // back to the zero-stripped reading.\n for (const z3 of zipPrefixCandidates(args.zip)) {\n const hit = zip3Index().get(z3);\n if (hit) return { lat: hit.lat, lon: hit.lon, precision: \"zip3\" };\n }\n\n // 4. State / province alone \u2014 population-weighted centroid.\n if (a1) {\n const row = adminIndex().get(normalizePlaceName(a1));\n if (row) return { lat: row.lat, lon: row.lon, precision: \"state\" };\n }\n\n // No name tier resolved \u2014 a literal (0,0) is all the row claims, so report it.\n return zeroZero;\n}\n\nexport type GeoPointColumns = {\n /** Per-row coordinates, aligned 1:1 with the input; null where unresolved. */\n lat: (number | null)[];\n lon: (number | null)[];\n /** COARSEST precision actually used, so the caller can annotate honestly. */\n precision: GeoPointPrecision | null;\n /** Distinct inputs that resolved to nothing (feeds the off-map annotation). */\n unmatched: string[];\n /** Rows placed by the largest-match tie-break on an ambiguous bare city name. */\n ambiguousRows: number;\n matchedRows: number;\n totalRows: number;\n};\n\nconst PRECISION_RANK: Record<GeoPointPrecision, number> = { latlon: 0, city: 1, zip3: 2, state: 3 };\n\n/**\n * Build aligned __geoLat__ / __geoLon__ columns for a full table. Pure.\n *\n * `precision` reports the COARSEST tier any row needed, because that is what bounds\n * what the chart may claim: one state-centroid row among a thousand city rows still\n * means the map is not uniformly city-accurate.\n */\nexport function buildGeoPointColumns(\n rows: Array<{ lat?: any; lon?: any; city?: any; state?: any; zip?: any }>,\n): GeoPointColumns {\n const lat: (number | null)[] = new Array(rows.length);\n const lon: (number | null)[] = new Array(rows.length);\n const unmatchedSeen = new Set<string>();\n const unmatched: string[] = [];\n let matchedRows = 0, ambiguousRows = 0;\n let coarsest: GeoPointPrecision | null = null;\n\n for (let i = 0; i < rows.length; ++i) {\n const r = rows[i];\n const hit = resolveGeoPoint(r);\n if (hit) {\n lat[i] = hit.lat; lon[i] = hit.lon;\n matchedRows++;\n if (hit.ambiguous) ambiguousRows++;\n if (coarsest === null || PRECISION_RANK[hit.precision] > PRECISION_RANK[coarsest])\n coarsest = hit.precision;\n } else {\n lat[i] = null; lon[i] = null;\n // Report the most specific thing the row offered, so the annotation names\n // something the user recognizes.\n const label = [r.city, r.state, r.zip].filter(v => v !== null && v !== undefined && String(v).trim() !== \"\")\n .map(v => String(v).trim()).join(\", \");\n if (label && !unmatchedSeen.has(label.toLowerCase())) {\n unmatchedSeen.add(label.toLowerCase());\n unmatched.push(label);\n }\n }\n }\n return { lat, lon, precision: coarsest, unmatched, ambiguousRows, matchedRows, totalRows: rows.length };\n}\n\n/** Is this an ALREADY-NORMALIZED name (normalizePlaceName applied) of a known city?\n * geoDetector calls this per distinct value to emit the \"city-name\" kind \u2014 it passes\n * its own normalized form, so this must NOT re-normalize. */\nexport function isKnownCity(normalizedName: string): boolean {\n return !!normalizedName && cityIndex().has(normalizedName);\n}\n\n/** Share of DISTINCT values that are known city names, 0..100 (one decimal). NOTE:\n * production offerability does NOT ride on this \u2014 detectGeo's \"city-name\" kind is the\n * gate (THRESHOLD_PCT 85, \u22652 matches, roster guard). This is the standalone measure\n * for harness/MCP callers; keep its notion of \"match\" aligned with isKnownCity. */\nexport function cityMatchPct(values: Array<string | null | undefined>): number {\n const seen = new Set<string>();\n let matched = 0;\n for (const v of values) {\n if (v === null || v === undefined) continue;\n const k = normalizePlaceName(String(v));\n if (!k || seen.has(k)) continue;\n seen.add(k);\n if (cityIndex().has(k)) matched++;\n }\n return seen.size === 0 ? 0 : Math.round((matched / seen.size) * 1000) / 10;\n}\n", "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Client-side GEO detector (geo/choropleth Phase 0 \u2014 the enabling SIGNAL)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Measures whether a categorical column is a GEOGRAPHIC dimension and, if so,\n// of which KIND (country / US-state / US-ZIP / US-county). Runs client-side in\n// shape-core so BOTH the Power BI visual and the MCP classifier share ONE\n// implementation \u2014 the profiler has the ACTUAL cell values (the server sees\n// only obfuscated tokens), so geo recognition MUST happen here.\n//\n// What ships to the server is an ENUM + a PERCENT + a bool, never raw values:\n// geoKind \u2014 the most specific kind that clears threshold (see GeoKind)\n// geoMatchPct \u2014 % of DISTINCT values that resolved to that kind\n// geoAmbiguous \u2014 the value set is a known code collision (ISO-2 vs USPS,\n// ZIP vs county-FIPS, or country-name vs US-state-name) that a\n// single column cannot disambiguate; the server gate should be\n// conservative. Set only when genuinely undecidable here.\n// This is strictly less revealing than isTemporal (shipped unconditionally) \u2014\n// \"column X looks like countries, 98% matched\" is not a user secret \u2014 so the\n// caller runs detection regardless of privacy level and ships only the summary.\n//\n// Design decisions (2026-07-21):\n// \u2022 ISO codes are the interchange. Normal form: country \u2192 ISO 3166-1 alpha-3;\n// US state \u2192 USPS. (Normalization to normal form is Phase 1's __geoIso__\n// builder; Phase 0 only DETECTS.)\n// \u2022 Country NAMES are recognized in all 27 languages the visual supports, built\n// at runtime from Intl.DisplayNames(type:'region') (the monthNames.ts pattern \u2014\n// zero shipped name table), UNION a small flat alias overlay (alias \u2192 ISO3) for\n// forms Intl doesn't emit (\"USA\", \"UK\", \"Holland\", historical names). The\n// overlay is a plain map on purpose: trivial for an LLM to extend.\n// \u2022 us-county-NAME is intentionally NOT detected here \u2014 it needs a sibling STATE\n// column to disambiguate (Illinois \"Lake\" vs Indiana \"Lake\"), which a single-\n// column detector can't see. Deferred to Phase 3, where the caller supplies\n// cross-column context.\n//\n// Returns null when nothing clears threshold \u2192 the caller ships no geo fields\n// (older servers ignore them anyway; the signal is inert until Phase 1's gate).\n\n// The city table lives in geoPoint (the point-geocode cascade owns it); this module\n// only asks \"is this a known city?\" to emit the city-name KIND. One-directional \u2014\n// geoPoint never imports geoDetector \u2014 so there is no import cycle.\nimport { isKnownCity, normalizePlaceName } from \"./geoPoint\";\n\n// ISO 3166-1: alpha-2 \u2192 alpha-3, the full assigned set. This is the ONLY geo\n// name/code table that ships (a few KB); everything else (country names in 27\n// languages) is derived at runtime from Intl. Packed as \"A2 A3\" pairs to keep\n// the source compact and the structure typo-resistant.\nconst ISO_3166_PAIRS =\n \"AD AND,AE ARE,AF AFG,AG ATG,AI AIA,AL ALB,AM ARM,AO AGO,AQ ATA,AR ARG,AS ASM,AT AUT,AU AUS,AW ABW,AX ALA,AZ AZE,\" +\n \"BA BIH,BB BRB,BD BGD,BE BEL,BF BFA,BG BGR,BH BHR,BI BDI,BJ BEN,BL BLM,BM BMU,BN BRN,BO BOL,BQ BES,BR BRA,BS BHS,BT BTN,BV BVT,BW BWA,BY BLR,BZ BLZ,\" +\n \"CA CAN,CC CCK,CD COD,CF CAF,CG COG,CH CHE,CI CIV,CK COK,CL CHL,CM CMR,CN CHN,CO COL,CR CRI,CU CUB,CV CPV,CW CUW,CX CXR,CY CYP,CZ CZE,\" +\n \"DE DEU,DJ DJI,DK DNK,DM DMA,DO DOM,DZ DZA,\" +\n \"EC ECU,EE EST,EG EGY,EH ESH,ER ERI,ES ESP,ET ETH,\" +\n \"FI FIN,FJ FJI,FK FLK,FM FSM,FO FRO,FR FRA,\" +\n \"GA GAB,GB GBR,GD GRD,GE GEO,GF GUF,GG GGY,GH GHA,GI GIB,GL GRL,GM GMB,GN GIN,GP GLP,GQ GNQ,GR GRC,GS SGS,GT GTM,GU GUM,GW GNB,GY GUY,\" +\n \"HK HKG,HM HMD,HN HND,HR HRV,HT HTI,HU HUN,\" +\n \"ID IDN,IE IRL,IL ISR,IM IMN,IN IND,IO IOT,IQ IRQ,IR IRN,IS ISL,IT ITA,\" +\n \"JE JEY,JM JAM,JO JOR,JP JPN,\" +\n \"KE KEN,KG KGZ,KH KHM,KI KIR,KM COM,KN KNA,KP PRK,KR KOR,KW KWT,KY CYM,KZ KAZ,\" +\n \"LA LAO,LB LBN,LC LCA,LI LIE,LK LKA,LR LBR,LS LSO,LT LTU,LU LUX,LV LVA,LY LBY,\" +\n \"MA MAR,MC MCO,MD MDA,ME MNE,MF MAF,MG MDG,MH MHL,MK MKD,ML MLI,MM MMR,MN MNG,MO MAC,MP MNP,MQ MTQ,MR MRT,MS MSR,MT MLT,MU MUS,MV MDV,MW MWI,MX MEX,MY MYS,MZ MOZ,\" +\n \"NA NAM,NC NCL,NE NER,NF NFK,NG NGA,NI NIC,NL NLD,NO NOR,NP NPL,NR NRU,NU NIU,NZ NZL,\" +\n \"OM OMN,\" +\n \"PA PAN,PE PER,PF PYF,PG PNG,PH PHL,PK PAK,PL POL,PM SPM,PN PCN,PR PRI,PS PSE,PT PRT,PW PLW,PY PRY,\" +\n \"QA QAT,\" +\n \"RE REU,RO ROU,RS SRB,RU RUS,RW RWA,\" +\n \"SA SAU,SB SLB,SC SYC,SD SDN,SE SWE,SG SGP,SH SHN,SI SVN,SJ SJM,SK SVK,SL SLE,SM SMR,SN SEN,SO SOM,SR SUR,SS SSD,ST STP,SV SLV,SX SXM,SY SYR,SZ SWZ,\" +\n \"TC TCA,TD TCD,TF ATF,TG TGO,TH THA,TJ TJK,TK TKL,TL TLS,TM TKM,TN TUN,TO TON,TR TUR,TT TTO,TV TUV,TW TWN,TZ TZA,\" +\n \"UA UKR,UG UGA,UM UMI,US USA,UY URY,UZ UZB,\" +\n \"VA VAT,VC VCT,VE VEN,VG VGB,VI VIR,VN VNM,VU VUT,\" +\n \"WF WLF,WS WSM,\" +\n \"YE YEM,YT MYT,\" +\n \"ZA ZAF,ZM ZMB,ZW ZWE\";\n\nconst ISO2_TO_ISO3: Map<string, string> = (() => {\n const m = new Map<string, string>();\n for (const pair of ISO_3166_PAIRS.split(\",\")) {\n const [a2, a3] = pair.split(\" \");\n if (a2 && a3) m.set(a2, a3);\n }\n return m;\n})();\nconst ISO2_SET: Set<string> = new Set(ISO2_TO_ISO3.keys());\nconst ISO3_SET: Set<string> = new Set(ISO2_TO_ISO3.values());\n\n// The 27 languages the visual localizes to (mirrors lib/shared/Localizer.ts\n// PHRASES \u2014 the definition of \"languages the visual supports today\"). Country\n// names are recognized in every one of these via Intl. If Localizer's set\n// changes, mirror it here.\nconst SUPPORTED_LANGS = [\n \"en\", \"fr\", \"es\", \"de\", \"it\", \"pt\", \"ru\", \"ja\", \"zh\", \"ko\", \"ar\", \"hi\", \"tr\",\n \"pl\", \"he\", \"nl\", \"sv\", \"fi\", \"no\", \"da\", \"cs\", \"el\", \"uk\", \"ro\", \"id\", \"vi\", \"th\",\n];\n\n// Flat alias overlay for country-name forms Intl.DisplayNames does NOT emit \u2014\n// colloquial abbreviations, common short forms, and historical names. Keys are\n// pre-normalize() spellings (they get normalized at build); values are ISO3.\n// Extend freely (this is the \"trivial for an LLM to generate\" map).\nconst COUNTRY_ALIAS_OVERLAY: Record<string, string> = {\n \"usa\": \"USA\", \"u.s.\": \"USA\", \"u.s.a.\": \"USA\", \"us\": \"USA\", \"america\": \"USA\",\n \"united states of america\": \"USA\", \"the united states\": \"USA\",\n \"uk\": \"GBR\", \"u.k.\": \"GBR\", \"great britain\": \"GBR\", \"britain\": \"GBR\", \"england\": \"GBR\",\n \"south korea\": \"KOR\", \"north korea\": \"PRK\", \"korea\": \"KOR\",\n \"ivory coast\": \"CIV\", \"cote d ivoire\": \"CIV\",\n \"vietnam\": \"VNM\", \"laos\": \"LAO\", \"syria\": \"SYR\", \"iran\": \"IRN\", \"russia\": \"RUS\",\n \"moldova\": \"MDA\", \"tanzania\": \"TZA\", \"bolivia\": \"BOL\", \"venezuela\": \"VEN\",\n \"brunei\": \"BRN\", \"czech republic\": \"CZE\", \"czechia\": \"CZE\", \"slovakia\": \"SVK\",\n \"uae\": \"ARE\", \"united arab emirates\": \"ARE\",\n \"drc\": \"COD\", \"dr congo\": \"COD\", \"democratic republic of the congo\": \"COD\",\n \"republic of the congo\": \"COG\", \"congo\": \"COG\",\n \"swaziland\": \"SWZ\", \"eswatini\": \"SWZ\", \"cape verde\": \"CPV\", \"cabo verde\": \"CPV\",\n \"burma\": \"MMR\", \"myanmar\": \"MMR\", \"holland\": \"NLD\", \"the netherlands\": \"NLD\",\n \"netherlands\": \"NLD\", \"vatican\": \"VAT\", \"vatican city\": \"VAT\", \"palestine\": \"PSE\",\n \"macau\": \"MAC\", \"macao\": \"MAC\", \"hong kong\": \"HKG\", \"taiwan\": \"TWN\",\n \"turkey\": \"TUR\", \"turkiye\": \"TUR\", \"macedonia\": \"MKD\", \"north macedonia\": \"MKD\",\n \"gambia\": \"GMB\", \"the gambia\": \"GMB\", \"bahamas\": \"BHS\", \"the bahamas\": \"BHS\",\n \"east timor\": \"TLS\", \"timor leste\": \"TLS\",\n};\n\n// US states + DC + the five inhabited territories: USPS code \u2194 name. Small\n// enough to ship verbatim (no Intl source for subnational names).\nconst US_STATE_PAIRS: [string, string][] = [\n [\"AL\", \"Alabama\"], [\"AK\", \"Alaska\"], [\"AZ\", \"Arizona\"], [\"AR\", \"Arkansas\"],\n [\"CA\", \"California\"], [\"CO\", \"Colorado\"], [\"CT\", \"Connecticut\"], [\"DE\", \"Delaware\"],\n [\"FL\", \"Florida\"], [\"GA\", \"Georgia\"], [\"HI\", \"Hawaii\"], [\"ID\", \"Idaho\"],\n [\"IL\", \"Illinois\"], [\"IN\", \"Indiana\"], [\"IA\", \"Iowa\"], [\"KS\", \"Kansas\"],\n [\"KY\", \"Kentucky\"], [\"LA\", \"Louisiana\"], [\"ME\", \"Maine\"], [\"MD\", \"Maryland\"],\n [\"MA\", \"Massachusetts\"], [\"MI\", \"Michigan\"], [\"MN\", \"Minnesota\"], [\"MS\", \"Mississippi\"],\n [\"MO\", \"Missouri\"], [\"MT\", \"Montana\"], [\"NE\", \"Nebraska\"], [\"NV\", \"Nevada\"],\n [\"NH\", \"New Hampshire\"], [\"NJ\", \"New Jersey\"], [\"NM\", \"New Mexico\"], [\"NY\", \"New York\"],\n [\"NC\", \"North Carolina\"], [\"ND\", \"North Dakota\"], [\"OH\", \"Ohio\"], [\"OK\", \"Oklahoma\"],\n [\"OR\", \"Oregon\"], [\"PA\", \"Pennsylvania\"], [\"RI\", \"Rhode Island\"], [\"SC\", \"South Carolina\"],\n [\"SD\", \"South Dakota\"], [\"TN\", \"Tennessee\"], [\"TX\", \"Texas\"], [\"UT\", \"Utah\"],\n [\"VT\", \"Vermont\"], [\"VA\", \"Virginia\"], [\"WA\", \"Washington\"], [\"WV\", \"West Virginia\"],\n [\"WI\", \"Wisconsin\"], [\"WY\", \"Wyoming\"], [\"DC\", \"District of Columbia\"],\n [\"PR\", \"Puerto Rico\"], [\"GU\", \"Guam\"], [\"VI\", \"U.S. Virgin Islands\"],\n [\"AS\", \"American Samoa\"], [\"MP\", \"Northern Mariana Islands\"],\n];\nconst USPS_SET: Set<string> = new Set(US_STATE_PAIRS.map(p => p[0]));\n\n// Common / traditional (AP-style) state abbreviations users actually type \u2014 the forms\n// USPS 2-letter codes and full names both miss (\"Calif.\", \"Fla.\", \"N.Y.\", \"Wash.\"). Keys\n// are pre-normalize spellings (normalizeName strips the periods \u2192 \"calif\", \"n y\", \"wash\");\n// values are USPS. A plain map so an LLM can extend it (the COUNTRY_ALIAS_OVERLAY pattern).\n// Bare 2-letter forms (with/without a period) already resolve via USPS us-state-code, so\n// this focuses on the distinctive multi-char + punctuated abbreviations.\nconst US_STATE_ALIAS_OVERLAY: Record<string, string> = {\n \"Ala.\": \"AL\", \"Alab.\": \"AL\", \"Ariz.\": \"AZ\", \"Ark.\": \"AR\",\n \"Calif.\": \"CA\", \"Cal.\": \"CA\", \"Colo.\": \"CO\", \"Col.\": \"CO\", \"Conn.\": \"CT\",\n \"Del.\": \"DE\", \"Fla.\": \"FL\", \"Flor.\": \"FL\", \"Ga.\": \"GA\",\n \"Ill.\": \"IL\", \"Ind.\": \"IN\", \"Kan.\": \"KS\", \"Kans.\": \"KS\", \"Ky.\": \"KY\",\n \"La.\": \"LA\", \"Md.\": \"MD\", \"Mass.\": \"MA\", \"Mich.\": \"MI\", \"Minn.\": \"MN\",\n \"Miss.\": \"MS\", \"Mo.\": \"MO\", \"Mont.\": \"MT\", \"Neb.\": \"NE\", \"Nebr.\": \"NE\",\n \"Nev.\": \"NV\", \"N.H.\": \"NH\", \"N.J.\": \"NJ\", \"N.M.\": \"NM\", \"N.Mex.\": \"NM\", \"N. Mex.\": \"NM\",\n \"N.Y.\": \"NY\", \"N.C.\": \"NC\", \"N.D.\": \"ND\", \"N.Dak.\": \"ND\", \"N. Dak.\": \"ND\",\n \"Okla.\": \"OK\", \"Ore.\": \"OR\", \"Oreg.\": \"OR\",\n \"Pa.\": \"PA\", \"Penn.\": \"PA\", \"Penna.\": \"PA\", \"R.I.\": \"RI\", \"S.C.\": \"SC\",\n \"S.D.\": \"SD\", \"S.Dak.\": \"SD\", \"S. Dak.\": \"SD\", \"Tenn.\": \"TN\", \"Tex.\": \"TX\",\n \"Vt.\": \"VT\", \"Va.\": \"VA\", \"Wash.\": \"WA\", \"W.Va.\": \"WV\", \"W. Va.\": \"WV\",\n \"Wis.\": \"WI\", \"Wisc.\": \"WI\", \"Wyo.\": \"WY\",\n \"D.C.\": \"DC\", \"Wash. D.C.\": \"DC\", \"Wash., D.C.\": \"DC\",\n \"P.R.\": \"PR\", \"V.I.\": \"VI\",\n};\nconst STATE_NAME_TO_USPS: Map<string, string> = (() => {\n const m = new Map<string, string>();\n for (const [code, name] of US_STATE_PAIRS) m.set(normalizeName(name), code);\n // A couple of common alternates Intl/CLDR or users write differently.\n m.set(normalizeName(\"Washington DC\"), \"DC\");\n m.set(normalizeName(\"Washington D.C.\"), \"DC\");\n m.set(normalizeName(\"US Virgin Islands\"), \"VI\");\n // Common/traditional abbreviations (Calif., Fla., N.Y., \u2026).\n for (const [alias, code] of Object.entries(US_STATE_ALIAS_OVERLAY)) {\n const key = normalizeName(alias);\n if (key && !m.has(key)) m.set(key, code);\n }\n return m;\n})();\n\n// Valid 2-digit state FIPS prefixes (50 states + DC + 5 territories). A 5-digit\n// numeric is a plausible county FIPS only when its first two digits are in here;\n// otherwise it's a ZIP (e.g. \"90210\" \u2192 prefix \"90\" \u2209 set \u2192 not a county).\nconst STATE_FIPS_SET: Set<string> = new Set([\n \"01\", \"02\", \"04\", \"05\", \"06\", \"08\", \"09\", \"10\", \"11\", \"12\", \"13\", \"15\", \"16\",\n \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\",\n \"30\", \"31\", \"32\", \"33\", \"34\", \"35\", \"36\", \"37\", \"38\", \"39\", \"40\", \"41\", \"42\",\n \"44\", \"45\", \"46\", \"47\", \"48\", \"49\", \"50\", \"51\", \"53\", \"54\", \"55\", \"56\",\n \"60\", \"66\", \"69\", \"72\", \"78\",\n]);\n\n// \u2500\u2500 Normalizers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Codes: uppercase + trim, so \"us\" / \" US \" / \"Us\" all compare as \"US\".\nfunction normalizeCode(s: string): string {\n return s.trim().toUpperCase();\n}\n// Names: lowercase, strip diacritics, drop punctuation (keep letters/digits of\n// ANY script + spaces so CJK / Arabic / Cyrillic names survive), collapse\n// whitespace. \"C\u00F4te d'Ivoire\" \u2192 \"cote d ivoire\"; \"\u65E5\u672C\" stays \"\u65E5\u672C\".\n// ONE implementation with geoPoint's normalizePlaceName, not a copy: isKnownCity's\n// contract is \"already normalized, do not re-normalize\" \u2014 detectGeo passes THIS\n// function's output into the city table, so the two normalizers diverging would make\n// city detection silently miss with no test catching it. (2026-07-25 sweep.)\n// Declared `function` (hoisted) \u2014 module-init tables above/below call it during load.\nfunction normalizeName(s: string): string {\n return normalizePlaceName(s);\n}\n\n// Country-name \u2192 ISO3 lookup, built once (lazily) from Intl across all supported\n// languages UNION the alias overlay. English is added first so it wins any\n// cross-language normalized-key collision; the overlay is applied last so an\n// explicit alias always resolves. Cached module-wide (monthNames.ts pattern).\nlet _countryNameMap: Map<string, string> | null = null;\nfunction countryNameMap(): Map<string, string> {\n if (_countryNameMap) return _countryNameMap;\n const m = new Map<string, string>();\n // English first (authoritative on collisions), then the rest.\n const langs = [\"en\", ...SUPPORTED_LANGS.filter(l => l !== \"en\")];\n for (const lang of langs) {\n let dn: Intl.DisplayNames | null = null;\n try {\n dn = new Intl.DisplayNames([lang], { type: \"region\" });\n } catch {\n continue; // runtime without this locale's data \u2014 skip it\n }\n for (const [a2, a3] of ISO2_TO_ISO3) {\n let name: string | undefined;\n try {\n name = dn.of(a2);\n } catch {\n name = undefined;\n }\n // Intl returns the input code when it has no localized name.\n if (!name || name === a2) continue;\n const key = normalizeName(name);\n if (key && !m.has(key)) m.set(key, a3);\n }\n }\n for (const [alias, a3] of Object.entries(COUNTRY_ALIAS_OVERLAY)) {\n const key = normalizeName(alias);\n if (key) m.set(key, a3); // overlay wins \u2014 explicit intent\n }\n _countryNameMap = m;\n return m;\n}\n\n// \u2500\u2500 Column-name tokens (tiebreaker / guard, never a substitute) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst COUNTRY_NAME_TOKENS = new Set([\"country\", \"countries\", \"nation\", \"nationality\", \"iso\", \"geo\"]);\nconst STATE_NAME_TOKENS = new Set([\"state\", \"province\", \"st\"]);\nconst ZIP_NAME_TOKENS = new Set([\"zip\", \"zipcode\", \"postal\", \"postcode\", \"plz\"]);\nconst COUNTY_NAME_TOKENS = new Set([\"county\", \"fips\", \"borough\", \"parish\"]);\nconst CITY_NAME_TOKENS = new Set([\"city\", \"cities\", \"town\", \"municipality\", \"metro\", \"location\", \"place\"]);\n\nfunction tokenizeName(name: string): string[] {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .replace(/[_\\-./]+/g, \" \")\n .toLowerCase()\n .split(/\\s+/)\n .filter(t => t.length > 0);\n}\nfunction hasAnyToken(columnName: string | undefined, tokens: Set<string>): boolean {\n if (!columnName) return false;\n for (const t of tokenizeName(columnName)) if (tokens.has(t)) return true;\n return false;\n}\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport type GeoKind =\n | \"country-iso3\"\n | \"country-iso2\"\n | \"country-name\"\n | \"us-state-code\"\n | \"us-state-name\"\n | \"us-zip5\"\n | \"us-county-fips\"\n // NOT a choropleth join key \u2014 no bundled geometry is keyed by city. It marks a column\n // whose values geoPoint can turn into a COORDINATE, which is what makes a point map\n // (North America Bubbles) offerable on data that has no lat/lon columns.\n | \"city-name\";\n\nexport type GeoDetectionResult = {\n geoKind: GeoKind;\n geoMatchPct: number; // 0..100, one decimal\n geoAmbiguous?: boolean; // known single-column code collision\n};\n\n// Emit the signal only above this match rate. The client REPORTS the measured\n// pct; the server gate re-checks against sysparm LLMGeoMinMatchPct (Phase 1).\nconst THRESHOLD_PCT = 85;\n// Dictionary-word kinds (country/state NAMES) collide with person/place names\n// (Chad, Georgia, Jordan). Require either a decent number of matched distinct\n// values OR a geo-ish column name, so a 3-name roster doesn't read as countries.\nconst NAME_KIND_MIN_DISTINCT = 5;\n// Cap the work on pathological high-cardinality free-text columns. Real geo\n// columns are far below this (\u2264 ~250 countries, 56 states, ~42k ZIPs). ZIP/FIPS\n// are cheap regex either way; this only bounds the dictionary path.\nconst MAX_DISTINCT_CONSIDERED = 60000;\n\ntype Candidate = { kind: GeoKind; pct: number; matched: number; spec: number };\n\n/**\n * Detect whether a column's values are a geographic dimension.\n * @param rawValues DISTINCT raw cell values (order irrelevant; duplicates are\n * harmless but the caller usually passes the distinct set).\n * @param columnName the source column name (tiebreaker/guard for ambiguous codes).\n * @param _locale reserved (country names already union all 27 languages; the\n * report locale is not needed to match). Kept for signature symmetry.\n */\nexport function detectGeo(\n rawValues: string[],\n columnName?: string,\n _locale?: string,\n): GeoDetectionResult | null {\n if (!rawValues || rawValues.length === 0) return null;\n\n // Distinct (CASE-INSENSITIVE \u2014 regions are the same place regardless of case,\n // so \"USA\"/\"usa\" is one region, not two), non-empty, string-ified. The first\n // spelling seen represents the region for matching.\n const distinct: string[] = [];\n const seen = new Set<string>();\n for (const v of rawValues) {\n if (v === null || v === undefined) continue;\n const s = String(v).trim();\n if (s.length === 0) continue;\n const key = s.toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n distinct.push(s);\n if (distinct.length > MAX_DISTINCT_CONSIDERED) return null;\n }\n const total = distinct.length;\n if (total < 2) return null; // a 1-region \"map\" is degenerate\n\n const upper = distinct.map(normalizeCode);\n const names = distinct.map(normalizeName);\n const cnMap = countryNameMap();\n\n let iso3 = 0, iso2 = 0, usps = 0, cName = 0, sName = 0, zip = 0, county = 0, cityHits = 0;\n let iso2AndUsps = 0; // values valid as BOTH an ISO-2 and a USPS code\n let cNameAndSName = 0; // values matching BOTH a country and a US-state name\n let stateBoth = 0; // values valid as BOTH a USPS code and a state name/abbrev\n // A column named zip/postal whose values Power BI coerced to a NUMBER lost its\n // leading zero(s): a whole New-England/NJ/PR (0xxxx) column becomes bare 3-4 digit\n // integers. Count those as ZIPs ONLY under a zip name token, so the column still\n // resolves as us-zip5 instead of failing detection outright (toGeoIso then pads\n // them back). Gated by the token so a generic small-integer column never misfires.\n const zipHint = hasAnyToken(columnName, ZIP_NAME_TOKENS);\n for (let i = 0; i < total; ++i) {\n const u = upper[i];\n const nm = names[i];\n const raw = distinct[i];\n const isIso2 = ISO2_SET.has(u);\n const isUsps = USPS_SET.has(u);\n if (ISO3_SET.has(u)) iso3++;\n if (isIso2) iso2++;\n if (isUsps) usps++;\n if (isIso2 && isUsps) iso2AndUsps++;\n const isCName = cnMap.has(nm);\n const isSName = STATE_NAME_TO_USPS.has(nm);\n if (isCName) cName++;\n if (isSName) sName++;\n if (isCName && isSName) cNameAndSName++;\n if (isUsps && isSName) stateBoth++;\n if (/^\\d{5}(-\\d{4})?$/.test(raw) || (zipHint && /^\\d{3,4}$/.test(raw))) zip++;\n if (/^\\d{5}$/.test(raw) && STATE_FIPS_SET.has(raw.slice(0, 2))) county++;\n if (isKnownCity(nm)) cityHits++;\n }\n\n const pct = (n: number) => (n / total) * 100;\n const cands: Candidate[] = [];\n const add = (kind: GeoKind, matched: number, spec: number, guardNameKind = false) => {\n const p = pct(matched);\n if (p < THRESHOLD_PCT || matched < 2) return;\n if (guardNameKind && matched < NAME_KIND_MIN_DISTINCT) {\n const tokens = kind === \"country-name\" ? COUNTRY_NAME_TOKENS\n : kind === \"city-name\" ? CITY_NAME_TOKENS\n : STATE_NAME_TOKENS;\n if (!hasAnyToken(columnName, tokens)) return; // likely a person/place roster\n }\n cands.push({ kind, matched, spec, pct: p });\n };\n // spec = specificity rank for tie-breaking (higher = more certain / less\n // collision-prone). ISO-3 and full names beat 2-letter codes and numerics.\n add(\"country-iso3\", iso3, 6);\n add(\"country-name\", cName, 5, true);\n add(\"us-state-name\", sName, 5, true);\n add(\"us-zip5\", zip, 4);\n add(\"us-county-fips\", county, 4);\n add(\"country-iso2\", iso2, 3);\n add(\"us-state-code\", usps, 3);\n // CITY sits at the BOTTOM of the specificity order on purpose. Several city names are\n // also state or country names (\"New York\", \"Washington\", \"Mexico\", \"Quebec\"), and a\n // column of those is far likelier to be the region than the city \u2014 so any region kind\n // that clears threshold outranks city. Guarded like the other dictionary kinds: a short\n // roster of proper nouns needs a city-ish column name before it reads as cities.\n add(\"city-name\", cityHits, 2, true);\n // A column that MIXES USPS codes with state names/abbreviations (\"TX\", \"California\",\n // \"Fla.\") splits between us-state-code and us-state-name, so neither kind alone clears\n // threshold. They are the SAME concept (a state) and toGeoIso resolves either form for\n // either kind, so score the UNION as one us-state candidate \u2014 but only when the column\n // genuinely mixes both forms (a pure-code column keeps its us-state-code candidate so\n // the ISO-2/USPS collision handling below is untouched).\n const uspsOnly = usps - stateBoth, sNameOnly = sName - stateBoth;\n if (uspsOnly > 0 && sNameOnly > 0) add(\"us-state-name\", usps + sName - stateBoth, 5, true);\n\n if (cands.length === 0) return null;\n cands.sort((a, b) => (b.pct - a.pct) || (b.spec - a.spec));\n\n const winner = cands[0];\n let ambiguous = false;\n\n // Resolve the three known collisions when the runner-up is the colliding\n // partner and the two are close (within 15 pts): let a decisive column name\n // pick; otherwise keep the default winner and FLAG ambiguity.\n const partnerOf: Partial<Record<GeoKind, GeoKind>> = {\n \"country-iso2\": \"us-state-code\", \"us-state-code\": \"country-iso2\",\n \"us-zip5\": \"us-county-fips\", \"us-county-fips\": \"us-zip5\",\n \"country-name\": \"us-state-name\", \"us-state-name\": \"country-name\",\n };\n const partnerKind = partnerOf[winner.kind];\n const runnerUp = cands[1];\n const collides = partnerKind && runnerUp && runnerUp.kind === partnerKind\n && Math.abs(winner.pct - runnerUp.pct) <= 15;\n\n if (winner.kind === \"country-iso2\" || winner.kind === \"us-state-code\") {\n // How much of the matched set is valid under BOTH interpretations.\n const overlapShare = winner.matched > 0 ? iso2AndUsps / winner.matched : 0;\n const countryHint = hasAnyToken(columnName, COUNTRY_NAME_TOKENS);\n const stateHint = hasAnyToken(columnName, STATE_NAME_TOKENS);\n if (collides || overlapShare >= 0.5) {\n if (countryHint && !stateHint) return finalize(\"country-iso2\", iso2, false);\n if (stateHint && !countryHint) return finalize(\"us-state-code\", usps, false);\n ambiguous = true; // undecidable from this column alone\n }\n } else if (winner.kind === \"us-zip5\" || winner.kind === \"us-county-fips\") {\n // Numeric 5-digit: ZIP and county-FIPS share the pattern. If any value's\n // prefix is not a valid state FIPS, it CAN'T be county \u2192 ZIP wins clean.\n const allCountyPlausible = county === zip && zip === total;\n const zipHint = hasAnyToken(columnName, ZIP_NAME_TOKENS);\n const countyHint = hasAnyToken(columnName, COUNTY_NAME_TOKENS);\n if (countyHint && !zipHint) return finalize(\"us-county-fips\", county, false);\n if (zipHint && !countyHint) return finalize(\"us-zip5\", zip, false);\n // No decisive name: default to ZIP (far more common in BI data); flag\n // ambiguous only when every value is ALSO a plausible county code.\n if (allCountyPlausible) return finalize(\"us-zip5\", zip, true);\n return finalize(\"us-zip5\", zip, false);\n } else if (collides && (winner.kind === \"country-name\" || winner.kind === \"us-state-name\")) {\n const overlapShare = winner.matched > 0 ? cNameAndSName / winner.matched : 0;\n const countryHint = hasAnyToken(columnName, COUNTRY_NAME_TOKENS);\n const stateHint = hasAnyToken(columnName, STATE_NAME_TOKENS);\n if (countryHint && !stateHint) return finalize(\"country-name\", cName, false);\n if (stateHint && !countryHint) return finalize(\"us-state-name\", sName, false);\n if (overlapShare >= 0.5) ambiguous = true;\n }\n\n return finalize(winner.kind, winner.matched, ambiguous);\n\n function finalize(kind: GeoKind, matched: number, amb: boolean): GeoDetectionResult {\n const r: GeoDetectionResult = { geoKind: kind, geoMatchPct: Math.round(pct(matched) * 10) / 10 };\n if (amb) r.geoAmbiguous = true;\n return r;\n }\n}\n\n// \u2500\u2500 __geoIso__ normalization (Phase 1 \u00A73a foundation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Deterministic value \u2192 NORMAL FORM for a known geoKind. This is the join key the\n// generated chart uses (Plotly locations / D3 path bind); the LLM never builds a\n// name\u2192code mapping in code \u2014 the client precomputes it here (single source, shared\n// by the visual's __geoIso__ builder and the MCP). Normal forms:\n// country-* \u2192 ISO 3166-1 alpha-3\n// us-state-* \u2192 USPS 2-letter\n// us-zip5 \u2192 5-digit ZIP (\u25004 extension stripped)\n// us-county-fips \u2192 5-digit FIPS\n// Returns null when the value doesn't resolve \u2192 the caller null-fills __geoIso__ and\n// counts it as unmatched (the classic silent-drop failure, handled deterministically).\nexport function toGeoIso(value: string | null | undefined, geoKind: GeoKind): string | null {\n if (value === null || value === undefined) return null;\n const raw = String(value).trim();\n if (raw.length === 0) return null;\n switch (geoKind) {\n case \"country-iso3\": {\n const u = normalizeCode(raw);\n return ISO3_SET.has(u) ? u : null;\n }\n case \"country-iso2\": {\n return ISO2_TO_ISO3.get(normalizeCode(raw)) ?? null;\n }\n case \"country-name\": {\n return countryNameMap().get(normalizeName(raw)) ?? null;\n }\n case \"us-state-code\":\n case \"us-state-name\": {\n // Tolerant regardless of the detected kind: try USPS code first, then the\n // name/common-abbreviation map (\"California\", \"Calif.\", \"N.Y.\"). So a code\n // column with a stray \"Calif.\" \u2014 or a name column with a stray \"CA\" \u2014 both\n // resolve rather than dropping to unmatched.\n const u = normalizeCode(raw);\n if (USPS_SET.has(u)) return u;\n return STATE_NAME_TO_USPS.get(normalizeName(raw)) ?? null;\n }\n case \"us-zip5\": {\n const m = /^(\\d{5})(-\\d{4})?$/.exec(raw);\n if (m) return m[1];\n // Power BI / Excel silently coerce a ZIP column to a NUMBER, dropping the\n // leading zero(s): \"01001\" -> 1001, \"00501\" -> 501. Within a column already\n // resolved as ZIP-5, a bare 3-4 digit integer is a zero-stripped ZIP \u2014 left-\n // pad to recover it (the whole New England / NJ / PR 0xxxx block otherwise\n // reads as unmatched, prod 2026-07-23). USPS ZIPs are 00501..99950, so any\n // <5-digit value is unambiguously a stripped leading zero.\n if (/^\\d{3,4}$/.test(raw)) return raw.padStart(5, \"0\");\n return null;\n }\n case \"us-county-fips\": {\n return /^\\d{5}$/.test(raw) && STATE_FIPS_SET.has(raw.slice(0, 2)) ? raw : null;\n }\n // \"city-name\" deliberately falls through: no bundled geometry is keyed by city,\n // so it is NOT a join kind \u2014 it feeds the geoPoint COORDINATE cascade instead.\n // Callers building a join column must gate on isJoinGeoKind first; asking this\n // function to join on city-name would null every row (a silent blank map).\n default:\n return null;\n }\n}\n\n/** True for kinds that resolve to a polygon join key (__geoIso__ against feature.id).\n * \"city-name\" is a GeoKind but NOT a join kind \u2014 it marks a column the geoPoint\n * cascade can turn into a coordinate. Building a join column from a non-join kind\n * produces an all-null __geoIso__ = a blank choropleth with no error. */\nexport function isJoinGeoKind(kind: string | null | undefined): boolean {\n switch (kind) {\n case \"country-iso3\":\n case \"country-iso2\":\n case \"country-name\":\n case \"us-state-code\":\n case \"us-state-name\":\n case \"us-zip5\":\n case \"us-county-fips\":\n return true;\n default:\n return false;\n }\n}\n\nexport type GeoIsoColumn = {\n // Per-row normal form, aligned 1:1 with the input values; null where a value\n // didn't resolve (\u2192 the synthetic __geoIso__ cell is null / not drawn).\n iso: (string | null)[];\n // Distinct raw values that failed to resolve \u2014 feeds options.geoUnmatched so\n // the chart can annotate \"N regions unmatched\" instead of silently dropping.\n unmatched: string[];\n // Convenience counts for the annotation + gate.\n matchedRows: number;\n totalRows: number;\n};\n\n/**\n * Build the aligned __geoIso__ column for a full column of values under a known\n * geoKind. Pure \u2014 the visual appends `iso` beside __rowIdx__ and surfaces\n * `unmatched` via options.geoUnmatched (Phase 1 \u00A73a).\n */\nexport function buildGeoIsoColumn(values: (string | null | undefined)[], geoKind: GeoKind): GeoIsoColumn {\n const iso: (string | null)[] = new Array(values.length);\n const unmatchedSeen = new Set<string>();\n const unmatched: string[] = [];\n let matchedRows = 0;\n for (let i = 0; i < values.length; ++i) {\n const norm = toGeoIso(values[i], geoKind);\n iso[i] = norm;\n if (norm !== null) {\n matchedRows++;\n } else {\n const v = values[i];\n if (v !== null && v !== undefined) {\n const s = String(v).trim();\n if (s.length > 0) {\n const key = s.toLowerCase();\n if (!unmatchedSeen.has(key)) { unmatchedSeen.add(key); unmatched.push(s); }\n }\n }\n }\n }\n return { iso, unmatched, matchedRows, totalRows: values.length };\n}\n", "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// POINT-COLUMN ROLE RESOLUTION \u2014 which column is the city, the state, the ZIP\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// geoPoint.ts answers \"given a city/state/zip, WHERE is this row?\". This module answers\n// the question that comes first: \"which COLUMN is the city, and which is the state?\".\n//\n// That used to be answered by the model: the codegen response names pointCityColumn /\n// pointStateColumn / pointZipColumn and the host geocoded from whatever it was handed.\n// A production North America point map showed why that is not sufficient. The model was\n// City{distinct=23}, StateCode{distinct=16}, Country{distinct=4}, <2 measures>\n// and answered `{\"city\":\"City\"}` \u2014 no state. Geocoding then ran city-only, so the\n// City+State tier never engaged, the \"state contradicts city\" guard never had a state\n// to check, and 8 of 32 marks were placed by the largest-match tie-break on a bare\n// name (\"Burlington\" -> Burlington ONTARIO, not Vermont) with the map presenting them\n// as ordinary points. Nothing was broken; a field was simply left blank.\n//\n// These roles are DETERMINISTICALLY RESOLVABLE from the values, and every classifier\n// needed already exists in geoPoint.ts. So the model's answer is treated as a HINT that\n// gets VERIFIED, and any role it omitted is BACKFILLED from the data. A hint is still\n// useful \u2014 it disambiguates which of two plausible columns was intended \u2014 but it is\n// never the only thing standing between the user and a wrong coordinate.\n//\n// THE COUNTRY TRAP this also closes. `resolveAdmin1(\"CA\")` is CALIFORNIA. Measured\n// across 15 country identifiers (\"US\" \"USA\" \"United States\" \"CAN\" \"Canada\" \"MX\" \"MEX\"\n// \"Mexico\" \u2026) that is the ONLY collision \u2014 but it is a catastrophic one: a Country\n// column bound into the state slot makes every Canadian city fail to match inside\n// California and fall back to the California centroid, piling Mississauga, Montr\u00E9al,\n// Burnaby and Laval onto one dot outside Bakersfield. \"Canada, inside the USA.\"\n//\n// It cannot be fixed the way the sibling \"Mexico\" collision was (geoPoint.ts drops the\n// bare admin1 NAME \"mexico\" because the country reading dominates and the State of\n// M\u00E9xico is obscure). For \"CA\" the California reading is overwhelmingly the common one\n// in real US data, so deleting the key would break far more than it fixes. The fix has\n// to be CONTEXTUAL, which is exactly what a column-level pass can do and a row-level\n// lookup cannot: judge the column by ALL its values at once.\n\nimport { normalizePlaceName, resolveAdmin1, isKnownCity, zipPrefixCandidates } from \"./geoPoint\";\n\n/** The place parts a coordinate can be resolved from. Any subset. */\nexport type PointBind = {\n city?: string;\n state?: string;\n zip?: string;\n lat?: string;\n lon?: string;\n};\n\nexport type PointRoleResolution = {\n /** The binding to geocode with: hint, minus anything refused, plus anything backfilled. */\n bind: PointBind | null;\n /** Roles the hint omitted that were resolved from the data (\"state=StateCode\"). */\n backfilled: string[];\n /** Roles the hint named that were REFUSED, with why (\"state=Country (country column)\"). */\n refused: string[];\n};\n\n// Share of DISTINCT values a column must match before a role is claimed for it. Mirrors\n// geoDetector's THRESHOLD_PCT: high enough that a column of something else cannot claim\n// a role by accident, loose enough to tolerate the blanks and typos real data carries.\nconst THRESHOLD_PCT = 85;\n// A role BACKFILLED from the data (no hint) additionally needs this many distinct\n// matches \u2014 geoDetector's roster guard. One matching value is a coincidence, not a\n// column: a lone \"CA\" is far likelier to be Canada than a single-state California table.\nconst MIN_DISTINCT_BACKFILL = 2;\n// ZIP is held to a HIGHER bar than the name roles, because its classifier is pure digit\n// shape and measures are digits too: a Revenue column carrying 54300 / 48900 / 33700\n// reads as a perfectly good run of 5-digit ZIPs. Callers are expected to keep measures\n// out of the candidate list, but that is one `isMeasure` flag away from failing open, and\n// a measure silently adopted as the ZIP column would relocate every point on the map. A\n// real ZIP column is ALL ZIPs, so demand exactly that.\nconst ZIP_THRESHOLD_PCT = 100;\n// Classification reads DISTINCT values, so a wide column costs no more than a narrow\n// one; this only bounds a pathological all-unique text column.\nconst MAX_DISTINCT_SCAN = 400;\n\n// Country identifiers, normalized. Deliberately NOT a general ISO table: this exists\n// only to break the one measured admin1 collision, and a column of countries is\n// recognized by ALL its values being country identifiers, not by any single one.\nconst COUNTRY_IDS = new Set([\n \"us\", \"usa\", \"u s\", \"u s a\", \"united states\", \"united states of america\", \"america\",\n \"ca\", \"can\", \"canada\",\n \"mx\", \"mex\", \"mexico\", \"estados unidos mexicanos\",\n]);\n\n/** Distinct, normalized, non-blank values of a column (capped). */\nfunction distinctNormalized(values: Array<unknown>): string[] {\n const seen = new Set<string>();\n for (const v of values) {\n if (v === null || v === undefined) continue;\n const k = normalizePlaceName(String(v));\n if (!k) continue;\n if (!seen.has(k)) {\n seen.add(k);\n if (seen.size >= MAX_DISTINCT_SCAN) break;\n }\n }\n return Array.from(seen);\n}\n\n/**\n * Is this column a list of COUNTRIES?\n *\n * True only when EVERY distinct value is a country identifier. That is the precise\n * shape of the trap: {US, CA, MX} and {CA} are countries, while {CA, TX} is a US state\n * column that happens to contain California. Being strict here matters \u2014 a loose test\n * would start refusing legitimate state columns.\n */\nexport function looksLikeCountryColumn(values: Array<unknown>): boolean {\n const d = distinctNormalized(values);\n if (d.length === 0) return false;\n return d.every(v => COUNTRY_IDS.has(v));\n}\n\n/** Share of DISTINCT values that resolve to a state/province, 0..100 (one decimal). */\nexport function admin1MatchPct(values: Array<unknown>): number {\n const d = distinctNormalized(values);\n if (d.length === 0) return 0;\n let n = 0;\n for (const v of d) if (resolveAdmin1(v)) n++;\n return Math.round((n / d.length) * 1000) / 10;\n}\n\n/** Distinct RAW (trimmed) values \u2014 for ZIPs, which normalizePlaceName would mangle:\n * it rewrites punctuation to spaces, turning \"90210-1234\" into \"90210 1234\", which\n * zipPrefixCandidates' ZIP+4 pattern then rejects. */\nfunction distinctRaw(values: Array<unknown>): string[] {\n const seen = new Set<string>();\n for (const v of values) {\n if (v === null || v === undefined) continue;\n const s = String(v).trim();\n if (!s) continue;\n if (!seen.has(s)) {\n seen.add(s);\n if (seen.size >= MAX_DISTINCT_SCAN) break;\n }\n }\n return Array.from(seen);\n}\n\n/** Share of DISTINCT values that read as a ZIP / ZIP-prefix, 0..100 (one decimal). */\nexport function zipMatchPct(values: Array<unknown>): number {\n const d = distinctRaw(values);\n if (d.length === 0) return 0;\n let n = 0;\n for (const v of d) if (zipPrefixCandidates(v).length > 0) n++;\n return Math.round((n / d.length) * 1000) / 10;\n}\n\n/** Share of DISTINCT values that are known city names, 0..100 (one decimal). */\nfunction cityPct(values: Array<unknown>): number {\n const d = distinctNormalized(values);\n if (d.length === 0) return 0;\n let n = 0;\n for (const v of d) if (isKnownCity(v)) n++;\n return Math.round((n / d.length) * 1000) / 10;\n}\n\nfunction distinctMatches(values: Array<unknown>, pred: (v: string) => boolean): number {\n let n = 0;\n for (const v of distinctNormalized(values)) if (pred(v)) n++;\n return n;\n}\n\n/** distinctMatches over RAW values (ZIP only \u2014 see distinctRaw). */\nfunction distinctRawMatches(values: Array<unknown>, pred: (v: string) => boolean): number {\n let n = 0;\n for (const v of distinctRaw(values)) if (pred(v)) n++;\n return n;\n}\n\n/**\n * Verify the hinted point-column roles against the data and fill in what the hint left\n * out. Pure: `columns` are the available column names, `rows` the row objects keyed by\n * those names, `hint` whatever the codegen response named (may be null).\n *\n * `columns` MUST exclude measures. A place is a dimension; a measure that wandered into\n * this list can be adopted as the ZIP column on digit shape alone (see ZIP_THRESHOLD_PCT)\n * and would move every point on the map.\n *\n * A role is only ever REFUSED for a positive reason (the column is countries; the column\n * does not look like states at all) \u2014 never merely because the classifier is unsure. And\n * a refusal drops that ONE role, leaving the rest of the binding intact, because\n * geocoding from city alone still beats geocoding from nothing.\n */\nexport function resolvePointRoles(\n columns: string[],\n rows: Array<Record<string, any>>,\n hint: PointBind | null,\n): PointRoleResolution {\n const backfilled: string[] = [];\n const refused: string[] = [];\n const out: PointBind = {};\n const colSet = new Set(columns);\n const valuesOf = (name: string) => rows.map(r => r[name]);\n\n // ---- 1. Carry over the hint, VERIFYING the two roles that can be catastrophically\n // wrong. city/lat/lon are carried as given: a mis-hinted city degrades to \"no match\"\n // (a visible off-map count), whereas a mis-hinted STATE actively relocates points.\n for (const role of [\"city\", \"zip\", \"lat\", \"lon\"] as const) {\n const name = hint?.[role];\n if (name && colSet.has(name)) out[role] = name;\n }\n const hintedState = hint?.state;\n if (hintedState && colSet.has(hintedState)) {\n const vals = valuesOf(hintedState);\n if (looksLikeCountryColumn(vals)) {\n // The \"CA\" trap. Every value is a country, so this is a country column that\n // landed in the state slot. Using it would resolve \"CA\" to California and\n // drag a whole country's cities into one US state.\n refused.push(`state=${hintedState} (every value is a country, not a state)`);\n } else if (admin1MatchPct(vals) < THRESHOLD_PCT) {\n refused.push(`state=${hintedState} (only ${admin1MatchPct(vals)}% of values are states/provinces)`);\n } else {\n out.state = hintedState;\n }\n }\n\n // ---- 2. Backfill the roles nothing supplied. This is the real-world fix: the model\n // named only the city, so the state column sat unused beside it.\n const taken = new Set(Object.values(out).filter(Boolean) as string[]);\n const candidates = columns.filter(c => !taken.has(c) && !c.startsWith(\"__\"));\n\n if (!out.state) {\n let best: { name: string; pct: number } | null = null;\n for (const c of candidates) {\n const vals = valuesOf(c);\n if (looksLikeCountryColumn(vals)) continue; // never a state\n const pct = admin1MatchPct(vals);\n if (pct < THRESHOLD_PCT) continue;\n if (distinctMatches(vals, v => !!resolveAdmin1(v)) < MIN_DISTINCT_BACKFILL) continue;\n if (!best || pct > best.pct) best = { name: c, pct };\n }\n if (best) {\n out.state = best.name;\n taken.add(best.name);\n backfilled.push(`state=${best.name} (${best.pct}% states/provinces)`);\n }\n }\n\n if (!out.zip) {\n for (const c of candidates) {\n if (taken.has(c)) continue;\n const vals = valuesOf(c);\n const pct = zipMatchPct(vals);\n if (pct < ZIP_THRESHOLD_PCT) continue;\n if (distinctRawMatches(vals, v => zipPrefixCandidates(v).length > 0) < MIN_DISTINCT_BACKFILL) continue;\n out.zip = c;\n taken.add(c);\n backfilled.push(`zip=${c} (${pct}% ZIP codes)`);\n break;\n }\n }\n\n if (!out.city) {\n let best: { name: string; pct: number } | null = null;\n for (const c of candidates) {\n if (taken.has(c)) continue;\n const vals = valuesOf(c);\n const pct = cityPct(vals);\n if (pct < THRESHOLD_PCT) continue;\n if (distinctMatches(vals, v => isKnownCity(v)) < MIN_DISTINCT_BACKFILL) continue;\n if (!best || pct > best.pct) best = { name: c, pct };\n }\n if (best) {\n out.city = best.name;\n backfilled.push(`city=${best.name} (${best.pct}% known cities)`);\n }\n }\n\n const any = !!(out.city || out.state || out.zip || (out.lat && out.lon));\n return { bind: any ? out : null, backfilled, refused };\n}\n", "// The RenderData builder \u2014 the exact `data` object generated render(container,\n// data, options) expects, shared by the Power BI visual (buildD3DataPayload) and\n// the MCP client (dataPayload) so the wire shape can never fork (Phase B remainder\n//). Semantics moved VERBATIM from the visual:\n// { columns: [...cols, __rowIdx__ [, __geoIso__] ],\n// rows: [ [v0, \u2026, vN, rowIdx [, iso]], \u2026 ] } // positional; Date\u2192ISO; null-coalesced\n// The trailing __rowIdx__ is HOST METADATA: its value is the row's position, and it\n// is what a mark's data-row-idx attribute carries back to the host on click.\n// __geoIso__ (geo binding present) is the deterministic ISO/USPS/prefix join key\n// (shape-core buildGeoIsoColumn; unmatched \u2192 null) the code binds against feature.id.\n// Locked by chart-host/tests/payload.test.ts (oracle = the visual's original loop).\n// A real PACKAGE specifier, not a path out of this package. The relative form\n// (\"../../shape-core/src/\u2026\") is what made `npm pack` ship a tarball whose every import\n// failed, and no tsconfig alias could fix it: TypeScript does not route RELATIVE specifiers\n// through `paths`, so `tsc -b` failed where the bundler succeeded.\nimport {\n buildGeoIsoColumn, isJoinGeoKind, buildGeoPointColumns, resolvePointRoles, type GeoKind,\n} from \"@bicharts/shape-core\";\n// GeoPointPrecision comes from OUR contract, not shape-core: it appears in this module's\n// public return type, and shape-core is a build-time devDependency that consumers never\n// install \u2014 so importing the type here put a dangling specifier in the shipped payload.d.ts.\nimport type { GeoPointPrecision } from \"./contract\";\n\n/** Which columns hold the place parts a coordinate can be resolved from. Any subset;\n * the cascade uses the most precise tier available per row. */\nexport interface GeoPointBinding {\n city?: string;\n state?: string;\n zip?: string;\n lat?: string;\n lon?: string;\n}\n\nexport interface RenderPayload {\n columns: any[];\n rows: any[][];\n // Present only when a geo binding was given: feeds options.geoUnmatched\n // (\"N regions unmatched\"). undefined for non-geo payloads.\n geoUnmatched?: { count: number; examples: string[] };\n // Diagnostics companion: DISTINCT raw values that failed to resolve (examples is\n // capped at 8; this is the uncapped count the visual's geoIso-d3 log reports).\n geoUnmatchedDistinct?: number;\n // Present only when a POINT binding was given (city/state/zip -> coordinates).\n // Feeds options.geoPoint so the chart can annotate honestly: `precision` is the\n // COARSEST tier any row needed, `ambiguousRows` counts rows placed by the\n // largest-match tie-break on a colliding bare city name.\n geoPoint?: {\n precision: GeoPointPrecision | null;\n unplaced: number;\n unplacedExamples: string[];\n ambiguousRows: number;\n // What the ROLE resolution did to the caller's binding, so the chart can say when\n // its coordinates rest on something other than what was asked for. Both are\n // ADDITIVE and optional: a host on an older contract simply ignores them.\n // backfilled \u2014 roles the binding omitted, resolved from the data\n // (\"state=StateCode (100% states/provinces)\")\n // refused \u2014 roles the binding named that were rejected, with why\n // (\"state=Country (every value is a country, not a state)\")\n rolesBackfilled?: string[];\n rolesRefused?: string[];\n };\n}\n\nexport function buildRenderPayload(\n cols: Array<{ name: string } & Record<string, any>>,\n rowObjs: Array<Record<string, any>>,\n geo?: { column: string; kind: string } | null,\n point?: GeoPointBinding | null,\n): RenderPayload {\n const colNames = cols.map(c => c.name);\n\n // POINT geocoding: resolve each row to a coordinate from whatever place parts it has\n // (City+State -> ZIP -> State) and append __geoLat__/__geoLon__. This is what lets a\n // point map draw from city NAMES alone, with no lat/lon columns in the data. Skipped\n // entirely when the caller supplied no point binding, so non-map charts pay nothing.\n let pLat: (number | null)[] | null = null;\n let pLon: (number | null)[] | null = null;\n let geoPoint: RenderPayload[\"geoPoint\"];\n // The caller's binding is a HINT, not the last word. The codegen response names these\n // roles and can leave one out \u2014 a production point map named the city, not the state, so\n // City+State never engaged and 8 marks landed on the wrong same-named city. Verify\n // what was named and backfill what was not, from the values themselves.\n // Measures are excluded: a place is a dimension, and a 5-digit Revenue reads as a ZIP.\n let bind: GeoPointBinding | null = point ?? null;\n let rolesBackfilled: string[] = [];\n let rolesRefused: string[] = [];\n if (bind) {\n const dims = cols.filter(c => !c.isMeasure).map(c => c.name);\n const res = resolvePointRoles(dims, rowObjs, bind);\n bind = res.bind;\n rolesBackfilled = res.backfilled;\n rolesRefused = res.refused;\n }\n const hasPointBind = !!bind && !!(bind.city || bind.state || bind.zip || (bind.lat && bind.lon));\n if (hasPointBind) {\n const p = bind!;\n const built = buildGeoPointColumns(rowObjs.map(r => ({\n lat: p.lat ? r[p.lat] : null,\n lon: p.lon ? r[p.lon] : null,\n city: p.city ? r[p.city] : null,\n state: p.state ? r[p.state] : null,\n zip: p.zip ? r[p.zip] : null,\n })));\n pLat = built.lat; pLon = built.lon;\n geoPoint = {\n precision: built.precision,\n unplaced: built.totalRows - built.matchedRows,\n unplacedExamples: built.unmatched.slice(0, 8),\n ambiguousRows: built.ambiguousRows,\n // Omitted entirely when nothing was adjusted, so the common case adds no bytes\n // and a chart can treat presence as \"something about this binding is worth\n // saying\".\n ...(rolesBackfilled.length ? { rolesBackfilled } : {}),\n ...(rolesRefused.length ? { rolesRefused } : {}),\n };\n }\n\n // Geo choropleth: precompute the deterministic __geoIso__ join column (normal-\n // form ISO/USPS/FIPS via shape-core; unmatched \u2192 null) so each region <path>\n // binds a CODE, never a name\u2192code map the generated code invents. Only when the\n // server named a geo column + kind for this render.\n let geoIso: (string | null)[] | null = null;\n let geoUnmatched: { count: number; examples: string[] } | undefined;\n let geoUnmatchedDistinct: number | undefined;\n // isJoinGeoKind guard: \"city-name\" (and any future coordinate-feeding kind) is a\n // GeoKind but not a JOIN kind \u2014 building __geoIso__ from it nulls every row and\n // the choropleth draws blank with \"N regions unmatched\" = 100%. Skip instead: no\n // __geoIso__ appended \u2192 the archetype's guarded probe reports honestly.\n if (geo && geo.column && geo.kind && isJoinGeoKind(geo.kind)) {\n const built = buildGeoIsoColumn(rowObjs.map(r => r[geo.column]), geo.kind as GeoKind);\n geoIso = built.iso;\n geoUnmatched = { count: built.totalRows - built.matchedRows, examples: built.unmatched.slice(0, 8) };\n geoUnmatchedDistinct = built.unmatched.length;\n }\n\n // Project the row objects back into arrays in the column order to keep the wire\n // shape predictable for the LLM. DateTime cells come straight off the index;\n // coerce to ISO so the contract in PromptPreable holds. Trailing columns:\n // __rowIdx__ [+ __geoIso__].\n const extra = 1 + (geoIso ? 1 : 0) + (pLat ? 2 : 0);\n const out: any[][] = new Array(rowObjs.length);\n for (let r = 0; r < rowObjs.length; r++) {\n const row = rowObjs[r];\n const a = new Array(cols.length + extra);\n for (let c = 0; c < cols.length; c++) {\n const v = row[colNames[c]];\n if (v instanceof Date) {\n a[c] = v.toISOString();\n } else {\n a[c] = v ?? null;\n }\n }\n // Trailing `__rowIdx__` \u2014 its value IS the row's position, exactly the index\n // the host bridge expects in data-row-idx. __geoIso__ / __geoLat__ / __geoLon__\n // are the same class: host-computed metadata, not plottable dimensions.\n let k = cols.length;\n a[k++] = r;\n if (geoIso) a[k++] = geoIso[r];\n if (pLat && pLon) { a[k++] = pLat[r]; a[k++] = pLon[r]; }\n out[r] = a;\n }\n const colsOut = [...cols, { name: \"__rowIdx__\", dataType: \"Integer\", isMeasure: false, modelDesc: \"\" }];\n if (geoIso) colsOut.push({ name: \"__geoIso__\", dataType: \"String\", isMeasure: false, modelDesc: \"\" });\n if (pLat) colsOut.push(\n { name: \"__geoLat__\", dataType: \"Double\", isMeasure: false, modelDesc: \"\" },\n { name: \"__geoLon__\", dataType: \"Double\", isMeasure: false, modelDesc: \"\" });\n return { columns: colsOut as any[], rows: out, geoUnmatched, geoUnmatchedDistinct, geoPoint };\n}\n\n", "// The GENERIC half of the visual's D3 host-bridge click/hover mark resolution\n// (chart-host Phase C \u2014 moved VERBATIM from the Power BI visual, comments included;\n// the PBI half \u2014 selectionManager dispatch, panel-suppress flags, tooltips, glyphs \u2014\n// stays in the visual). Everything here is pure DOM computation over the contract's\n// mark grammar (.d3-mark/.d3-legend-mark/.d3-axis-filter + data-row-idx); the only\n// side channel is the injected diagnostic log callback, so both the visual and any\n// other host (createChartHost, tests) resolve clicks identically.\n//\n// Each function carries the prod lesson that created it \u2014 do not simplify.\n\nexport interface MarkResolverEnv {\n root: HTMLElement; // the chart container (hit scope)\n doc: Document; // owner document (elementsFromPoint)\n log: (tag: string, data: object) => void; // diagnostic sink (ctx.log in the visual)\n}\n\nexport interface MarkResolver {\n isInvisibleStrokePath(el: Element): boolean;\n distToPathCenterline(el: SVGPathElement, x: number, y: number): number;\n refineHitCorridor(mark: HTMLElement | null, x: number, y: number, sel: string): HTMLElement | null;\n findMark(target: EventTarget | null, sel?: string, ev?: MouseEvent): HTMLElement | null;\n rowIdxsFromMark(el: Element | null): number[];\n penetrateOverlayAt(x: number, y: number, sel: string): HTMLElement | null;\n resolveByGeometry(x: number, y: number, sel: string): HTMLElement | null;\n}\n\nexport function createMarkResolver(env: MarkResolverEnv): MarkResolver {\n const { root, doc, log } = env;\n\n // The visual can rely on the browser's GLOBAL `Element`; chart-host cannot. It runs\n // against foreign documents too \u2014 a jsdom sidecar, the server exec gate, a test that\n // builds its own JSDOM \u2014 where the global is absent or belongs to a DIFFERENT realm,\n // and `instanceof` across realms is false even for a real element. Resolve the\n // constructor from the document's own view, then duck-type as the last resort.\n const isElement = (t: any): boolean => {\n const E = (doc as any)?.defaultView?.Element ?? (globalThis as any).Element;\n if (E && t instanceof E) return true;\n return !!t && typeof t.closest === \"function\" && typeof t.getAttribute === \"function\";\n };\n const viewStyle = (n: Element): string => {\n try {\n const gcs = (doc as any)?.defaultView?.getComputedStyle\n ?? (typeof getComputedStyle === \"function\" ? getComputedStyle : null);\n return gcs ? gcs(n as any).pointerEvents : \"\";\n } catch { return \"\"; }\n };\n\n // Overlapping invisible hit corridors (prod: thin Yantian\u2192Vancouver arc vs the\n // fat Hong Kong corridor stacked on top \u2014 3 of 6 points along the thinnest arc\n // resolved to a DIFFERENT route). Disambiguate at event time: when the resolved\n // mark is an invisible stroke corridor, ask elementsFromPoint for the FULL hit\n // stack and pick the corridor whose CENTERLINE passes closest to the click \u2014\n // the most precise claim wins. Clicks away from any thin corridor see a\n // single-corridor stack and resolve exactly as before. Structural (no\n // chart-type list); line charts' same-width companions are unaffected (ties\n // keep the topmost).\n const isInvisibleStrokePath = (el: Element): boolean => {\n if (el.tagName.toLowerCase() !== \"path\") return false;\n const f = el.getAttribute(\"fill\");\n if (f !== null && f !== \"\" && f !== \"none\" && f !== \"transparent\") return false;\n const s = (el.getAttribute(\"stroke\") || \"\").trim().toLowerCase();\n const so = parseFloat(el.getAttribute(\"stroke-opacity\") || \"1\");\n return s === \"transparent\" || s === \"none\" || so <= 0.01;\n };\n\n // Screen-space distance from (x,y) to the path's CENTERLINE, by sampling.\n // 64 samples on a few-hundred-px arc \u2248 5-10px resolution \u2014 plenty to rank\n // \"which arc did the user aim at\" (corridors are \u226514px apart in the failing\n // cases). Click-time only; few candidates.\n const distToPathCenterline = (el: SVGPathElement, x: number, y: number): number => {\n const L = el.getTotalLength();\n if (!(L > 0)) return Number.MAX_VALUE;\n const m = el.getScreenCTM();\n if (!m) return Number.MAX_VALUE;\n let best = Number.MAX_VALUE;\n const N = 64;\n for (let i = 0; i <= N; i++) {\n const pt = el.getPointAtLength((L * i) / N);\n const dx = m.a * pt.x + m.c * pt.y + m.e - x;\n const dy = m.b * pt.x + m.d * pt.y + m.f - y;\n const d = dx * dx + dy * dy;\n if (d < best) best = d;\n }\n return Math.sqrt(best);\n };\n\n const refineHitCorridor = (mark: HTMLElement | null, x: number, y: number, sel: string): HTMLElement | null => {\n try {\n if (!mark || !isInvisibleStrokePath(mark) || !mark.getAttribute(\"data-row-idx\")) return mark;\n // Gather ALL competing invisible corridors at this point. With\n // only one, the plain resolution stands.\n const stack = doc.elementsFromPoint(x, y);\n const candidates: Element[] = [];\n for (const el of stack) {\n if (!root.contains(el)) continue;\n if (!el.matches || !el.matches(sel)) continue;\n if (!el.getAttribute(\"data-row-idx\")) continue;\n if (!isInvisibleStrokePath(el)) continue;\n candidates.push(el);\n }\n if (candidates.indexOf(mark) < 0) candidates.push(mark);\n if (candidates.length < 2) return mark;\n // The user aimed at a CENTERLINE, not a corridor: pick the\n // candidate whose path passes closest to the click point.\n // (Width ties \u2014 two overlapping min-width corridors \u2014 made a\n // width-based rule useless; distance is the real intent.)\n let best: Element = mark;\n let bestD = Number.MAX_VALUE;\n for (const el of candidates) {\n const d = distToPathCenterline(el as SVGPathElement, x, y);\n if (d < bestD) { bestD = d; best = el; }\n }\n if (best !== mark) {\n log(\"d3-hit-corridor-refined\", {\n candidates: candidates.length, distPx: Math.round(bestD),\n fromRows: (mark.getAttribute(\"data-row-idx\") || \"\").slice(0, 24),\n toRows: (best.getAttribute(\"data-row-idx\") || \"\").slice(0, 24),\n });\n }\n return best as HTMLElement;\n } catch { return mark; }\n };\n\n const findMark = (target: EventTarget | null, sel: string = \".d3-mark\", ev?: MouseEvent): HTMLElement | null => {\n if (!isElement(target)) return null;\n const el = target as unknown as Element;\n // closest() crosses SVG boundaries \u2014 works for both <rect\n // class=\"d3-mark\"> and <g class=\"d3-mark\"><rect/></g>.\n const m = el.closest(sel) as HTMLElement | SVGElement | null;\n // With event coordinates available, resolve overlapping invisible\n // hit corridors to the narrowest one containing the point.\n if (m && ev) return refineHitCorridor(m as HTMLElement, ev.clientX, ev.clientY, sel);\n return m as HTMLElement | null;\n };\n\n const rowIdxsFromMark = (el: Element | null): number[] => {\n if (!el) return [];\n const raw = el.getAttribute(\"data-row-idx\");\n if (!raw) return [];\n const out: number[] = [];\n for (const tok of raw.split(\",\")) {\n const n = parseInt(tok.trim(), 10);\n if (!isNaN(n) && n >= 0) out.push(n);\n }\n return out;\n };\n\n // Overlay penetration (2026-06-23): a full-canvas background / spacer rect\n // with a PAINTABLE fill (fill:'transparent' is NOT fill:'none') and default\n // pointer-events, appended ON TOP of the marks, swallows the click \u2014 e.target is\n // then the inert overlay, findMark finds no mark, and cross-filter silently dies\n // (a production route-yield heatmap: clicking cells AND axis labels did nothing).\n // Walk the hit-stack at the point and take the first real mark UNDERNEATH the\n // overlay. This runs ONLY on the branch that already does nothing, so it never\n // alters a click that already resolved to a mark; a genuinely empty click (no\n // mark under the point) still resolves to null and no-ops.\n const penetrateOverlayAt = (x: number, y: number, sel: string): HTMLElement | null => {\n try {\n const stack = doc.elementsFromPoint(x, y);\n for (const el of stack) {\n if (!root.contains(el) || !isElement(el)) continue;\n // The stacked element may BE the mark, or be an inner decoration\n // WITHIN one (a HITTABLE bar / cell shape inside a `.d3-mark`\n // group). elementsFromPoint returns hit-testable leaf shapes but\n // NOT their <g> ancestors, so resolve UP via closest() to the\n // enclosing mark. NOTE: elementsFromPoint HONOURS\n // pointer-events:none (it omits those elements), so this only\n // recovers a mark whose inner shapes are pointer-events-ENABLED; a\n // row whose children are ALL pointer-events:none is invisible here\n // and is handled by the GEOMETRIC fallback. (closest() crosses SVG\n // boundaries and only runs on the already-no-mark branch, so it\n // never changes a click that resolved normally.)\n const cand = (el.matches(sel) ? el : el.closest(sel)) as Element | null;\n if (!cand || !root.contains(cand) || !cand.getAttribute(\"data-row-idx\")) continue;\n log(\"d3_click-overlay-penetrated\", { tag: el.tagName, cls: el.getAttribute(\"class\"), via: el === cand ? \"self\" : \"closest\" });\n return cand as unknown as HTMLElement;\n }\n } catch { /* elementsFromPoint unsupported \u2014 fall through to no-op */ }\n return null;\n };\n\n // GEOMETRIC last resort (2026-06-29, a production Tabular-with-embedded chart\n // row-select dead): when a `.d3-mark` ROW and ALL its children are\n // pointer-events:none with NO enabled hit surface, the row is\n // hit-test-INVISIBLE \u2014 elementFromPoint returns whatever is BEHIND it and\n // elementsFromPoint OMITS it (both honour pointer-events:none), so neither\n // findMark nor the closest() stack walk can recover it. Resolve by pure\n // GEOMETRY: getBoundingClientRect ignores pointer-events, so hit-test the\n // click against every mark box and take the SMALLEST (most specific) one\n // containing the point. Runs ONLY after every pointer-events path already\n // failed, so it never alters a click that resolved normally.\n const resolveByGeometry = (x: number, y: number, sel: string): HTMLElement | null => {\n try {\n const cands = root.querySelectorAll<SVGElement | HTMLElement>(sel);\n // Cap: this is for table-like (small-N) row charts; a chart with\n // thousands of marks isn't the all-none pathology and we don't want\n // a per-empty-click O(n) rect sweep.\n let best: Element | null = null, bestArea = Number.MAX_VALUE;\n if (cands.length <= 600) {\n for (const el of Array.from(cands)) {\n if (!el.getAttribute(\"data-row-idx\")) continue;\n const r = (el as any).getBoundingClientRect ? el.getBoundingClientRect() : null;\n if (!r || r.width <= 0 || r.height <= 0) continue;\n if (x < r.left || x > r.right || y < r.top || y > r.bottom) continue;\n const area = r.width * r.height;\n if (area < bestArea) { bestArea = area; best = el; }\n }\n }\n // SCOPING GUARD \u2014 only resolve a mark that is genuinely UNCLICKABLE\n // (no painted descendant with pointer-events enabled). That is exactly\n // the pathology: a row whose every shape is pointer-events:none. If the\n // mark HAS a hittable shape then a real click on it would already have\n // resolved above, so being here means the click hit a GAP inside the\n // mark's box \u2014 treat as empty (let the panel toggle), do NOT hijack it.\n // Keeps the fallback inert for every normally-hittable D3 chart.\n const hasHittableShape = (node: Element): boolean => {\n const SHAPE = /^(rect|circle|ellipse|path|line|polygon|polyline|text|image|use)$/;\n const all = [node, ...Array.from(node.querySelectorAll(\"*\"))];\n for (const n of all) {\n const tag = (n.tagName || \"\").toLowerCase();\n if (!SHAPE.test(tag)) continue;\n const pe = viewStyle(n);\n if (pe && pe !== \"none\") return true;\n }\n return false;\n };\n if (best && !hasHittableShape(best)) {\n log(\"d3_click-geom-resolved\", { rows: (best.getAttribute(\"data-row-idx\") || \"\").slice(0, 24) });\n return best as unknown as HTMLElement;\n } else if (best) {\n log(\"d3_click-geom-skip-hittable\", {});\n }\n } catch { /* getBoundingClientRect / getComputedStyle unsupported \u2014 fall through */ }\n return null;\n };\n\n return { isInvisibleStrokePath, distToPathCenterline, refineHitCorridor, findMark, rowIdxsFromMark, penetrateOverlayAt, resolveByGeometry };\n}\n", "// createChartHost \u2014 the host-side RUNTIME of the contract (Phase C, first cut).\n//\n// A minimal, host-agnostic implementation of everything a page must do to run a\n// generated render(container, data, options) chart: compile the code the same way\n// the visual/preview/exec-gate do, resolve options through the shared defaults,\n// wire the cross-filter bridge (mark clicks + the llm-xfilter-refresh event), honor\n// the container lifecycle slots, and re-render on setOptions/setData \u2014 which is how\n// the live-restyle knobs (colorScaleLow/High, aggregation, animMaxIdealFrames) work.\n//\n// The Power BI visual does NOT use this runtime (its own bridge carries years of\n// PBI-specific behavior: selectionManager, host tooltips, glyphs, geometry hit\n// disambiguation); this is for OTHER hosts \u2014 the MCP preview, an SDK page, tests.\n// Delegating the visual's generic half onto shared selection logic remains the\n// harness-gated last step of Phase C.\n//\n// Animation scope (v1): the contract exposes only __llmAnimStop (stop) \u2014 playback\n// and frame stepping are chart-owned (the play button + scrubber the chart draws).\n// An imperative play()/goTo() would need NEW contract slots on the archetypes\n// (contract v1.1 candidate, replay-gated) \u2014 deliberately not invented here.\nimport { type RenderOptions, ROW_IDX_ATTR, MARK_CLASS, LEGEND_MARK_CLASS, AXIS_FILTER_CLASS,\n XFILTER_REFRESH_EVENT, CONTAINER_SLOT_ANIM_STOP, CONTAINER_SLOT_XF_CLEAR,\n CONTAINER_SLOT_INITIAL_XF_MARK, HOST_CONTAINER_CLASS, SELECTION_ACTIVE_CLASS,\n MARK_SELECTED_CLASS, DIM_OPACITY_VAR, DIM_OPACITY_DEFAULT,\n HOST_CONTRACT_VERSION } from \"./contract\";\nimport { resolveOptions, type ResolveOptionsInput } from \"./defaults\";\n// Deliberately NOT \"./geo\": that module statically imports ~1.3 MB of generated geometry,\n// which every consumer then paid for even to draw a bar chart (GAP-11). geoLazy holds the\n// cache and the dynamic loader but no asset, so the runtime entry stays lean.\nimport { geoFromCache } from \"./geoLazy\";\nimport { createMarkResolver } from \"./selection\";\n\nexport type RenderFn = (container: HTMLElement, data: any, options: RenderOptions) => void;\n\nexport interface ChartHostConfig {\n // The payload as buildRenderPayload / the MCP data.sample.json produce it. geoUnmatched\n // and geoPoint ride ALONGSIDE the table because they are facts ABOUT the binding, and\n // render() auto-promotes them into options \u2014 a host that just hands over the whole file\n // gets honest annotations without knowing they exist.\n data: {\n columns: any[];\n rows: any[][];\n geoUnmatched?: { count: number; examples: string[] };\n geoPoint?: RenderOptions[\"geoPoint\"];\n /** Provenance the MCP stamps into data.sample.json. `hostContract` is checked\n * against HOST_CONTRACT_VERSION so a major-version drift is LOUD, not silent. */\n meta?: { hostContract?: string; chart?: string };\n };\n /** Generated render() source \u2014 compiled exactly like the visual/preview/exec-gate. */\n code?: string;\n /** Pre-compiled alternative to `code`. */\n renderFn?: RenderFn;\n /** Partial raw options; resolveOptions applies the shared defaults/clamps. */\n options?: ResolveOptionsInput;\n /** D3 v7 instance handed to the compiled code. Default: (globalThis as any).d3. */\n d3?: any;\n /** Attach geometry for this geoKind as options.geo (choropleths / basemaps). Resolved\n * from `geoProvider` or the geoLazy cache \u2014 see `loadGeo`. NOT eagerly bundled. */\n geoKind?: string;\n /** Supply geometry for a geoKind synchronously. The escape hatch for a host that\n * already holds its assets (e.g. `geoForKind` from \"@bicharts/chart-host/geo\", or the\n * data.geo.json the MCP wrote next to the chart). */\n geoProvider?: (geoKind: string) => any | undefined;\n}\n\nexport interface ChartHost {\n render(): void;\n /** Merge raw option changes and re-render \u2014 the live-restyle path (colour scale,\n * aggregation, animMaxIdealFrames, timeline style\u2026). Never a regeneration. */\n setOptions(partial: ResolveOptionsInput): void;\n setData(data: ChartHostConfig[\"data\"]): void;\n readonly options: RenderOptions;\n selection: {\n /** Subscribe to cross-filter changes (mark clicks + the chart's own\n * llm-xfilter-refresh dispatches). rowIdxs = [] means cleared. */\n onChange(cb: (rowIdxs: number[], source: string) => void): () => void;\n /** Clear the chart's own selection (calls __llmXfClear when the chart set it). */\n clear(): void;\n readonly current: number[] | null;\n };\n animation: {\n /** Stop playback (calls __llmAnimStop when set). Call before unmount. */\n stop(): void;\n };\n /** The mark the chart nominated as its initial filter (__llmInitialXfMark), if any. */\n initialMark(): Element | null;\n destroy(): void;\n}\n\nconst parseRowIdxs = (s: string | null | undefined): number[] =>\n (s || \"\").split(\",\").map(x => parseInt(x, 10)).filter(n => Number.isFinite(n));\n\n// d3 plugins are separate packages that ATTACH onto the d3 object. When one is missing the\n// generated code fails as `d3.sankey is not a function` several frames deep inside compiled\n// chart source \u2014 a message that tells a host nothing about what to install. Name the cause\n// and the way forward instead; the package list matches what the Power BI visual loads, so\n// a chart behaves the same in both hosts.\nconst D3_PLUGIN_PACKAGES: Record<string, string> = {\n sankey: \"d3-sankey\", sankeyLinkHorizontal: \"d3-sankey\", sankeyJustify: \"d3-sankey\",\n sankeyCenter: \"d3-sankey\", sankeyLeft: \"d3-sankey\", sankeyRight: \"d3-sankey\",\n hexbin: \"d3-hexbin\",\n voronoiTreemap: \"d3-voronoi-treemap\", voronoiMap: \"d3-voronoi-map\",\n weightedVoronoi: \"d3-weighted-voronoi\",\n};\n\nexport function explainRenderFailure(err: unknown, d3: any): unknown {\n const msg = String((err as any)?.message ?? err ?? \"\");\n if (!d3) {\n return new Error(\n \"[@bicharts/chart-host] no d3 was provided, so the generated chart could not run. \" +\n \"Pass it explicitly \u2014 createChartHost(el, { d3 }) / <BicChart d3={d3} /> \u2014 or set \" +\n \"window.d3. The chart needs D3 v7 (npm install d3@7). Original error: \" + msg);\n }\n const m = /d3\\.(\\w+) is not a function|(\\w+) is not a function/.exec(msg);\n const name = m?.[1] ?? m?.[2] ?? \"\";\n const pkg = D3_PLUGIN_PACKAGES[name];\n if (pkg) {\n return new Error(\n `[@bicharts/chart-host] this chart calls d3.${name}(), which comes from the separate ` +\n `\"${pkg}\" package and is not attached to the d3 you passed. Install it and augment ` +\n `the SAME d3 instance: npm install ${pkg} then import { ${name} } from \"${pkg}\"; ` +\n `Object.assign(d3, { ${name} }); Original error: ` + msg);\n }\n return err;\n}\n\nexport function compileRenderFn(code: string, win: any, doc: any, d3: any): RenderFn {\n // Same compile the preview/exec-gate use: the code must define a top-level render().\n const factory = new Function(\"container\", \"data\", \"options\", \"window\", \"document\", \"d3\",\n code + \"\\n;return (typeof render === 'function') ? render : null;\");\n const fn = factory.call(null, null, null, null, win, doc, d3);\n if (typeof fn !== \"function\") throw new Error(\"generated code did not define render(container, data, options)\");\n return fn as RenderFn;\n}\n\nexport function createChartHost(container: HTMLElement, config: ChartHostConfig): ChartHost {\n const doc: any = container.ownerDocument;\n const win: any = doc?.defaultView ?? (globalThis as any);\n const d3 = config.d3 ?? (globalThis as any).d3 ?? win?.d3;\n let data = config.data;\n // A generated chart is COMMITTED SOURCE that outlives the tool that wrote it, so a host\n // on a different major version of the grammar is a real possibility. Warn once, loudly\n // and specifically \u2014 a renamed mark class fails as \"nothing is clickable\", which is\n // exactly the kind of silent breakage nobody traces back to a version.\n {\n const stamped = config.data?.meta?.hostContract;\n const major = (v?: string) => (v || \"\").split(\".\")[0];\n if (stamped && major(stamped) !== major(HOST_CONTRACT_VERSION)) {\n try {\n (win?.console ?? console)?.warn(\n `[@bicharts/chart-host] contract MAJOR mismatch: this chart was generated against ` +\n `v${stamped}, the host implements v${HOST_CONTRACT_VERSION}. Marks, container ` +\n `slots or the cross-filter event may not line up \u2014 regenerate the chart.`);\n } catch { /* no console \u2014 the check is advisory, never fatal */ }\n }\n }\n let raw: ResolveOptionsInput = { ...(config.options ?? {}) };\n let resolved = resolveOptions(raw);\n let renderFn: RenderFn | null = config.renderFn ?? null;\n let destroyed = false;\n let warnedGeo = false;\n let current: number[] | null = null;\n const subs = new Set<(rowIdxs: number[], source: string) => void>();\n\n // ---- Selection affordance ------------------------------------------------\n // Inject the dim/hover rules ONCE per document, scoped to hosted containers, using\n // the same class grammar the Power BI visual uses. Without this a click cross-filters\n // other charts but the clicked chart itself gives no feedback \u2014 the \"I clicked a\n // legend swatch and nothing happened\" report from the React demo.\n const ensureAffordanceStyles = () => {\n try {\n if (!doc || doc.getElementById(\"bic-chart-host-affordances\")) return;\n const st = doc.createElement(\"style\");\n st.id = \"bic-chart-host-affordances\";\n st.textContent = `\n/* TYPOGRAPHY FIREWALL. Generated charts set font-size but almost never line-height or\n letter-spacing, so they INHERIT whatever the host page cascades in. A page with\n \\`:root { font: 18px/145% }\\` (the stock Vite template does exactly this) hands every\n chart an inherited 26.1px line box; an 11px label in a 14px band is then sliced through\n the middle. The chart is not wrong and the host is not wrong \u2014 the boundary between\n them is, so the boundary fixes it. Both properties are overridable: a chart that sets\n its own line-height still wins. Deliberately NOT resetting font-family or color, which\n a host SHOULD be able to theme. Found 2026-07-26 in the React demo, on a chart that\n renders perfectly inside Power BI. */\n.${HOST_CONTAINER_CLASS} { line-height: normal; letter-spacing: normal; }\n.${HOST_CONTAINER_CLASS} .${MARK_CLASS} { cursor: pointer; transition: opacity 120ms ease-in-out, filter 100ms ease-in-out; }\n.${HOST_CONTAINER_CLASS} .${MARK_CLASS}:hover { filter: brightness(1.1) drop-shadow(0 0 1.5px rgba(0,0,0,0.5)) !important; }\n.${HOST_CONTAINER_CLASS}:has(.${MARK_CLASS}:hover) .${MARK_CLASS}:not(:hover) { opacity: 0.55; }\n.${HOST_CONTAINER_CLASS}.${SELECTION_ACTIVE_CLASS} .${MARK_CLASS}:not(.${MARK_SELECTED_CLASS}) {\n opacity: var(${DIM_OPACITY_VAR}, ${DIM_OPACITY_DEFAULT}) !important;\n}\n.${HOST_CONTAINER_CLASS} .${LEGEND_MARK_CLASS} { cursor: pointer; }`;\n (doc.head || doc.documentElement)?.appendChild(st);\n } catch { /* no document (jsdom edge) \u2014 affordance is cosmetic, never fatal */ }\n };\n\n // Paint the current selection onto the marks. Re-run after EVERY render, because\n // the chart rebuilds its DOM and the classes would otherwise be lost on repaint.\n const paintSelection = () => {\n try {\n const sel = current;\n const active = !!sel && sel.length > 0;\n container.classList?.[active ? \"add\" : \"remove\"](SELECTION_ACTIVE_CLASS);\n const wanted = new Set(sel ?? []);\n const marks = container.querySelectorAll(`.${MARK_CLASS}[${ROW_IDX_ATTR}], .${LEGEND_MARK_CLASS}[${ROW_IDX_ATTR}]`);\n for (const m of Array.from(marks)) {\n // A mark represents SEVERAL rows (a legend swatch, an aggregated slice);\n // it counts as selected when ANY of its rows is in the selection, which is\n // what makes a legend click light up its own swatch and its marks together.\n const rows = parseRowIdxs(m.getAttribute(ROW_IDX_ATTR));\n const on = active && rows.some(r => wanted.has(r));\n m.classList?.[on ? \"add\" : \"remove\"](MARK_SELECTED_CLASS);\n }\n } catch { /* cosmetic */ }\n };\n\n const notify = (rowIdxs: number[], source: string) => {\n current = rowIdxs;\n paintSelection();\n for (const cb of subs) { try { cb(rowIdxs, source); } catch { /* subscriber error is not ours */ } }\n };\n\n // The chart's own dispatch (scrubber ticks, period changes) \u2014 the primary\n // mechanism for animated charts. detail = {clear,source} | {mark,source}.\n // xfAt timestamps it so the click handler can tell \"the chart already handled this\n // gesture\" from \"a static chart marked an axis label and dispatched nothing\".\n let xfAt = 0;\n const XF_ECHO_MS = 150;\n const onXf = (e: any) => {\n const d = e?.detail || {};\n xfAt = Date.now();\n if (d.clear) notify([], d.source || \"chart\");\n else if (d.mark) notify(parseRowIdxs(d.mark.getAttribute?.(ROW_IDX_ATTR)), d.source || \"chart\");\n };\n // Plain mark clicks (static charts + choropleth regions): the chart does NOT\n // dispatch the event for these \u2014 the host reads data-row-idx itself.\n //\n // The selector MATCHES THE VISUAL's (`.d3-mark, .d3-legend-mark, .d3-axis-filter`).\n // .d3-axis-filter used to be excluded here on the assumption that a chart owning an\n // axis-tick click always dispatches llm-xfilter-refresh \u2014 true for ANIMATED scrubber\n // ticks, false for the static charts that label a category axis `.d3-axis-filter` and\n // dispatch nothing (measured 2026-07-26 on a cold-generated grouped bar: the class and\n // data-row-idx are emitted, no dispatch anywhere in the file). Clicking a category\n // label therefore did nothing in an SDK host while working in Power BI. `xfJustFired`\n // below keeps the animated case from double-firing.\n const resolver = createMarkResolver({\n root: container, doc,\n log: () => { /* diagnostics are the visual's concern; the resolution logic is shared */ },\n });\n const clickSel = `.${MARK_CLASS}[${ROW_IDX_ATTR}], .${LEGEND_MARK_CLASS}[${ROW_IDX_ATTR}], .${AXIS_FILTER_CLASS}[${ROW_IDX_ATTR}]`;\n const onClick = (e: any) => {\n // A chart that dispatched for THIS gesture has already been handled by onXf, which\n // runs first (the chart's own handler sits on the tick; this one on the container).\n if (Date.now() - xfAt < XF_ECHO_MS) return;\n let el: any = resolver.findMark(e?.target, clickSel, e as MouseEvent);\n // OVERLAY PENETRATION then GEOMETRY \u2014 the two hardenings the visual has carried\n // since two production charts exposed it, already ported into selection.ts but never wired up here.\n // Without them a tooltip/crosshair overlay painted OVER the marks (fill:'transparent'\n // is a painted fill, so it hit-tests) makes every mark click read as \"empty canvas\"\n // and the chart silently cannot cross-filter \u2014 which is exactly what a cold consumer\n // hit on 2026-07-26, then worked around with their own elementsFromPoint hack.\n // Both run ONLY on the branch that already resolved nothing, so neither can alter a\n // click that resolved normally.\n if (!el) el = resolver.penetrateOverlayAt(e?.clientX, e?.clientY, clickSel);\n if (!el) el = resolver.resolveByGeometry(e?.clientX, e?.clientY, clickSel);\n if (!el) {\n // Click on empty canvas CLEARS, the way every BI tool behaves. Without this\n // a selection is a one-way door: the React demo could filter to one city and\n // had no gesture to get back (\"how can I go back to nothing selected?\").\n if (current && current.length) notify([], \"user\");\n return;\n }\n const rows = parseRowIdxs(el.getAttribute(ROW_IDX_ATTR));\n if (!rows.length) return;\n\n // MULTI-SELECT (Ctrl / Cmd / Shift), matching the Power BI visual.\n // Without this a chart that grows a selection fine inside Power BI silently cannot\n // outside it \u2014 a parity gap in the one thing this package exists to guarantee.\n //\n // The semantics are TOGGLE-PER-ROW, not union. That distinction is the whole bug the\n // visual's own comment warns about: \"a naive union would leave previously-selected\n // marks lit up after the user Ctrl-clicked them off.\" Ctrl-clicking a selected mark\n // must REMOVE it, and a mark spanning several rows (a legend swatch) toggles each of\n // its rows independently \u2014 which is what makes Ctrl-clicking a legend swatch off\n // leave the other series still selected.\n if (e?.ctrlKey || e?.metaKey || e?.shiftKey) {\n const next = new Set(current ?? []);\n for (const r of rows) {\n if (next.has(r)) next.delete(r);\n else next.add(r);\n }\n // Toggling the last row off is a legitimate way to reach empty; notify([]) is\n // the clear, so a modifier-click can undo a selection without hunting for canvas.\n notify(Array.from(next), \"user\");\n return;\n }\n\n // Re-clicking the SAME mark toggles it off \u2014 the other half of that gesture. Only\n // on the non-modifier path: with a modifier, per-row toggling already covers it, and\n // running both is the dual-owner drift the visual hit (it clears lastMarkClickSig\n // when isMulti for exactly this reason).\n const same = !!current && current.length === rows.length && rows.every(r => current!.includes(r));\n notify(same ? [] : rows, \"user\");\n };\n container.addEventListener(XFILTER_REFRESH_EVENT, onXf);\n container.addEventListener(\"click\", onClick);\n container.classList?.add(HOST_CONTAINER_CLASS);\n ensureAffordanceStyles();\n\n const stopAnim = () => { const s = (container as any)[CONTAINER_SLOT_ANIM_STOP]; if (typeof s === \"function\") { try { s(); } catch { /* chart timer already dead */ } } };\n\n const host: ChartHost = {\n render() {\n if (destroyed) return;\n stopAnim(); // contract: stop the old timer before repaint\n container.innerHTML = \"\"; // the host owns clearing between renders\n if (!renderFn) {\n if (!config.code) throw new Error(\"createChartHost: provide code or renderFn\");\n renderFn = compileRenderFn(config.code, win, doc, d3);\n }\n // Attach geometry + unmatched when configured and not explicitly supplied.\n // render() is SYNCHRONOUS by contract, so geoKind resolves against whatever the\n // caller has already made available \u2014 an explicit geoProvider, or the cache that\n // `await loadGeo(kind)` / registerGeo() fills. A caller who sets geoKind without\n // doing either gets no geometry, which is why that case warns rather than drawing\n // a mysteriously empty map.\n if (resolved.geo === undefined && config.geoKind) {\n const g = config.geoProvider?.(config.geoKind) ?? geoFromCache(config.geoKind);\n if (g) resolved = { ...resolved, geo: g };\n else if (!warnedGeo) {\n warnedGeo = true;\n try {\n (win?.console ?? console)?.warn(\n `[@bicharts/chart-host] geoKind \"${config.geoKind}\" was set but no geometry is ` +\n `available, so this chart will draw without a basemap. Either ` +\n `\\`await loadGeo(\"${config.geoKind}\")\\` before rendering, pass a geoProvider, ` +\n `or import { geoForKind } from \"@bicharts/chart-host/geo\" and set options.geo.`);\n } catch { /* advisory only */ }\n }\n }\n if (resolved.geoUnmatched === undefined && data.geoUnmatched) resolved = { ...resolved, geoUnmatched: data.geoUnmatched };\n // Same promotion for point maps: without it the chart cannot say which rows it\n // could not place, or that a \"city\" dot is really a state centroid.\n if (resolved.geoPoint === undefined && data.geoPoint) resolved = { ...resolved, geoPoint: data.geoPoint };\n try {\n renderFn(container, { columns: data.columns, rows: data.rows }, resolved);\n } catch (err) {\n throw explainRenderFailure(err, d3);\n }\n // The chart just rebuilt its DOM, so the selection classes are gone. Repaint\n // them or a cross-filtered chart loses its own highlight on every restyle.\n paintSelection();\n },\n setOptions(partial) {\n raw = { ...raw, ...partial };\n resolved = resolveOptions(raw);\n host.render();\n },\n setData(next) {\n data = next;\n host.render();\n },\n get options() { return resolved; },\n selection: {\n onChange(cb) { subs.add(cb); return () => subs.delete(cb); },\n clear() {\n const s = (container as any)[CONTAINER_SLOT_XF_CLEAR];\n if (typeof s === \"function\") { try { s(); } catch { /* chart already clear */ } }\n // Always settle the host's own state, even when the chart owns the clear:\n // a chart that clears WITHOUT dispatching would otherwise leave the host\n // (and every subscriber) believing the old selection is still live.\n notify([], \"host\");\n },\n get current() { return current; },\n },\n animation: { stop: stopAnim },\n initialMark() { return ((container as any)[CONTAINER_SLOT_INITIAL_XF_MARK] as Element) ?? null; },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n stopAnim();\n container.removeEventListener(XFILTER_REFRESH_EVENT, onXf);\n container.removeEventListener(\"click\", onClick);\n subs.clear();\n container.innerHTML = \"\";\n },\n };\n return host;\n}\n\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|