1"""Astral geocoder is a database of locations stored within the package.
2
3To get the :class:`~astral.LocationInfo` for a location use the
4:func:`~astral.geocoder.lookup` function e.g. ::
5
6    from astral.geocoder import lookup, database
7    l = lookup("London", database())
8
9All locations stored in the database can be accessed using the `all_locations` generator ::
10
11    from astral.geocoder import all_locations
12    for location in all_locations:
13        print(location)
14"""
15
16from functools import reduce
17from typing import Dict, Generator, List, Optional, Tuple, Union
18
19from astral import LocationInfo, dms_to_float
20
21__all__ = ["lookup", "database", "add_locations", "all_locations"]
22
23
24# region Location Info
25# name,region,timezone,latitude,longitude,elevation
26_LOCATION_INFO = """Abu Dhabi,UAE,Asia/Dubai,24°28'N,54°22'E
27Abu Dhabi,United Arab Emirates,Asia/Dubai,24°28'N,54°22'E
28Abuja,Nigeria,Africa/Lagos,09°05'N,07°32'E
29Accra,Ghana,Africa/Accra,05°35'N,00°06'W
30Addis Ababa,Ethiopia,Africa/Addis_Ababa,09°02'N,38°42'E
31Adelaide,Australia,Australia/Adelaide,34°56'S,138°36'E
32Al Jubail,Saudi Arabia,Asia/Riyadh,25°24'N,49°39'W
33Algiers,Algeria,Africa/Algiers,36°42'N,03°08'E
34Amman,Jordan,Asia/Amman,31°57'N,35°52'E
35Amsterdam,Netherlands,Europe/Amsterdam,52°23'N,04°54'E
36Andorra la Vella,Andorra,Europe/Andorra,42°31'N,01°32'E
37Ankara,Turkey,Europe/Istanbul,39°57'N,32°54'E
38Antananarivo,Madagascar,Indian/Antananarivo,18°55'S,47°31'E
39Apia,Samoa,Pacific/Apia,13°50'S,171°50'W
40Ashgabat,Turkmenistan,Asia/Ashgabat,38°00'N,57°50'E
41Asmara,Eritrea,Africa/Asmara,15°19'N,38°55'E
42Astana,Kazakhstan,Asia/Qyzylorda,51°10'N,71°30'E
43Asuncion,Paraguay,America/Asuncion,25°10'S,57°30'W
44Athens,Greece,Europe/Athens,37°58'N,23°46'E
45Avarua,Cook Islands,Etc/GMT-10,21°12'N,159°46'W
46Baghdad,Iraq,Asia/Baghdad,33°20'N,44°30'E
47Baku,Azerbaijan,Asia/Baku,40°29'N,49°56'E
48Bamako,Mali,Africa/Bamako,12°34'N,07°55'W
49Bandar Seri Begawan,Brunei Darussalam,Asia/Brunei,04°52'N,115°00'E
50Bangkok,Thailand,Asia/Bangkok,13°45'N,100°35'E
51Bangui,Central African Republic,Africa/Bangui,04°23'N,18°35'E
52Banjul,Gambia,Africa/Banjul,13°28'N,16°40'W
53Basse-Terre,Guadeloupe,America/Guadeloupe,16°00'N,61°44'W
54Basseterre,Saint Kitts and Nevis,America/St_Kitts,17°17'N,62°43'W
55Beijing,China,Asia/Harbin,39°55'N,116°20'E
56Beirut,Lebanon,Asia/Beirut,33°53'N,35°31'E
57Belfast,Northern Ireland,Europe/Belfast,54°36'N,5°56'W
58Belgrade,Yugoslavia,Europe/Belgrade,44°50'N,20°37'E
59Belmopan,Belize,America/Belize,17°18'N,88°30'W
60Berlin,Germany,Europe/Berlin,52°30'N,13°25'E
61Bern,Switzerland,Europe/Zurich,46°57'N,07°28'E
62Bishkek,Kyrgyzstan,Asia/Bishkek,42°54'N,74°46'E
63Bissau,Guinea-Bissau,Africa/Bissau,11°45'N,15°45'W
64Bloemfontein,South Africa,Africa/Johannesburg,29°12'S,26°07'E
65Bogota,Colombia,America/Bogota,04°34'N,74°00'W
66Brasilia,Brazil,Brazil/East,15°47'S,47°55'W
67Bratislava,Slovakia,Europe/Bratislava,48°10'N,17°07'E
68Brazzaville,Congo,Africa/Brazzaville,04°09'S,15°12'E
69Bridgetown,Barbados,America/Barbados,13°05'N,59°30'W
70Brisbane,Australia,Australia/Brisbane,27°30'S,153°01'E
71Brussels,Belgium,Europe/Brussels,50°51'N,04°21'E
72Bucharest,Romania,Europe/Bucharest,44°27'N,26°10'E
73Bucuresti,Romania,Europe/Bucharest,44°27'N,26°10'E
74Budapest,Hungary,Europe/Budapest,47°29'N,19°05'E
75Buenos Aires,Argentina,America/Buenos_Aires,34°62'S,58°44'W
76Bujumbura,Burundi,Africa/Bujumbura,03°16'S,29°18'E
77Cairo,Egypt,Africa/Cairo,30°01'N,31°14'E
78Canberra,Australia,Australia/Canberra,35°15'S,149°08'E
79Cape Town,South Africa,Africa/Johannesburg,33°55'S,18°22'E
80Caracas,Venezuela,America/Caracas,10°30'N,66°55'W
81Castries,Saint Lucia,America/St_Lucia,14°02'N,60°58'W
82Cayenne,French Guiana,America/Cayenne,05°05'N,52°18'W
83Charlotte Amalie,United States of Virgin Islands,America/Virgin,18°21'N,64°56'W
84Chisinau,Moldova,Europe/Chisinau,47°02'N,28°50'E
85Conakry,Guinea,Africa/Conakry,09°29'N,13°49'W
86Copenhagen,Denmark,Europe/Copenhagen,55°41'N,12°34'E
87Cotonou,Benin,Africa/Porto-Novo,06°23'N,02°42'E
88Dakar,Senegal,Africa/Dakar,14°34'N,17°29'W
89Damascus,Syrian Arab Republic,Asia/Damascus,33°30'N,36°18'E
90Dammam,Saudi Arabia,Asia/Riyadh,26°30'N,50°12'E
91Darwin,Australia,Australia/Darwin,12°26'S,130°50'E
92Dhaka,Bangladesh,Asia/Dhaka,23°43'N,90°26'E
93Dili,East Timor,Asia/Dili,08°29'S,125°34'E
94Djibouti,Djibouti,Africa/Djibouti,11°08'N,42°20'E
95Dodoma,United Republic of Tanzania,Africa/Dar_es_Salaam,06°08'S,35°45'E
96Doha,Qatar,Asia/Qatar,25°15'N,51°35'E
97Douglas,Isle Of Man,Europe/London,54°9'N,4°29'W
98Dublin,Ireland,Europe/Dublin,53°21'N,06°15'W
99Dushanbe,Tajikistan,Asia/Dushanbe,38°33'N,68°48'E
100El Aaiun,Morocco,UTC,27°9'N,13°12'W
101Fort-de-France,Martinique,America/Martinique,14°36'N,61°02'W
102Freetown,Sierra Leone,Africa/Freetown,08°30'N,13°17'W
103Funafuti,Tuvalu,Pacific/Funafuti,08°31'S,179°13'E
104Gaborone,Botswana,Africa/Gaborone,24°45'S,25°57'E
105George Town,Cayman Islands,America/Cayman,19°20'N,81°24'W
106Georgetown,Guyana,America/Guyana,06°50'N,58°12'W
107Gibraltar,Gibraltar,Europe/Gibraltar,36°9'N,5°21'W
108Guatemala,Guatemala,America/Guatemala,14°40'N,90°22'W
109Hanoi,Viet Nam,Asia/Saigon,21°05'N,105°55'E
110Harare,Zimbabwe,Africa/Harare,17°43'S,31°02'E
111Havana,Cuba,America/Havana,23°08'N,82°22'W
112Helsinki,Finland,Europe/Helsinki,60°15'N,25°03'E
113Hobart,Tasmania,Australia/Hobart,42°53'S,147°19'E
114Hong Kong,China,Asia/Hong_Kong,22°16'N,114°09'E
115Honiara,Solomon Islands,Pacific/Guadalcanal,09°27'S,159°57'E
116Islamabad,Pakistan,Asia/Karachi,33°40'N,73°10'E
117Jakarta,Indonesia,Asia/Jakarta,06°09'S,106°49'E
118Jerusalem,Israel,Asia/Jerusalem,31°47'N,35°12'E
119Juba,South Sudan,Africa/Juba,4°51'N,31°36'E
120Jubail,Saudi Arabia,Asia/Riyadh,27°02'N,49°39'E
121Kabul,Afghanistan,Asia/Kabul,34°28'N,69°11'E
122Kampala,Uganda,Africa/Kampala,00°20'N,32°30'E
123Kathmandu,Nepal,Asia/Kathmandu,27°45'N,85°20'E
124Khartoum,Sudan,Africa/Khartoum,15°31'N,32°35'E
125Kiev,Ukraine,Europe/Kiev,50°30'N,30°28'E
126Kigali,Rwanda,Africa/Kigali,01°59'S,30°04'E
127Kingston,Jamaica,America/Jamaica,18°00'N,76°50'W
128Kingston,Norfolk Island,Pacific/Norfolk,45°20'S,168°43'E
129Kingstown,Saint Vincent and the Grenadines,America/St_Vincent,13°10'N,61°10'W
130Kinshasa,Democratic Republic of the Congo,Africa/Kinshasa,04°20'S,15°15'E
131Koror,Palau,Pacific/Palau,07°20'N,134°28'E
132Kuala Lumpur,Malaysia,Asia/Kuala_Lumpur,03°09'N,101°41'E
133Kuwait,Kuwait,Asia/Kuwait,29°30'N,48°00'E
134La Paz,Bolivia,America/La_Paz,16°20'S,68°10'W
135Libreville,Gabon,Africa/Libreville,00°25'N,09°26'E
136Lilongwe,Malawi,Africa/Blantyre,14°00'S,33°48'E
137Lima,Peru,America/Lima,12°00'S,77°00'W
138Lisbon,Portugal,Europe/Lisbon,38°42'N,09°10'W
139Ljubljana,Slovenia,Europe/Ljubljana,46°04'N,14°33'E
140Lome,Togo,Africa/Lome,06°09'N,01°20'E
141London,England,Europe/London,51°28'24"N,00°00'3"W
142Luanda,Angola,Africa/Luanda,08°50'S,13°15'E
143Lusaka,Zambia,Africa/Lusaka,15°28'S,28°16'E
144Luxembourg,Luxembourg,Europe/Luxembourg,49°37'N,06°09'E
145Macau,Macao,Asia/Macau,22°12'N,113°33'E
146Madinah,Saudi Arabia,Asia/Riyadh,24°28'N,39°36'E
147Madrid,Spain,Europe/Madrid,40°25'N,03°45'W
148Majuro,Marshall Islands,Pacific/Majuro,7°4'N,171°16'E
149Makkah,Saudi Arabia,Asia/Riyadh,21°26'N,39°49'E
150Malabo,Equatorial Guinea,Africa/Malabo,03°45'N,08°50'E
151Male,Maldives,Indian/Maldives,04°00'N,73°28'E
152Mamoudzou,Mayotte,Indian/Mayotte,12°48'S,45°14'E
153Managua,Nicaragua,America/Managua,12°06'N,86°20'W
154Manama,Bahrain,Asia/Bahrain,26°10'N,50°30'E
155Manila,Philippines,Asia/Manila,14°40'N,121°03'E
156Maputo,Mozambique,Africa/Maputo,25°58'S,32°32'E
157Maseru,Lesotho,Africa/Maseru,29°18'S,27°30'E
158Masqat,Oman,Asia/Muscat,23°37'N,58°36'E
159Mbabane,Swaziland,Africa/Mbabane,26°18'S,31°06'E
160Mecca,Saudi Arabia,Asia/Riyadh,21°26'N,39°49'E
161Medina,Saudi Arabia,Asia/Riyadh,24°28'N,39°36'E
162Melbourne,Australia,Australia/Melbourne,37°48'S,144°57'E
163Mexico,Mexico,America/Mexico_City,19°20'N,99°10'W
164Minsk,Belarus,Europe/Minsk,53°52'N,27°30'E
165Mogadishu,Somalia,Africa/Mogadishu,02°02'N,45°25'E
166Monaco,Priciplality Of Monaco,Europe/Monaco,43°43'N,7°25'E
167Monrovia,Liberia,Africa/Monrovia,06°18'N,10°47'W
168Montevideo,Uruguay,America/Montevideo,34°50'S,56°11'W
169Moroni,Comoros,Indian/Comoro,11°40'S,43°16'E
170Moscow,Russian Federation,Europe/Moscow,55°45'N,37°35'E
171Moskva,Russian Federation,Europe/Moscow,55°45'N,37°35'E
172Mumbai,India,Asia/Kolkata,18°58'N,72°49'E
173Muscat,Oman,Asia/Muscat,23°37'N,58°32'E
174N'Djamena,Chad,Africa/Ndjamena,12°10'N,14°59'E
175Nairobi,Kenya,Africa/Nairobi,01°17'S,36°48'E
176Nassau,Bahamas,America/Nassau,25°05'N,77°20'W
177Naypyidaw,Myanmar,Asia/Rangoon,19°45'N,96°6'E
178New Delhi,India,Asia/Kolkata,28°37'N,77°13'E
179Ngerulmud,Palau,Pacific/Palau,7°30'N,134°37'E
180Niamey,Niger,Africa/Niamey,13°27'N,02°06'E
181Nicosia,Cyprus,Asia/Nicosia,35°10'N,33°25'E
182Nouakchott,Mauritania,Africa/Nouakchott,20°10'S,57°30'E
183Noumea,New Caledonia,Pacific/Noumea,22°17'S,166°30'E
184Nuku'alofa,Tonga,Pacific/Tongatapu,21°10'S,174°00'W
185Nuuk,Greenland,America/Godthab,64°10'N,51°35'W
186Oranjestad,Aruba,America/Aruba,12°32'N,70°02'W
187Oslo,Norway,Europe/Oslo,59°55'N,10°45'E
188Ottawa,Canada,US/Eastern,45°27'N,75°42'W
189Ouagadougou,Burkina Faso,Africa/Ouagadougou,12°15'N,01°30'W
190P'yongyang,Democratic People's Republic of Korea,Asia/Pyongyang,39°09'N,125°30'E
191Pago Pago,American Samoa,Pacific/Pago_Pago,14°16'S,170°43'W
192Palikir,Micronesia,Pacific/Ponape,06°55'N,158°09'E
193Panama,Panama,America/Panama,09°00'N,79°25'W
194Papeete,French Polynesia,Pacific/Tahiti,17°32'S,149°34'W
195Paramaribo,Suriname,America/Paramaribo,05°50'N,55°10'W
196Paris,France,Europe/Paris,48°50'N,02°20'E
197Perth,Australia,Australia/Perth,31°56'S,115°50'E
198Phnom Penh,Cambodia,Asia/Phnom_Penh,11°33'N,104°55'E
199Podgorica,Montenegro,Europe/Podgorica,42°28'N,19°16'E
200Port Louis,Mauritius,Indian/Mauritius,20°9'S,57°30'E
201Port Moresby,Papua New Guinea,Pacific/Port_Moresby,09°24'S,147°08'E
202Port-Vila,Vanuatu,Pacific/Efate,17°45'S,168°18'E
203Port-au-Prince,Haiti,America/Port-au-Prince,18°40'N,72°20'W
204Port of Spain,Trinidad and Tobago,America/Port_of_Spain,10°40'N,61°31'W
205Porto-Novo,Benin,Africa/Porto-Novo,06°23'N,02°42'E
206Prague,Czech Republic,Europe/Prague,50°05'N,14°22'E
207Praia,Cape Verde,Atlantic/Cape_Verde,15°02'N,23°34'W
208Pretoria,South Africa,Africa/Johannesburg,25°44'S,28°12'E
209Pristina,Albania,Europe/Tirane,42°40'N,21°10'E
210Quito,Ecuador,America/Guayaquil,00°15'S,78°35'W
211Rabat,Morocco,Africa/Casablanca,34°1'N,6°50'W
212Reykjavik,Iceland,Atlantic/Reykjavik,64°10'N,21°57'W
213Riga,Latvia,Europe/Riga,56°53'N,24°08'E
214Riyadh,Saudi Arabia,Asia/Riyadh,24°41'N,46°42'E
215Road Town,British Virgin Islands,America/Virgin,18°27'N,64°37'W
216Rome,Italy,Europe/Rome,41°54'N,12°29'E
217Roseau,Dominica,America/Dominica,15°20'N,61°24'W
218Saint Helier,Jersey,Etc/GMT,49°11'N,2°6'W
219Saint Pierre,Saint Pierre and Miquelon,America/Miquelon,46°46'N,56°12'W
220Saipan,Northern Mariana Islands,Pacific/Saipan,15°12'N,145°45'E
221Sana,Yemen,Asia/Aden,15°20'N,44°12'W
222Sana'a,Yemen,Asia/Aden,15°20'N,44°12'W
223San Jose,Costa Rica,America/Costa_Rica,09°55'N,84°02'W
224San Juan,Puerto Rico,America/Puerto_Rico,18°28'N,66°07'W
225San Marino,San Marino,Europe/San_Marino,43°55'N,12°30'E
226San Salvador,El Salvador,America/El_Salvador,13°40'N,89°10'W
227Santiago,Chile,America/Santiago,33°24'S,70°40'W
228Santo Domingo,Dominica Republic,America/Santo_Domingo,18°30'N,69°59'W
229Sao Tome,Sao Tome and Principe,Africa/Sao_Tome,00°10'N,06°39'E
230Sarajevo,Bosnia and Herzegovina,Europe/Sarajevo,43°52'N,18°26'E
231Seoul,Republic of Korea,Asia/Seoul,37°31'N,126°58'E
232Singapore,Republic of Singapore,Asia/Singapore,1°18'N,103°48'E
233Skopje,The Former Yugoslav Republic of Macedonia,Europe/Skopje,42°01'N,21°26'E
234Sofia,Bulgaria,Europe/Sofia,42°45'N,23°20'E
235Sri Jayawardenapura Kotte,Sri Lanka,Asia/Colombo,6°54'N,79°53'E
236St. George's,Grenada,America/Grenada,32°22'N,64°40'W
237St. John's,Antigua and Barbuda,America/Antigua,17°7'N,61°51'W
238St. Peter Port,Guernsey,Europe/Guernsey,49°26'N,02°33'W
239Stanley,Falkland Islands,Atlantic/Stanley,51°40'S,59°51'W
240Stockholm,Sweden,Europe/Stockholm,59°20'N,18°05'E
241Sucre,Bolivia,America/La_Paz,16°20'S,68°10'W
242Suva,Fiji,Pacific/Fiji,18°06'S,178°30'E
243Sydney,Australia,Australia/Sydney,33°53'S,151°13'E
244Taipei,Republic of China (Taiwan),Asia/Taipei,25°02'N,121°38'E
245T'bilisi,Georgia,Asia/Tbilisi,41°43'N,44°50'E
246Tbilisi,Georgia,Asia/Tbilisi,41°43'N,44°50'E
247Tallinn,Estonia,Europe/Tallinn,59°22'N,24°48'E
248Tarawa,Kiribati,Pacific/Tarawa,01°30'N,173°00'E
249Tashkent,Uzbekistan,Asia/Tashkent,41°20'N,69°10'E
250Tegucigalpa,Honduras,America/Tegucigalpa,14°05'N,87°14'W
251Tehran,Iran,Asia/Tehran,35°44'N,51°30'E
252Thimphu,Bhutan,Asia/Thimphu,27°31'N,89°45'E
253Tirana,Albania,Europe/Tirane,41°18'N,19°49'E
254Tirane,Albania,Europe/Tirane,41°18'N,19°49'E
255Torshavn,Faroe Islands,Atlantic/Faroe,62°05'N,06°56'W
256Tokyo,Japan,Asia/Tokyo,35°41'N,139°41'E
257Tripoli,Libyan Arab Jamahiriya,Africa/Tripoli,32°49'N,13°07'E
258Tunis,Tunisia,Africa/Tunis,36°50'N,10°11'E
259Ulan Bator,Mongolia,Asia/Ulaanbaatar,47°55'N,106°55'E
260Ulaanbaatar,Mongolia,Asia/Ulaanbaatar,47°55'N,106°55'E
261Vaduz,Liechtenstein,Europe/Vaduz,47°08'N,09°31'E
262Valletta,Malta,Europe/Malta,35°54'N,14°31'E
263Vienna,Austria,Europe/Vienna,48°12'N,16°22'E
264Vientiane,Lao People's Democratic Republic,Asia/Vientiane,17°58'N,102°36'E
265Vilnius,Lithuania,Europe/Vilnius,54°38'N,25°19'E
266W. Indies,Antigua and Barbuda,America/Antigua,17°20'N,61°48'W
267Warsaw,Poland,Europe/Warsaw,52°13'N,21°00'E
268Washington DC,USA,US/Eastern,39°91'N,77°02'W
269Wellington,New Zealand,Pacific/Auckland,41°19'S,174°46'E
270Willemstad,Netherlands Antilles,America/Curacao,12°05'N,69°00'W
271Windhoek,Namibia,Africa/Windhoek,22°35'S,17°04'E
272Yamoussoukro,Cote d'Ivoire,Africa/Abidjan,06°49'N,05°17'W
273Yangon,Myanmar,Asia/Rangoon,16°45'N,96°20'E
274Yaounde,Cameroon,Africa/Douala,03°50'N,11°35'E
275Yaren,Nauru,Pacific/Nauru,0°32'S,166°55'E
276Yerevan,Armenia,Asia/Yerevan,40°10'N,44°31'E
277Zagreb,Croatia,Europe/Zagreb,45°50'N,15°58'E
278
279# UK Cities
280Aberdeen,Scotland,Europe/London,57°08'N,02°06'W
281Birmingham,England,Europe/London,52°30'N,01°50'W
282Bolton,England,Europe/London,53°35'N,02°15'W
283Bradford,England,Europe/London,53°47'N,01°45'W
284Bristol,England,Europe/London,51°28'N,02°35'W
285Cardiff,Wales,Europe/London,51°29'N,03°13'W
286Crawley,England,Europe/London,51°8'N,00°10'W
287Edinburgh,Scotland,Europe/London,55°57'N,03°13'W
288Glasgow,Scotland,Europe/London,55°50'N,04°15'W
289Greenwich,England,Europe/London,51°28'N,00°00'W
290Leeds,England,Europe/London,53°48'N,01°35'W
291Leicester,England,Europe/London,52°38'N,01°08'W
292Liverpool,England,Europe/London,53°25'N,03°00'W
293Manchester,England,Europe/London,53°30'N,02°15'W
294Newcastle Upon Tyne,England,Europe/London,54°59'N,01°36'W
295Newcastle,England,Europe/London,54°59'N,01°36'W
296Norwich,England,Europe/London,52°38'N,01°18'E
297Oxford,England,Europe/London,51°45'N,01°15'W
298Plymouth,England,Europe/London,50°25'N,04°15'W
299Portsmouth,England,Europe/London,50°48'N,01°05'W
300Reading,England,Europe/London,51°27'N,0°58'W
301Sheffield,England,Europe/London,53°23'N,01°28'W
302Southampton,England,Europe/London,50°55'N,01°25'W
303Swansea,England,Europe/London,51°37'N,03°57'W
304Swindon,England,Europe/London,51°34'N,01°47'W
305Wolverhampton,England,Europe/London,52°35'N,2°08'W
306Barrow-In-Furness,England,Europe/London,54°06'N,3°13'W
307
308# US State Capitals
309Montgomery,USA,US/Central,32°21'N,86°16'W
310Juneau,USA,US/Alaska,58°23'N,134°11'W
311Phoenix,USA,America/Phoenix,33°26'N,112°04'W
312Little Rock,USA,US/Central,34°44'N,92°19'W
313Sacramento,USA,US/Pacific,38°33'N,121°28'W
314Denver,USA,US/Mountain,39°44'N,104°59'W
315Hartford,USA,US/Eastern,41°45'N,72°41'W
316Dover,USA,US/Eastern,39°09'N,75°31'W
317Tallahassee,USA,US/Eastern,30°27'N,84°16'W
318Atlanta,USA,US/Eastern,33°45'N,84°23'W
319Honolulu,USA,US/Hawaii,21°18'N,157°49'W
320Boise,USA,US/Mountain,43°36'N,116°12'W
321Springfield,USA,US/Central,39°47'N,89°39'W
322Indianapolis,USA,US/Eastern,39°46'N,86°9'W
323Des Moines,USA,US/Central,41°35'N,93°37'W
324Topeka,USA,US/Central,39°03'N,95°41'W
325Frankfort,USA,US/Eastern,38°11'N,84°51'W
326Baton Rouge,USA,US/Central,30°27'N,91°8'W
327Augusta,USA,US/Eastern,44°18'N,69°46'W
328Annapolis,USA,US/Eastern,38°58'N,76°30'W
329Boston,USA,US/Eastern,42°21'N,71°03'W
330Lansing,USA,US/Eastern,42°44'N,84°32'W
331Saint Paul,USA,US/Central,44°56'N,93°05'W
332Jackson,USA,US/Central,32°17'N,90°11'W
333Jefferson City,USA,US/Central,38°34'N,92°10'W
334Helena,USA,US/Mountain,46°35'N,112°1'W
335Lincoln,USA,US/Central,40°48'N,96°40'W
336Carson City,USA,US/Pacific,39°9'N,119°45'W
337Concord,USA,US/Eastern,43°12'N,71°32'W
338Trenton,USA,US/Eastern,40°13'N,74°45'W
339Santa Fe,USA,US/Mountain,35°40'N,105°57'W
340Albany,USA,US/Eastern,42°39'N,73°46'W
341Raleigh,USA,US/Eastern,35°49'N,78°38'W
342Bismarck,USA,US/Central,46°48'N,100°46'W
343Columbus,USA,US/Eastern,39°59'N,82°59'W
344Oklahoma City,USA,US/Central,35°28'N,97°32'W
345Salem,USA,US/Pacific,44°55'N,123°1'W
346Harrisburg,USA,US/Eastern,40°16'N,76°52'W
347Providence,USA,US/Eastern,41°49'N,71°25'W
348Columbia,USA,US/Eastern,34°00'N,81°02'W
349Pierre,USA,US/Central,44°22'N,100°20'W
350Nashville,USA,US/Central,36°10'N,86°47'W
351Austin,USA,US/Central,30°16'N,97°45'W
352Salt Lake City,USA,US/Mountain,40°45'N,111°53'W
353Montpelier,USA,US/Eastern,44°15'N,72°34'W
354Richmond,USA,US/Eastern,37°32'N,77°25'W
355Olympia,USA,US/Pacific,47°2'N,122°53'W
356Charleston,USA,US/Eastern,38°20'N,81°38'W
357Madison,USA,US/Central,43°4'N,89°24'W
358Cheyenne,USA,US/Mountain,41°8'N,104°48'W
359
360# Major US Cities
361Birmingham,USA,US/Central,33°39'N,86°48'W
362Anchorage,USA,US/Alaska,61°13'N,149°53'W
363Los Angeles,USA,US/Pacific,34°03'N,118°15'W
364San Francisco,USA,US/Pacific,37°46'N,122°25'W
365Bridgeport,USA,US/Eastern,41°11'N,73°11'W
366Wilmington,USA,US/Eastern,39°44'N,75°32'W
367Jacksonville,USA,US/Eastern,30°19'N,81°39'W
368Miami,USA,US/Eastern,26°8'N,80°12'W
369Chicago,USA,US/Central,41°50'N,87°41'W
370Wichita,USA,US/Central,37°41'N,97°20'W
371Louisville,USA,US/Eastern,38°15'N,85°45'W
372New Orleans,USA,US/Central,29°57'N,90°4'W
373Portland,USA,US/Eastern,43°39'N,70°16'W
374Baltimore,USA,US/Eastern,39°17'N,76°37'W
375Detroit,USA,US/Eastern,42°19'N,83°2'W
376Minneapolis,USA,US/Central,44°58'N,93°15'W
377Kansas City,USA,US/Central,39°06'N,94°35'W
378Billings,USA,US/Mountain,45°47'N,108°32'W
379Omaha,USA,US/Central,41°15'N,96°0'W
380Las Vegas,USA,US/Pacific,36°10'N,115°08'W
381Manchester,USA,US/Eastern,42°59'N,71°27'W
382Newark,USA,US/Eastern,40°44'N,74°11'W
383Albuquerque,USA,US/Mountain,35°06'N,106°36'W
384New York,USA,US/Eastern,40°43'N,74°0'W
385Charlotte,USA,US/Eastern,35°13'N,80°50'W
386Fargo,USA,US/Central,46°52'N,96°47'W
387Cleveland,USA,US/Eastern,41°28'N,81°40'W
388Philadelphia,USA,US/Eastern,39°57'N,75°10'W
389Sioux Falls,USA,US/Central,43°32'N,96°43'W
390Memphis,USA,US/Central,35°07'N,89°58'W
391Houston,USA,US/Central,29°45'N,95°22'W
392Dallas,USA,US/Central,32°47'N,96°48'W
393Burlington,USA,US/Eastern,44°28'N,73°9'W
394Virginia Beach,USA,US/Eastern,36°50'N,76°05'W
395Seattle,USA,US/Pacific,47°36'N,122°19'W
396Milwaukee,USA,US/Central,43°03'N,87°57'W
397San Diego,USA,US/Pacific,32°42'N,117°09'W
398Orlando,USA,US/Eastern,28°32'N,81°22'W
399Buffalo,USA,US/Eastern,42°54'N,78°50'W
400Toledo,USA,US/Eastern,41°39'N,83°34'W
401
402# Canadian cities
403Vancouver,Canada,America/Vancouver,49°15'N,123°6'W
404Calgary,Canada,America/Edmonton,51°2'N,114°3'W
405Edmonton,Canada,America/Edmonton,53°32'N,113°29'W
406Saskatoon,Canada,America/Regina,52°8'N,106°40'W
407Regina,Canada,America/Regina,50°27'N,104°36'W
408Winnipeg,Canada,America/Winnipeg,49°53'N,97°8'W
409Toronto,Canada,America/Toronto,43°39'N,79°22'W
410Montreal,Canada,America/Montreal,45°30'N,73°33'W
411Quebec,Canada,America/Toronto,46°48'N,71°14'W
412Fredericton,Canada,America/Halifax,45°57'N,66°38'W
413Halifax,Canada,America/Halifax,44°38'N,63°34'W
414Charlottetown,Canada,America/Halifax,46°14'N,63°7'W
415St. John's,Canada,America/Halifax,47°33'N,52°42'W
416Whitehorse,Canada,America/Whitehorse,60°43'N,135°3'W
417Yellowknife,Canada,America/Yellowknife,62°27'N,114°22'W
418Iqaluit,Canada,America/Iqaluit,63°44'N,68°31'W
419"""
420# endregion
421
422GroupName = str
423GroupInfo = Dict
424LocationInfoList = List[LocationInfo]
425LocationDatabase = Dict[GroupName, GroupInfo[str, LocationInfoList]]
426
427
428def database() -> LocationDatabase:
429    """Returns a database populated with the inital set of locations stored
430    in this module
431    """
432    db: LocationDatabase = {}
433    _add_locations_from_str(_LOCATION_INFO, db)
434    return db
435
436
437def _sanitize_key(key) -> str:
438    """Sanitize the location or group key to look up
439
440    Args:
441        key: The key to sanitize
442    """
443    return str(key).lower().replace(" ", "_")
444
445
446def _location_count(db: LocationDatabase) -> int:
447    """Returns the count of the locations currently in the database"""
448    return reduce(lambda count, group: count + len(group), db.values(), 0)
449
450
451def _get_group(name: str, db: LocationDatabase) -> Optional[GroupInfo]:
452    return db.get(name, None)
453
454
455def _add_location_to_db(location: LocationInfo, db: LocationDatabase) -> None:
456    """Add a single location to a database"""
457    key = _sanitize_key(location.timezone_group)
458    group = _get_group(key, db)
459    if not group:
460        group = {}
461        db[key] = group
462
463    location_key = _sanitize_key(location.name)
464    if location_key not in group:
465        group[location_key] = [location]
466    else:
467        group[location_key].append(location)
468
469
470def _indexable_to_locationinfo(idxable) -> LocationInfo:
471    return LocationInfo(
472        name=idxable[0],
473        region=idxable[1],
474        timezone=idxable[2],
475        latitude=dms_to_float(idxable[3], 90.0),
476        longitude=dms_to_float(idxable[4], 180.0),
477    )
478
479
480def _add_locations_from_str(location_string: str, db: LocationDatabase) -> None:
481    """Add locations from a string."""
482
483    for line in location_string.split("\n"):
484        line = line.strip()
485        if line != "" and line[0] != "#":
486            info = line.split(",")
487            location = _indexable_to_locationinfo(info)
488            _add_location_to_db(location, db)
489
490
491def _add_locations_from_list(
492    location_list: List[Union[Tuple, str]], db: LocationDatabase
493) -> None:
494    """Add locations from a list of either strings or lists of strings or tuples of strings."""
495    for info in location_list:
496        if isinstance(info, str):
497            _add_locations_from_str(info, db)
498        elif isinstance(info, (list, tuple)):
499            location = _indexable_to_locationinfo(info)
500            _add_location_to_db(location, db)
501
502
503def add_locations(locations: Union[List, str], db: LocationDatabase) -> None:
504    """Add locations to the database.
505
506    Locations can be added by passing either a string with one line per location or by passing
507    a list containing strings, lists or tuples (lists and tuples are passed directly to the
508    LocationInfo constructor)."""
509    if isinstance(locations, str):
510        _add_locations_from_str(locations, db)
511    elif isinstance(locations, (list, tuple)):
512        _add_locations_from_list(locations, db)
513
514
515def group(region: str, db: LocationDatabase) -> GroupInfo:
516    """Access to each timezone group. For example London is in timezone
517    group Europe.
518
519    Lookups are case insensitive
520
521    Args:
522        region: the name to look up
523
524    Raises:
525        KeyError: if the location is not found
526    """
527    key = _sanitize_key(region)
528    for name, value in db.items():
529        if name == key:
530            return value
531
532    raise KeyError(f"Unrecognised Group - {region}")
533
534
535def lookup_in_group(location: str, group: Dict) -> LocationInfo:
536    """Looks up the location within a group dictionary
537
538    You can supply an optional region name by adding a comma
539    followed by the region name. Where multiple locations have the
540    same name you may need to supply the region name otherwise
541    the first result will be returned which may not be the one
542    you're looking for::
543
544        location = group['Abu Dhabi,United Arab Emirates']
545
546    Lookups are case insensitive.
547
548    Args:
549        location: The location to look up
550        group: The location group to look in
551
552    Raises:
553        KeyError: if the location is not found
554    """
555    key = _sanitize_key(location)
556
557    try:
558        lookup_name, lookup_region = key.split(",", 1)
559    except ValueError:
560        lookup_name = key
561        lookup_region = ""
562
563    lookup_name = lookup_name.strip("\"'")
564    lookup_region = lookup_region.strip("\"'")
565
566    for (location_name, location_list) in group.items():
567        if location_name == lookup_name:
568            if lookup_region == "":
569                return location_list[0]
570
571            for loc in location_list:
572                if _sanitize_key(loc.region) == lookup_region:
573                    return loc
574
575    raise KeyError(f"Unrecognised location name - {key}")
576
577
578def lookup(name: str, db: LocationDatabase) -> Union[Dict, LocationInfo]:
579    """Look up a name in a database.
580
581    If a group with the name specified is a group name then that will
582    be returned. If no group is found a location with the name will be
583    looked up.
584
585    Args:
586        name: The group/location name to look up
587        db:   The location database to look in
588
589    Raises:
590        KeyError: if the name is not found
591    """
592
593    key = _sanitize_key(name)
594    for group_key, group in db.items():
595        if group_key == key:
596            return group
597
598        try:
599            return lookup_in_group(name, group)
600        except KeyError:
601            pass
602
603    raise KeyError(f"Unrecognised name - {name}")
604
605
606def all_locations(db: LocationDatabase) -> Generator[LocationInfo, None, None]:
607    """A generator that returns all the :class:`~astral.LocationInfo`\\s contained in the database
608    """
609    for group_info in db.values():
610        for location_list in group_info.values():
611            for location in location_list:
612                yield location
613