SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
6 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
16.5 KB · 312 lines typescript
Raw Blame History
1import type { GeoPrecision } from "@dci/core";23/**4 * Curated city → coordinates table for the cloud / hyperscaler connectors.5 *6 * Coordinates are CITY-level markers (town centre, ±a few km), never facility locations. Entries with7 * `precision: "metro"` or `"approximate"` are wider areas (a metro, a county, a state region) that a8 * provider names instead of a city; they are flagged as such so the UI never claims exactness.9 * Providers only ever publish the city/area, so nothing here can be more precise than the city.10 */11export interface CityEntry {12  /** canonical display name */13  city: string;14  iso: string;15  lat: number;16  lng: number;17  /** state / province / region (display) */18  region?: string;19  precision?: GeoPrecision;20  aliases?: string[];21}2223const C = (city: string, iso: string, lat: number, lng: number, region?: string, aliases?: string[], precision?: GeoPrecision): CityEntry => ({ city, iso, lat, lng, region, aliases, precision });2425export const CITIES: CityEntry[] = [26  // ---- North America (US) ----27  C("Ashburn", "US", 39.04, -77.49, "Virginia"),28  C("Northern Virginia", "US", 39.04, -77.49, "Virginia", ["N. Virginia", "Virginia"], "metro"),29  C("Loudoun County", "US", 39.09, -77.64, "Virginia", ["Loundoun County"], "approximate"),30  C("Prince William County", "US", 38.7, -77.48, "Virginia", undefined, "approximate"),31  C("Chesterfield County", "US", 37.38, -77.59, "Virginia", undefined, "approximate"),32  C("Henrico", "US", 37.55, -77.4, "Virginia", ["Henrico County"], "approximate"),33  C("Boydton", "US", 36.67, -78.39, "Virginia"),34  C("Columbus", "US", 39.96, -83.0, "Ohio"),35  C("New Albany", "US", 40.08, -82.81, "Ohio"),36  C("Lancaster", "US", 39.71, -82.6, "Ohio"),37  C("Bowling Green", "US", 41.37, -83.65, "Ohio"),38  C("Chicago", "US", 41.88, -87.63, "Illinois"),39  C("DeKalb", "US", 41.93, -88.75, "Illinois"),40  C("Dallas", "US", 32.78, -96.8, "Texas"),41  C("Fort Worth", "US", 32.75, -97.33, "Texas"),42  C("Houston", "US", 29.76, -95.37, "Texas"),43  C("San Antonio", "US", 29.42, -98.49, "Texas"),44  C("El Paso", "US", 31.76, -106.49, "Texas"),45  C("Temple", "US", 31.1, -97.34, "Texas"),46  C("Midlothian", "US", 32.48, -97.0, "Texas"),47  C("Red Oak", "US", 32.52, -96.8, "Texas"),48  C("Pampa", "US", 35.54, -100.96, "Texas"),49  C("Armstrong County", "US", 34.97, -101.36, "Texas", undefined, "approximate"),50  C("Haskell County", "US", 33.18, -99.73, "Texas", undefined, "approximate"),51  C("Wilbarger County", "US", 34.08, -99.24, "Texas", undefined, "approximate"),52  C("Phoenix", "US", 33.45, -112.07, "Arizona"),53  C("Mesa", "US", 33.42, -111.83, "Arizona"),54  C("Goodyear", "US", 33.44, -112.36, "Arizona"),55  C("Los Angeles", "US", 34.05, -118.24, "California"),56  C("San Jose", "US", 37.34, -121.89, "California"),57  C("Silicon Valley", "US", 37.39, -121.96, "California", undefined, "metro"),58  C("Santa Clara", "US", 37.35, -121.96, "California"),59  C("Las Vegas", "US", 36.17, -115.14, "Nevada"),60  C("Henderson", "US", 36.04, -114.98, "Nevada"),61  C("Storey County", "US", 39.45, -119.53, "Nevada", undefined, "approximate"),62  C("Salt Lake City", "US", 40.76, -111.89, "Utah"),63  C("Eagle Mountain", "US", 40.31, -112.01, "Utah"),64  C("Denver", "US", 39.74, -104.99, "Colorado"),65  C("Seattle", "US", 47.61, -122.33, "Washington"),66  C("Quincy", "US", 47.23, -119.85, "Washington"),67  C("Portland", "US", 45.52, -122.68, "Oregon"),68  C("The Dalles", "US", 45.59, -121.18, "Oregon"),69  C("Prineville", "US", 44.3, -120.83, "Oregon"),70  C("Boardman", "US", 45.84, -119.7, "Oregon"),71  C("Honolulu", "US", 21.31, -157.86, "Hawaii"),72  C("Minneapolis", "US", 44.98, -93.27, "Minnesota"),73  C("Rosemount", "US", 44.74, -93.13, "Minnesota"),74  C("Hermantown", "US", 46.81, -92.24, "Minnesota"),75  C("Pine Island", "US", 44.2, -92.65, "Minnesota"),76  C("Kansas City", "US", 39.1, -94.58, "Missouri"),77  C("New Florence", "US", 38.91, -91.45, "Missouri"),78  C("Council Bluffs", "US", 41.26, -95.86, "Iowa"),79  C("Cedar Rapids", "US", 41.98, -91.67, "Iowa"),80  C("Altoona", "US", 41.64, -93.47, "Iowa"),81  C("Des Moines", "US", 41.59, -93.62, "Iowa"),82  C("West Des Moines", "US", 41.58, -93.71, "Iowa"),83  C("Omaha", "US", 41.26, -95.94, "Nebraska"),84  C("Papillion", "US", 41.15, -96.04, "Nebraska"),85  C("Sarpy", "US", 41.11, -96.11, "Nebraska", ["Sarpy County"], "approximate"),86  C("Lincoln", "US", 40.81, -96.68, "Nebraska"),87  C("Cheyenne", "US", 41.14, -104.82, "Wyoming"),88  C("Atlanta", "US", 33.75, -84.39, "Georgia"),89  C("Douglas County", "US", 33.7, -84.77, "Georgia", undefined, "approximate"),90  C("Columbia County", "US", 33.55, -82.26, "Georgia", undefined, "approximate"),91  C("LaGrange", "US", 33.04, -85.03, "Georgia"),92  C("Stanton Springs", "US", 33.6, -83.72, "Georgia", undefined, "approximate"),93  C("Miami", "US", 25.76, -80.19, "Florida"),94  C("Boston", "US", 42.36, -71.06, "Massachusetts"),95  C("Washington, D.C.", "US", 38.91, -77.04, "District of Columbia", ["Washington DC", "Washington D.C.", "Washington, DC"]),96  C("New York City", "US", 40.71, -74.01, "New York", ["New York"]),97  C("Philadelphia", "US", 39.95, -75.17, "Pennsylvania"),98  C("Moncks Corner", "US", 33.2, -80.01, "South Carolina"),99  C("Berkeley County", "US", 33.2, -79.95, "South Carolina", undefined, "approximate"),100  C("Dorchester County", "US", 33.17, -80.54, "South Carolina", undefined, "approximate"),101  C("The Lowcountry", "US", 33.16, -80.01, "South Carolina", undefined, "approximate"),102  C("Aiken", "US", 33.56, -81.72, "South Carolina"),103  C("Lenoir", "US", 35.91, -81.54, "North Carolina"),104  C("Forest City", "US", 35.33, -81.87, "North Carolina"),105  C("Huntsville", "US", 34.73, -86.59, "Alabama"),106  C("Montgomery", "US", 32.38, -86.31, "Alabama"),107  C("Jackson County", "US", 34.78, -85.99, "Alabama", undefined, "approximate"),108  C("Gallatin", "US", 36.39, -86.45, "Tennessee"),109  C("Montgomery County", "US", 36.5, -87.38, "Tennessee", undefined, "approximate"),110  C("Fort Wayne", "US", 41.08, -85.14, "Indiana"),111  C("Michigan City", "US", 41.71, -86.9, "Indiana"),112  C("Morgan County", "US", 39.48, -86.45, "Indiana", undefined, "approximate"),113  C("Jeffersonville", "US", 38.28, -85.74, "Indiana"),114  C("Lebanon", "US", 40.05, -86.47, "Indiana"),115  C("Van Buren Township", "US", 42.22, -83.5, "Michigan", undefined, "approximate"),116  C("Tulsa", "US", 36.15, -95.99, "Oklahoma"),117  C("Stillwater", "US", 36.12, -97.06, "Oklahoma"),118  C("Mayes County", "US", 36.3, -95.23, "Oklahoma", ["Pryor"], "approximate"),119  C("Muskogee County", "US", 35.62, -95.37, "Oklahoma", undefined, "approximate"),120  C("Richland Parish", "US", 32.42, -91.76, "Louisiana", undefined, "approximate"),121  C("West Memphis", "US", 35.15, -90.18, "Arkansas"),122  C("Kuna", "US", 43.49, -116.42, "Idaho"),123  C("Los Lunas", "US", 34.81, -106.73, "New Mexico"),124  C("Beaver Dam", "US", 43.46, -88.84, "Wisconsin"),125  C("Mount Pleasant", "US", 42.72, -87.89, "Wisconsin"),126  // ---- Canada ----127  C("Toronto", "CA", 43.65, -79.38, "Ontario"),128  C("Montréal", "CA", 45.5, -73.57, "Québec", ["Montreal"]),129  C("Québec City", "CA", 46.81, -71.21, "Québec", ["Quebec City", "Quebec"]),130  C("Calgary", "CA", 51.05, -114.07, "Alberta"),131  C("Sturgeon County", "CA", 53.78, -113.55, "Alberta", undefined, "approximate"),132  C("Vancouver", "CA", 49.28, -123.12, "British Columbia"),133  // ---- Latin America ----134  C("Querétaro", "MX", 20.59, -100.39, "Querétaro", ["Queretaro", "Querétaro State"]),135  C("Monterrey", "MX", 25.69, -100.32, "Nuevo León"),136  C("São Paulo", "BR", -23.55, -46.63, "São Paulo", ["Sao Paulo", "Sau Paulo", "São Paulo State"]),137  C("Osasco", "BR", -23.53, -46.79, "São Paulo"),138  C("Vinhedo", "BR", -23.03, -46.98, "São Paulo"),139  C("Rio de Janeiro", "BR", -22.91, -43.17, "Rio de Janeiro"),140  C("Santiago", "CL", -33.45, -70.67, "Santiago Metropolitan"),141  C("Quilicura", "CL", -33.36, -70.73, "Santiago Metropolitan"),142  C("Valparaíso", "CL", -33.05, -71.62, "Valparaíso", ["Valparaiso"]),143  C("Bogotá", "CO", 4.71, -74.07, "Bogotá", ["Bogota"]),144  C("Buenos Aires", "AR", -34.6, -58.38),145  C("Lima", "PE", -12.05, -77.04),146  C("Canelones", "UY", -34.52, -56.28, "Canelones"),147  // ---- Europe ----148  C("Dublin", "IE", 53.35, -6.26, "Leinster"),149  C("Clonee", "IE", 53.41, -6.44, "Meath"),150  C("London", "GB", 51.51, -0.13, "England"),151  C("Cardiff", "GB", 51.48, -3.18, "Wales"),152  C("Newport", "GB", 51.58, -2.99, "Wales"),153  C("Waltham Cross", "GB", 51.69, -0.03, "England"),154  C("Amsterdam", "NL", 52.37, 4.9, "North Holland"),155  C("Eemshaven", "NL", 53.45, 6.83, "Groningen"),156  C("Groningen", "NL", 53.22, 6.57, "Groningen"),157  C("Middenmeer", "NL", 52.81, 4.99, "North Holland"),158  C("Winschoten", "NL", 53.14, 7.03, "Groningen"),159  C("Brussels", "BE", 50.85, 4.35, "Brussels-Capital"),160  C("St. Ghislain", "BE", 50.45, 3.82, "Hainaut", ["Saint-Ghislain", "St Ghislain"]),161  C("Farciennes", "BE", 50.43, 4.54, "Hainaut"),162  C("Paris", "FR", 48.86, 2.35, "Île-de-France"),163  C("Marseille", "FR", 43.3, 5.37, "Provence-Alpes-Côte d'Azur"),164  C("Frankfurt", "DE", 50.11, 8.68, "Hesse", ["Frankfurt am Main"]),165  C("Hanau", "DE", 50.13, 8.92, "Hesse"),166  C("Dietzenbach", "DE", 50.02, 8.79, "Hesse"),167  C("Berlin", "DE", 52.52, 13.41, "Berlin"),168  C("Hamburg", "DE", 53.55, 9.99, "Hamburg"),169  C("Magdeburg", "DE", 52.13, 11.63, "Saxony-Anhalt"),170  C("Zurich", "CH", 47.38, 8.54, "Zürich", ["Zürich"]),171  C("Geneva", "CH", 46.2, 6.14, "Geneva"),172  C("Vienna", "AT", 48.21, 16.37, "Vienna"),173  C("Kronstorf", "AT", 48.14, 14.46, "Upper Austria"),174  C("Milan", "IT", 45.46, 9.19, "Lombardy"),175  C("Turin", "IT", 45.07, 7.69, "Piedmont"),176  C("Madrid", "ES", 40.42, -3.7, "Community of Madrid"),177  C("Aragón", "ES", 41.65, -0.89, "Aragón", ["Aragon", "Zaragoza"], "metro"),178  C("Lisbon", "PT", 38.72, -9.14),179  C("Stockholm", "SE", 59.33, 18.07, "Stockholm"),180  C("Gävle", "SE", 60.67, 17.14, "Gävleborg", ["Gävle and Sandviken", "Gavle"]),181  C("Luleå", "SE", 65.58, 22.16, "Norrbotten", ["Lulea"]),182  C("Horndal", "SE", 60.3, 16.42, "Dalarna"),183  C("Oslo", "NO", 59.91, 10.75, "Oslo"),184  C("Skien", "NO", 59.21, 9.61, "Telemark"),185  C("Copenhagen", "DK", 55.68, 12.57, "Capital Region"),186  C("Odense", "DK", 55.4, 10.39, "Southern Denmark"),187  C("Fredericia", "DK", 55.57, 9.75, "Southern Denmark"),188  C("Helsinki", "FI", 60.17, 24.94, "Uusimaa"),189  C("Hamina", "FI", 60.57, 27.2, "Kymenlaakso"),190  C("Muhos", "FI", 64.81, 25.99, "North Ostrobothnia"),191  C("Vaala", "FI", 64.56, 26.84, "Kainuu"),192  C("Warsaw", "PL", 52.23, 21.01, "Masovian"),193  C("Athens", "GR", 37.98, 23.73, "Attica"),194  C("Istanbul", "TR", 41.01, 28.98),195  C("Jovanovac", "RS", 44.06, 20.95, "Šumadija", ["Kragujevac"], "approximate"),196  // ---- Middle East & Africa ----197  C("Tel Aviv", "IL", 32.09, 34.78),198  C("Jerusalem", "IL", 31.77, 35.21),199  C("Dubai", "AE", 25.2, 55.27, "Dubai"),200  C("Abu Dhabi", "AE", 24.45, 54.38, "Abu Dhabi"),201  C("Doha", "QA", 25.29, 51.53),202  C("Riyadh", "SA", 24.71, 46.68, "Riyadh"),203  C("Jeddah", "SA", 21.49, 39.19, "Makkah"),204  C("Dammam", "SA", 26.43, 50.1, "Eastern Province"),205  C("Manama", "BH", 26.23, 50.59),206  C("Muscat", "OM", 23.59, 58.41),207  C("Johannesburg", "ZA", -26.2, 28.05, "Gauteng"),208  C("Cape Town", "ZA", -33.92, 18.42, "Western Cape"),209  C("Casablanca", "MA", 33.57, -7.59),210  C("Lagos", "NG", 6.52, 3.38, "Lagos"),211  C("Nairobi", "KE", -1.29, 36.82),212  // ---- Asia Pacific ----213  C("Tokyo", "JP", 35.68, 139.69, "Tokyo", ["Tokyo, Saitama", "Saitama"]),214  C("Osaka", "JP", 34.69, 135.5, "Osaka"),215  C("Inzai", "JP", 35.83, 140.15, "Chiba"),216  C("Seoul", "KR", 37.57, 126.98, "Seoul"),217  C("Busan", "KR", 35.18, 129.08, "Busan"),218  C("Chuncheon", "KR", 37.88, 127.73, "Gangwon"),219  C("Hong Kong", "HK", 22.32, 114.17),220  C("Taipei", "TW", 25.03, 121.57, "Taipei"),221  C("Changhua County", "TW", 24.08, 120.54, "Changhua", ["Changhua"], "approximate"),222  C("Singapore", "SG", 1.35, 103.82),223  C("Jurong West", "SG", 1.34, 103.71),224  C("Kuala Lumpur", "MY", 3.14, 101.69, "Federal Territory"),225  C("Johor Bahru", "MY", 1.49, 103.74, "Johor", ["Johorbahru", "Johor"]),226  C("Kulai", "MY", 1.66, 103.6, "Johor"),227  C("Selangor", "MY", 3.07, 101.52, "Selangor", undefined, "approximate"),228  C("Jakarta", "ID", -6.21, 106.85, "Jakarta"),229  C("Batam", "ID", 1.13, 104.05, "Riau Islands"),230  C("Bangkok", "TH", 13.76, 100.5, "Bangkok"),231  C("Chonburi", "TH", 13.36, 100.98, "Chonburi"),232  C("Manila", "PH", 14.6, 120.98, "Metro Manila"),233  C("Hanoi", "VN", 21.03, 105.85),234  C("Mumbai", "IN", 19.08, 72.88, "Maharashtra"),235  C("Pune", "IN", 18.52, 73.86, "Maharashtra"),236  C("Hyderabad", "IN", 17.39, 78.49, "Telangana"),237  C("Chennai", "IN", 13.08, 80.27, "Tamil Nadu"),238  C("Delhi", "IN", 28.61, 77.21, "Delhi", ["New Delhi"]),239  C("Kolkata", "IN", 22.57, 88.36, "West Bengal"),240  C("Andhra Pradesh", "IN", 15.91, 79.74, "Andhra Pradesh", ["Visakhapatnam"], "approximate"),241  C("Sydney", "AU", -33.87, 151.21, "New South Wales", ["New South Wales"]),242  C("Melbourne", "AU", -37.81, 144.96, "Victoria", ["Victoria"]),243  C("Canberra", "AU", -35.28, 149.13, "Australian Capital Territory"),244  C("Perth", "AU", -31.95, 115.86, "Western Australia"),245  C("Auckland", "NZ", -36.85, 174.76, "Auckland"),246  // ---- China ----247  C("Beijing", "CN", 39.9, 116.41, "Beijing"),248  C("Shanghai", "CN", 31.23, 121.47, "Shanghai"),249  C("Shenzhen", "CN", 22.54, 114.06, "Guangdong"),250  C("Guangzhou", "CN", 23.13, 113.26, "Guangdong"),251  C("Heyuan", "CN", 23.74, 114.7, "Guangdong"),252  C("Hangzhou", "CN", 30.27, 120.15, "Zhejiang"),253  C("Nanjing", "CN", 32.06, 118.8, "Jiangsu"),254  C("Qingdao", "CN", 36.07, 120.38, "Shandong"),255  C("Zhangjiakou", "CN", 40.77, 114.89, "Hebei"),256  C("Hohhot", "CN", 40.84, 111.75, "Inner Mongolia"),257  C("Ulanqab", "CN", 41.02, 113.13, "Inner Mongolia"),258  C("Chengdu", "CN", 30.57, 104.07, "Sichuan"),259  C("Chongqing", "CN", 29.56, 106.55, "Chongqing"),260  C("Zhongwei", "CN", 37.5, 105.19, "Ningxia"),261  C("Wuhan", "CN", 30.59, 114.31, "Hubei"),262  C("Fuzhou", "CN", 26.07, 119.3, "Fujian"),263  C("Ningxia", "CN", 37.5, 105.19, "Ningxia", undefined, "approximate"),264  C("Macao", "MO", 22.2, 113.54, undefined, ["Macau"]),265];266267/** US state name → ISO 3166-2 style code (used for regionName and to resolve countryIso2 = US). */268export const US_STATES: Record<string, string> = {269  alabama: "AL", alaska: "AK", arizona: "AZ", arkansas: "AR", california: "CA", colorado: "CO", connecticut: "CT", delaware: "DE", florida: "FL", georgia: "GA", hawaii: "HI", idaho: "ID", illinois: "IL", indiana: "IN", iowa: "IA", kansas: "KS", kentucky: "KY", louisiana: "LA", maine: "ME", maryland: "MD", massachusetts: "MA", michigan: "MI", minnesota: "MN", mississippi: "MS", missouri: "MO", montana: "MT", nebraska: "NE", nevada: "NV", "new hampshire": "NH", "new jersey": "NJ", "new mexico": "NM", "new york": "NY", "north carolina": "NC", "north dakota": "ND", ohio: "OH", oklahoma: "OK", oregon: "OR", pennsylvania: "PA", "rhode island": "RI", "south carolina": "SC", "south dakota": "SD", tennessee: "TN", texas: "TX", utah: "UT", vermont: "VT", virginia: "VA", washington: "WA", "west virginia": "WV", wisconsin: "WI", wyoming: "WY", "district of columbia": "DC", "washington dc": "DC", "washington, d.c.": "DC",270};271272export const CA_PROVINCES: Record<string, string> = {273  ontario: "ON", québec: "QC", quebec: "QC", alberta: "AB", "british columbia": "BC", manitoba: "MB", saskatchewan: "SK", "nova scotia": "NS", "new brunswick": "NB", "newfoundland and labrador": "NL", "prince edward island": "PE",274};275276export function normKey(s: string): string {277  return s.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();278}279280const INDEX = new Map<string, CityEntry>();281for (const e of CITIES) {282  for (const n of [e.city, ...(e.aliases ?? [])]) {283    const k = normKey(n);284    const withIso = `${k}|${e.iso}`;285    if (!INDEX.has(withIso)) INDEX.set(withIso, e);286    if (!INDEX.has(k)) INDEX.set(k, e);287  }288}289290/** Look a city up by name (accent/case-insensitive), optionally constrained to a country. */291export function lookupCity(name: string | null | undefined, iso?: string | null): CityEntry | null {292  if (!name) return null;293  const k = normKey(name);294  if (!k) return null;295  if (iso) return INDEX.get(`${k}|${iso.toUpperCase()}`) ?? null;296  return INDEX.get(k) ?? null;297}298299/** "Virginia" → "US", "Ontario" → "CA", else null. */300export function countryFromStateName(name: string | null | undefined): string | null {301  if (!name) return null;302  const k = name.trim().toLowerCase();303  if (US_STATES[k]) return "US";304  if (CA_PROVINCES[k]) return "CA";305  return null;306}307308/** Title-case a state name key from US_STATES / CA_PROVINCES for display. */309export function stateDisplayName(name: string): string {310  return name.trim().replace(/\s+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).replace(/\bDc\b/, "DC").replace(/\bOf\b/, "of").replace(/\bAnd\b/, "and");311}312