├── NGeoNamesTests ├── Testdata │ ├── emptyfile.txt │ ├── invalid_admin1CodesASCII.txt │ ├── invalid.ext │ ├── test_featureClasses_en.txt │ ├── test_geofilereadercustom2.txt │ ├── test_hierarchy.txt │ ├── test_geonames_simple.txt │ ├── test_continentCodes.txt │ ├── test_iso-languagecodes.txt │ ├── test_userTags.txt │ ├── test_geonames_ext.txt │ ├── countryInfo_gzip_compat.txt.gz │ ├── test_admin2Codes.txt │ ├── countryInfo_not_gzip_compat.txt.gz │ ├── test_featureCodes_en.txt │ ├── test_admin1CodesASCII.txt │ ├── test_alternateNamesV2.txt │ ├── test_timeZones.txt │ ├── test_geonames.txt │ ├── test_postalCodes.txt │ ├── test_geofilereadercustom1.txt │ ├── test_countryInfo.txt │ ├── test_countryInfo2.txt │ ├── test_alternateNames.txt │ ├── test_custom.txt │ └── test_extendedgeonames.txt ├── CustomComposer.cs ├── CustomEntity.cs ├── CustomParser.cs ├── GeoUtilTests.cs ├── Properties │ └── AssemblyInfo.cs ├── GeoFileReaderTests.cs ├── EntitiesTests.cs ├── FileUtil.cs ├── GeoFileWriterTests.cs ├── ReverseGeoCodeTests.cs ├── ComposerTests.cs └── NGeoNamesTests.csproj ├── icon.png ├── .github └── FUNDING.yml ├── NGeoNames ├── packages.config ├── Resources │ ├── continentCodes.txt │ └── featureClasses_en.txt ├── Entities │ ├── IGeoLocation.cs │ ├── UserTag.cs │ ├── Continent.cs │ ├── FeatureClass.cs │ ├── GeoName.cs │ ├── FeatureCode.cs │ ├── HierarchyNode.cs │ ├── Admin1Code.cs │ ├── Admin2Code.cs │ ├── ISOLanguageCode.cs │ ├── TimeZone.cs │ ├── AlternateNameV2.cs │ ├── Postalcode.cs │ ├── AlternateName.cs │ ├── CountryInfo.cs │ └── ExtendedGeoName.cs ├── Parsers │ ├── ParserException.cs │ ├── IParser.cs │ ├── UserTagParser.cs │ ├── ContinentParser.cs │ ├── HierarchyParser.cs │ ├── Admin1CodeParser.cs │ ├── Admin2CodeParser.cs │ ├── FeatureClassParser.cs │ ├── ISOLanguageCodeParser.cs │ ├── FeatureCodeParser.cs │ ├── TimeZoneParser.cs │ ├── PostalcodeParser.cs │ ├── AlternateNameParser.cs │ ├── AlternateNameParserV2.cs │ ├── ExtendedGeoNameParser.cs │ ├── CountryInfoParser.cs │ ├── GeoNameParser.cs │ └── BaseParser.cs ├── Composers │ ├── UserTagComposer.cs │ ├── ContinentComposer.cs │ ├── FeatureClassComposer.cs │ ├── Admin1CodeComposer.cs │ ├── Admin2CodeComposer.cs │ ├── HierarchyComposer.cs │ ├── ISOLanguageCodeComposer.cs │ ├── TimeZoneComposer.cs │ ├── IComposer.cs │ ├── CountryInfoComposer.cs │ ├── AlternateNameComposer.cs │ ├── PostalcodeComposer.cs │ ├── AlternateNameV2Composer.cs │ ├── FeatureCodeComposer.cs │ ├── ExtendedGeoNameComposer.cs │ ├── GeoNameComposer.cs │ └── BaseComposer.cs ├── FileType.cs ├── NGeoNames.csproj ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── GeoUtil.cs ├── GeoExtensionMethods.cs └── ReverseGeoCode.cs ├── DumpTester ├── App.config ├── Properties │ └── AssemblyInfo.cs ├── DumpTester.csproj └── Program.cs ├── LICENSE ├── NGeoNames.sln ├── .gitignore └── NGeoNames.Documentation └── NGeoNamesHelp.shfbproj /NGeoNamesTests/Testdata/emptyfile.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/invalid_admin1CodesASCII.txt: -------------------------------------------------------------------------------- 1 | FOO -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/invalid.ext: -------------------------------------------------------------------------------- 1 | this file contains valid data -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobThree/NGeoNames/HEAD/icon.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [RobThree] 2 | custom: ["https://paypal.me/robiii"] 3 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_featureClasses_en.txt: -------------------------------------------------------------------------------- 1 | #See http://www.geonames.org/export/codes.html 2 | X Test -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_geofilereadercustom2.txt: -------------------------------------------------------------------------------- 1 | this!file!has! 2 | no!comments!and! 3 | no!empty!lines! -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_hierarchy.txt: -------------------------------------------------------------------------------- 1 | 6295630 6255146 ADM 2 | 6255149 0 ADM 3 | 672027 663875 4 | -1 3623365 -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_geonames_simple.txt: -------------------------------------------------------------------------------- 1 | 1136469 Khōst 33.33951 69.92041 2 | 3865840 Añatuya 28.46064 62.83472 3 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_continentCodes.txt: -------------------------------------------------------------------------------- 1 | #See http://download.geonames.org/export/dump/readme.txt 2 | EU Europe 6255148 3 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_iso-languagecodes.txt: -------------------------------------------------------------------------------- 1 | ISO 639-3 ISO 639-2 ISO 639-1 Language Name 2 | alw Alaba-K’abeena 3 | zul zul zu Zulu -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_userTags.txt: -------------------------------------------------------------------------------- 1 | 2599253 opengeodb 2 | 6255065 http://de.wikipedia.org/wiki/Gotthardgebäude 3 | 6941058 lyžařské 4 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_geonames_ext.txt: -------------------------------------------------------------------------------- 1 | 1136469 Khōst 33.33951 69.92041 2 | 3865840 Añatuya -28.46064 -62.83472 3 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/countryInfo_gzip_compat.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobThree/NGeoNames/HEAD/NGeoNamesTests/Testdata/countryInfo_gzip_compat.txt.gz -------------------------------------------------------------------------------- /NGeoNames/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_admin2Codes.txt: -------------------------------------------------------------------------------- 1 | AF.01.7052666 Darwāz-e Bālā Darwaz-e Bala 7052666 2 | CA.10.11 Gaspésie-Îles-de-la-Madeleine Gaspesie-Iles-de-la-Madeleine 0 -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/countryInfo_not_gzip_compat.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobThree/NGeoNames/HEAD/NGeoNamesTests/Testdata/countryInfo_not_gzip_compat.txt.gz -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_featureCodes_en.txt: -------------------------------------------------------------------------------- 1 | A.ADM1 first-order administrative division a primary administrative division of a country, such as a state in the United States 2 | XXX Test 3 | null not available -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_admin1CodesASCII.txt: -------------------------------------------------------------------------------- 1 | CF.04 Mambéré-Kadéï Mambere-Kadei 2386161 2 | This.is.a.VERY.LONG.ID Iğdır Igdir 0 3 | TR.80 Şırnak Sirnak 443189 4 | UA.26 Zaporiz’ka Oblast’ Zaporiz'ka Oblast' 687699 -------------------------------------------------------------------------------- /NGeoNames/Resources/continentCodes.txt: -------------------------------------------------------------------------------- 1 | #See http://download.geonames.org/export/dump/readme.txt 2 | AF Africa 6255146 3 | AS Asia 6255147 4 | EU Europe 6255148 5 | NA North America 6255149 6 | OC Oceania 6255151 7 | SA South America 6255150 8 | AN Antarctica 6255152 -------------------------------------------------------------------------------- /NGeoNames/Resources/featureClasses_en.txt: -------------------------------------------------------------------------------- 1 | #See http://www.geonames.org/export/codes.html 2 | A country, state, region, ... 3 | H stream, lake, ... 4 | L parks, area, ... 5 | P city, village, ... 6 | R road, railroad 7 | S spot, building, farm 8 | T mountain, hill, rock, ... 9 | U undersea 10 | V forest, heath, ... -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_alternateNamesV2.txt: -------------------------------------------------------------------------------- 1 | 12453592 7258581 Rossmoor CDP 1 19 February 2008 2 | 9528154 472757 en Volgograd 1 1961 3 | 13653740 11853854 ku قولەی کانی ماران 1 1 قولەی کانی ماران Qulai Kanimaran 4 | 13653663 6955096 es Comitán de Domínguez 19150903 5 | 13634199 472757 en Tsaritsyn 1 1589 1925 -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_timeZones.txt: -------------------------------------------------------------------------------- 1 | CountryCode TimeZoneId GMT offset 1. Jan 2014 DST offset 1. Jul 2014 rawOffset (independant of DST) 2 | MR Africa/Nouakchott 0.0 0.0 0.0 3 | US America/Adak -10.0 -9.0 -10.0 4 | AU Australia/Darwin 9.5 9.5 9.5 5 | AU Australia/Eucla 8.75 8.75 8.75 6 | TZ Africa/Dar_es_Salaam 3.0 3.0 3.0 -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_geonames.txt: -------------------------------------------------------------------------------- 1 | 1136469 Khōst Khost Khost,Khowst,Khōst,Matun,Matūn,khwst,mtwn,Хост,خوست,متون 33.33951 69.92041 P PPLA AF 37 96123 1183 Asia/Kabul 2012-01-16 2 | 3865840 Añatuya Anatuya Anatuya,Añatuya,Unatuya,Uñatuya -28.46064 -62.83472 P PPLA2 AR 22 20261 111 America/Argentina/Cordoba 2014-03-07 3 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_postalCodes.txt: -------------------------------------------------------------------------------- 1 | AU 0200 Australian National University Australian Capital Territory ACT CANBERRA 2 | CZ 561 13 Orlické Podhůří-Rozsocha x) Ústí nad Orlicí 3611 50.0333 16.2833 3 | RU 216270 Пржевальское Смоленская Область ДЕМИДОВСКИЙ РАЙОН 55.5075 31.85 4 4 | CH 2023 Gorgier Canton de Neuchâtel NE District de Boudry 2401 Gorgier 6410 46.9211 6.7559 6 -------------------------------------------------------------------------------- /DumpTester/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_geofilereadercustom1.txt: -------------------------------------------------------------------------------- 1 | This line will be skipped regardless the missing hashtag at the beginning of the line because this test will be run with skiplines = 1 2 | #comment line will be skipped as wel because this test wil be run with hascomments = true 3 | fields,in,this,test,are,comma-separated,instead,of,tab-separated 4 | 5 | #the above empty line should have been skipped as well 6 | ,,,,,,,, 7 | #the above line should result in 9 empty data fields 8 | #the entire file should result in 2 records parsed -------------------------------------------------------------------------------- /NGeoNames/Entities/IGeoLocation.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Defines a generalized interface to define a location by latitude/longitude. 5 | /// 6 | public interface IGeoLocation 7 | { 8 | /// 9 | /// Gets the latitude of the location. 10 | /// 11 | double Latitude { get; set; } 12 | 13 | /// 14 | /// Sets the latitude of the location. 15 | /// 16 | double Longitude { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /NGeoNames/Entities/UserTag.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents information about a geoname.org entity. 5 | /// 6 | public class UserTag 7 | { 8 | /// 9 | /// Gets/sets the geoname database Id the is referring to. 10 | /// 11 | public int GeoNameId { get; set; } 12 | 13 | /// 14 | /// Gets/sets the value of the . 15 | /// 16 | public string Tag { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /NGeoNamesTests/CustomComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Composers; 2 | using NGeoNamesTests; 3 | using System.Text; 4 | 5 | namespace NGeoNames 6 | { 7 | internal class CustomComposer : BaseComposer 8 | { 9 | public CustomComposer(Encoding encoding, char fieldseparator) 10 | { 11 | Encoding = encoding; 12 | FieldSeparator = fieldseparator; 13 | } 14 | 15 | public override string Compose(CustomEntity value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Data); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/ParserException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// The exception that is thrown when parsing an object from supplied data fails. 7 | /// 8 | public class ParserException : Exception 9 | { 10 | /// 11 | /// Initializes a new instance of the class with a specified error message. 12 | /// 13 | /// The message that describes the error. 14 | public ParserException(string message) 15 | : base(message) { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_countryInfo.txt: -------------------------------------------------------------------------------- 1 | # GeoNames.org Country Information TEST FILE 2 | # ========================================== 3 | #ISO ISO3 ISO-Numeric fips Country Capital Area(in sq km) Population Continent tld CurrencyCode CurrencyName Phone Postal Code Format Postal Code Regex Languages geonameid neighbours EquivalentFipsCode 4 | BZ BLZ 084 BH Belize Belmopan 22966 314522 NA .bz BZD Dollar 501 en-BZ,es 3582678 GT,MX 5 | XX XXX 999 MADE UP COUNTRY 0 EU .XX XXX X-Dollar 1 @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA ^(([A-Z]\d{2}[A-Z]{2})|([A-Z]\d{3}[A-Z]{2})|([A-Z]{2}\d{2}[A-Z]{2})|([A-Z]{2}\d{3}[A-Z]{2})|([A-Z]\d[A-Z]\d[A-Z]{2})|([A-Z]{2}\d[A-Z]\d[A-Z]{2})|(GIR0AA))$ XXX -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_countryInfo2.txt: -------------------------------------------------------------------------------- 1 | # GeoNames.org Country Information TEST FILE 2 | # ========================================== 3 | #ISO ISO3 ISO-Numeric fips Country Capital Area(in sq km) Population Continent tld CurrencyCode CurrencyName Phone Postal Code Format Postal Code Regex Languages geonameid neighbours EquivalentFipsCode 4 | BZ BLZ 084 BH Belize Belmopan 22966 314522 NA .bz BZD Dollar +501 en-BZ,es 3582678 GT,MX 5 | XX XXX 999 MADE UP COUNTRY 0 EU .XX XXX X-Dollar +1 @# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA ^(([A-Z]\d{2}[A-Z]{2})|([A-Z]\d{3}[A-Z]{2})|([A-Z]{2}\d{2}[A-Z]{2})|([A-Z]{2}\d{3}[A-Z]{2})|([A-Z]\d[A-Z]\d[A-Z]{2})|([A-Z]{2}\d[A-Z]\d[A-Z]{2})|(GIR0AA))$ XXX -------------------------------------------------------------------------------- /NGeoNames/Entities/Continent.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents a continent. 5 | /// 6 | public class Continent 7 | { 8 | /// 9 | /// Gets/sets the code. 10 | /// 11 | public string Code { get; set; } 12 | 13 | /// 14 | /// Gets/sets the name. 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// Gets/sets the geoname database Id. 20 | /// 21 | public int GeoNameId { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /NGeoNames/Entities/FeatureClass.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents a feature class. 5 | /// 6 | /// See http://www.geonames.org/export/codes.html. 7 | public class FeatureClass 8 | { 9 | /// 10 | /// Gets/sets the class "Id" (or "code) of the . 11 | /// 12 | public string Class { get; set; } 13 | 14 | /// 15 | /// Gets/sets the description of the . 16 | /// 17 | public string Description { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_alternateNames.txt: -------------------------------------------------------------------------------- 1 | 2488123 4 fa رودخانه زاکلی 2 | 8078498 12 link http://en.wikipedia.org/wiki/Takht-e_Qeysar 3 | 3810073 523619 Нагольная 4 | 9189113 1616332 th บ้านน้ำฉ่า 5 | 8196652 2648438 ko 글렌로시스 6 | 7636553 2649571 post TW13 7 | 7481730 2649672 iata FAB 8 | 1886364 2660644 icao LSGG 9 | 5906078 4041549 faac GSN 10 | 5906077 4041549 Saipan International Airport 11 | 2989172 4041549 link http://en.wikipedia.org/wiki/Saipan_International_Airport 12 | 2259865 6559563 fr_1793 Ile-de-la-Liberté 13 | 2197207 6559568 bo ཞིང་རི 14 | 8488823 6615458 abbr TRTO 15 | 2256760 6615468 en The Jekyll & Hyde Pub 1 1 16 | 2299521 6621102 it Torre Fiumicelli 1 17 | 8066371 6639742 fr Abbaye Saint-Antoine-des-Champs 1 -------------------------------------------------------------------------------- /NGeoNames/Composers/UserTagComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class UserTagComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(UserTag value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.GeoNameId, value.Tag); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNames/Composers/ContinentComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class ContinentComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(Continent value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Code, value.Name, value.GeoNameId); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNamesTests/CustomEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | namespace NGeoNamesTests 4 | { 5 | internal class CustomEntity 6 | { 7 | public string[] Data { get; set; } 8 | } 9 | 10 | internal class CustomEntityComparer : IComparer, IComparer 11 | { 12 | public int Compare(CustomEntity x, CustomEntity y) 13 | { 14 | int r = x.Data.Length.CompareTo(y.Data.Length); 15 | if (r != 0) 16 | return r; 17 | 18 | for (int i = 0; i < x.Data.Length && r == 0; i++) 19 | r = x.Data[i].CompareTo(y.Data[i]); 20 | return r; 21 | } 22 | 23 | public int Compare(object x, object y) 24 | { 25 | return Compare(x as CustomEntity, y as CustomEntity); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /NGeoNames/Composers/FeatureClassComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class FeatureClassComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(FeatureClass value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Class, value.Description); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNames/Composers/Admin1CodeComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing an . 7 | /// 8 | public class Admin1CodeComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(Admin1Code value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Code, value.Name, value.NameASCII, value.GeoNameId); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNames/Composers/Admin2CodeComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing an . 7 | /// 8 | public class Admin2CodeComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(Admin2Code value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Code, value.Name, value.NameASCII, value.GeoNameId); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNames/Composers/HierarchyComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class HierarchyComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(HierarchyNode value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.ParentId, value.ChildId, value.Type); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /NGeoNames/Composers/ISOLanguageCodeComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing an . 7 | /// 8 | public class ISOLanguageCodeComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(ISOLanguageCode value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.ISO_639_3, value.ISO_639_2, value.ISO_639_1, value.LanguageName); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /NGeoNames/Composers/TimeZoneComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class TimeZoneComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(TimeZone value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.CountryCode, TimeZoneToString(value.TimeZoneId), FloatToString(value.GMTOffset), 18 | FloatToString(value.DSTOffset), FloatToString(value.RawOffset)); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /NGeoNames/Entities/GeoName.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents a ("compact") GeoName record; this class also serves as baseclass for . 5 | /// 6 | /// 7 | public class GeoName : IGeoLocation 8 | { 9 | /// 10 | /// Gets/sets the record-ID in the geonames database. 11 | /// 12 | public int Id { get; set; } 13 | 14 | /// 15 | /// Gets/sets the name of geographical point. 16 | /// 17 | public string Name { get; set; } 18 | 19 | /// 20 | /// Gets/sets the latitude in decimal degrees. 21 | /// 22 | public double Latitude { get; set; } 23 | 24 | /// 25 | /// Gets/sets the longitude in decimal degrees. 26 | /// 27 | public double Longitude { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NGeoNames/Entities/FeatureCode.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents a feature code. 5 | /// 6 | /// See http://www.geonames.org/export/codes.html. 7 | public class FeatureCode 8 | { 9 | /// 10 | /// Gets/sets the this belongs to. 11 | /// 12 | public string Class { get; set; } 13 | 14 | /// 15 | /// Gets/sets the code of the . 16 | /// 17 | public string Code { get; set; } 18 | 19 | /// 20 | /// Gets/sets the name of the . 21 | /// 22 | public string Name { get; set; } 23 | 24 | /// 25 | /// Gets/sets the description of the . 26 | /// 27 | public string Description { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NGeoNames/Entities/HierarchyNode.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents information about the hierarchy of an geoname.org entity. 5 | /// 6 | public class HierarchyNode 7 | { 8 | /// 9 | /// Gets/sets the entity's parent geoname database Id. 10 | /// 11 | public int ParentId { get; set; } 12 | 13 | /// 14 | /// Gets/sets the entity's child geoname database Id. 15 | /// 16 | public int ChildId { get; set; } 17 | 18 | /// 19 | /// Gets/sets the type. 20 | /// 21 | /// 22 | /// The type 'ADM' stands for the admin hierarchy modeled by the admin1-4 codes. The other entries are entered 23 | /// with the geonames.org user interface. The relation toponym-adm hierarchy is not included in the file, it 24 | /// can instead be built from the admincodes of the toponym. 25 | /// 26 | public string Type { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /NGeoNames/Composers/IComposer.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Defines a generalized composer to compose geoname.org files of a specific type. 7 | /// 8 | /// The type of objects to compose. 9 | public interface IComposer 10 | { 11 | /// 12 | /// Gets the to use when writing/composing the file/stream. 13 | /// 14 | Encoding Encoding { get; } 15 | 16 | /// 17 | /// Gets the field separator to use when writing/composing the file/stream. 18 | /// 19 | char FieldSeparator { get; } 20 | 21 | /// 22 | /// Composes a string representing an object of type T to be used in geoname.org files. 23 | /// 24 | /// An object that represents the value to be written. 25 | /// A string representing an object of type T to be used in geoname.org files. 26 | string Compose(T value); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rob Janssen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /NGeoNames/Entities/Admin1Code.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents an administrative division. 5 | /// 6 | public class Admin1Code 7 | { 8 | /// 9 | /// Gets/sets the code of the administrative division. 10 | /// 11 | public string Code { get; set; } 12 | 13 | /// 14 | /// Gets/sets the name of the administrative division. 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// Gets/sets the name of the administrative division in plain ASCII. 20 | /// 21 | /// 22 | /// Non-ASCII values have been found in the data; it is unfortunately (currently) *NOT* guaranteed that this 23 | /// property contains ASCII-only strings. 24 | /// 25 | public string NameASCII { get; set; } 26 | 27 | /// 28 | /// Gets/sets the geoname database Id of the administrative division. 29 | /// 30 | public int GeoNameId { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /NGeoNamesTests/CustomParser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames.Parsers; 3 | using System.Text; 4 | 5 | namespace NGeoNamesTests 6 | { 7 | internal class CustomParser : IParser 8 | { 9 | public bool HasComments { get; private set; } 10 | public int SkipLines { get; private set; } 11 | public int ExpectedNumberOfFields { get; private set; } 12 | public Encoding Encoding { get; private set; } 13 | public char[] FieldSeparators { get; private set; } 14 | 15 | public CustomParser(int expectedfields, int skiplines, char[] fieldseparators, Encoding encoding, bool hascomments) 16 | { 17 | SkipLines = skiplines; 18 | ExpectedNumberOfFields = expectedfields; 19 | FieldSeparators = fieldseparators; 20 | Encoding = encoding; 21 | HasComments = hascomments; 22 | } 23 | 24 | public CustomEntity Parse(string[] fields) 25 | { 26 | Assert.AreEqual(ExpectedNumberOfFields, fields.Length); 27 | return new CustomEntity { Data = fields }; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /NGeoNames/Entities/Admin2Code.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents an administrative subdivision. 5 | /// 6 | public class Admin2Code 7 | { 8 | /// 9 | /// Gets/sets the code of the administrative subdivision. 10 | /// 11 | public string Code { get; set; } 12 | 13 | /// 14 | /// Gets/sets the name of the administrative subdivision. 15 | /// 16 | public string Name { get; set; } 17 | 18 | /// 19 | /// Gets/sets the name of the administrative subdivision in plain ASCII. 20 | /// 21 | /// 22 | /// Non-ASCII values have been found in the data; it is unfortunately (currently) *NOT* guaranteed that this 23 | /// property contains ASCII-only strings. 24 | /// 25 | public string NameASCII { get; set; } 26 | 27 | 28 | /// 29 | /// Gets/sets the geoname database Id of the administrative subdivision. 30 | /// 31 | public int GeoNameId { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NGeoNames/Composers/CountryInfoComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class CountryInfoComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(CountryInfo value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.ISO_Alpha2, value.ISO_Alpha3, value.ISO_Numeric, value.FIPS, value.Country, 18 | value.Capital, value.Area, value.Population, value.Continent, value.Tld, value.CurrencyCode, value.CurrencyName, value.Phone, 19 | value.PostalCodeFormat, value.PostalCodeRegex, ArrayToValue(value.Languages), value.GeoNameId, ArrayToValue(value.Neighbours), value.EquivalentFipsCode); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /NGeoNames/Composers/AlternateNameComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing an . 7 | /// 8 | public class AlternateNameComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(AlternateName value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Id, value.GeoNameId, string.IsNullOrEmpty(value.Type) ? value.ISOLanguage : value.Type, 18 | value.Name, Bool2String(value.IsPreferredName), Bool2String(value.IsShortName), Bool2String(value.IsColloquial), Bool2String(value.IsHistoric)); 19 | } 20 | 21 | private static string Bool2String(bool value) 22 | { 23 | return value ? "1" : string.Empty; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /NGeoNames/Composers/PostalcodeComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class PostalcodeComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(Postalcode value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.CountryCode, value.PostalCode, value.PlaceName, 18 | GetArrayValue(value.AdminName, 0), GetArrayValue(value.AdminCode, 0), 19 | GetArrayValue(value.AdminName, 1), GetArrayValue(value.AdminCode, 1), 20 | GetArrayValue(value.AdminName, 2), GetArrayValue(value.AdminCode, 2), 21 | DoubleToString(value.Latitude), DoubleToString(value.Longitude), value.Accuracy 22 | ); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /NGeoNames/Entities/ISOLanguageCode.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents a language. 5 | /// 6 | public class ISOLanguageCode 7 | { 8 | /// 9 | /// Gets/sets the ISO-639 Alpha-3 code for comprehensive coverage of languages. 10 | /// 11 | /// See https://en.wikipedia.org/wiki/ISO_639-3. 12 | public string ISO_639_3 { get; set; } 13 | 14 | /// 15 | /// Gets/sets the ISO-639 Alpha-3 code. 16 | /// 17 | /// See https://en.wikipedia.org/wiki/ISO_639-2. 18 | public string ISO_639_2 { get; set; } 19 | 20 | /// 21 | /// Gets/sets the ISO-639 Alpha-2 code. 22 | /// 23 | /// See https://en.wikipedia.org/wiki/ISO_639-1. 24 | public string ISO_639_1 { get; set; } 25 | 26 | /// 27 | /// Gets/sets the name of the language. 28 | /// 29 | public string LanguageName { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /NGeoNames/Composers/AlternateNameV2Composer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing an . 7 | /// 8 | public class AlternateNameV2Composer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(AlternateNameV2 value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), value.Id, value.GeoNameId, string.IsNullOrEmpty(value.Type) ? value.ISOLanguage : value.Type, 18 | value.Name, Bool2String(value.IsPreferredName), Bool2String(value.IsShortName), Bool2String(value.IsColloquial), Bool2String(value.IsHistoric), 19 | value.From, value.To); 20 | } 21 | 22 | private static string Bool2String(bool value) 23 | { 24 | return value ? "1" : string.Empty; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /NGeoNamesTests/GeoUtilTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames; 3 | 4 | namespace NGeoNamesTests 5 | { 6 | 7 | [TestClass] 8 | public class GeoUtilTests 9 | { 10 | [TestMethod] 11 | public void GeoUtil_ConvertsMilesToMetersCorrecly() 12 | { 13 | var target = GeoUtil.MilesToMeters(200); //200 mile to meters 14 | Assert.AreEqual(321868.8, target, float.Epsilon); 15 | } 16 | 17 | 18 | [TestMethod] 19 | public void GeoUtil_ConvertsMetersToMilesCorrecly() 20 | { 21 | var target = GeoUtil.MetersToMiles(500); //500 meters to miles 22 | Assert.AreEqual(0.310685596118667, target, float.Epsilon); 23 | } 24 | 25 | [TestMethod] 26 | public void GeoUtil_ConvertsYardsToMetersCorrecly() 27 | { 28 | var target = GeoUtil.YardsToMeters(200); //200 yards to meters 29 | Assert.AreEqual(182.88, target, float.Epsilon); 30 | } 31 | 32 | 33 | [TestMethod] 34 | public void GeoUtil_ConvertsMetersToYardsCorrecly() 35 | { 36 | var target = GeoUtil.MetersToYards(500); //500 meters to yards 37 | Assert.AreEqual(546.80664916885394, target, float.Epsilon); 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /NGeoNames/Composers/FeatureCodeComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class FeatureCodeComposer : BaseComposer 9 | { 10 | /// 11 | /// Composes the specified into a string. 12 | /// 13 | /// The to be composed into a string. 14 | /// A string representing the specified . 15 | public override string Compose(FeatureCode value) 16 | { 17 | return string.Join(FieldSeparator.ToString(), GetFeatureCodeString(value), value.Name, value.Description); 18 | } 19 | 20 | private string GetFeatureCodeString(FeatureCode value) 21 | { 22 | if (!string.IsNullOrEmpty(value.Class) && (!string.IsNullOrEmpty(value.Code))) 23 | return string.Format("{0}.{1}", value.Class, value.Code); 24 | if (!string.IsNullOrEmpty(value.Class)) 25 | return value.Class; 26 | if (!string.IsNullOrEmpty(value.Code)) 27 | return value.Code; 28 | return "null"; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /NGeoNames/Entities/TimeZone.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents a timezone. 5 | /// 6 | public class TimeZone 7 | { 8 | /// 9 | /// Gets/sets the ISO-3166 2-letter country code of the . 10 | /// 11 | public string CountryCode { get; set; } 12 | 13 | /// 14 | /// Gets/sets the timezone "id" of the . 15 | /// 16 | /// 17 | /// Note that the data contains underscores ("_") for spaces but that these are replaced with actual spaces 18 | /// when reading data and that these spaces get replace with underscores again when writing data. 19 | /// 20 | public string TimeZoneId { get; set; } 21 | 22 | /// 23 | /// Gets/sets the GMT ofsset (reference date Jan. 1st) of the . 24 | /// 25 | public float GMTOffset { get; set; } 26 | 27 | /// 28 | /// Gets/sets the DST ofsset (reference date Jul. 1st) of the . 29 | /// 30 | public float DSTOffset { get; set; } 31 | 32 | /// 33 | /// Gets/sets the raw offset (independant of DST) of the . 34 | /// 35 | public float RawOffset { get; set; } 36 | } 37 | } -------------------------------------------------------------------------------- /NGeoNames/FileType.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | namespace NGeoNames 3 | { 4 | /// 5 | /// Specifies constants that define known (or 'supported') file types. 6 | /// 7 | public enum FileType 8 | { 9 | /// Auto detect the file type; uses the file's extension to determine filetype. 10 | AutoDetect = 0, 11 | /// The file is a plain textfile. 12 | Plain = 1, 13 | /// The file is a GZipped textfile. 14 | GZip = 2 15 | } 16 | 17 | /// 18 | /// Internal utility class. 19 | /// 20 | internal static class FileUtil 21 | { 22 | /// 23 | /// Returns the of a file by looking at the file-extension. 24 | /// 25 | /// The path of the file. 26 | /// The of the file 27 | public static FileType GetFileTypeFromExtension(string path) 28 | { 29 | var fi = new FileInfo(path); 30 | switch (fi.Extension.ToLowerInvariant()) 31 | { 32 | case ".txt": 33 | return FileType.Plain; 34 | case ".gz": 35 | return FileType.GZip; 36 | } 37 | throw new System.NotSupportedException("Unable to detect filetype from file extension"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /NGeoNames/Composers/ExtendedGeoNameComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System.Globalization; 3 | 4 | namespace NGeoNames.Composers 5 | { 6 | /// 7 | /// Provides methods for composing a string representing an . 8 | /// 9 | public class ExtendedGeoNameComposer : BaseComposer 10 | { 11 | /// 12 | /// Composes the specified into a string. 13 | /// 14 | /// The to be composed into a string. 15 | /// A string representing the specified . 16 | public override string Compose(ExtendedGeoName value) 17 | { 18 | return string.Join(FieldSeparator.ToString(), value.Id, value.Name, value.NameASCII, ArrayToValue(value.AlternateNames), 19 | value.Latitude.ToString(CultureInfo.InvariantCulture), value.Longitude.ToString(CultureInfo.InvariantCulture), value.FeatureClass, value.FeatureCode, value.CountryCode, 20 | ArrayToValue(value.AlternateCountryCodes), GetArrayValue(value.Admincodes, 0), GetArrayValue(value.Admincodes, 1), GetArrayValue(value.Admincodes, 2), GetArrayValue(value.Admincodes, 3), 21 | value.Population, value.Elevation, value.Dem, TimeZoneToString(value.Timezone), DateTimeToString(value.ModificationDate)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DumpTester/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("DumpTester")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("DumpTester")] 12 | [assembly: AssemblyCopyright("Copyright © 2014")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("6f04755e-2ccd-4f5d-9a0f-7622addb9391")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /NGeoNamesTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("NGeoNamesTests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("NGeoNamesTests")] 12 | [assembly: AssemblyCopyright("Copyright © 2014")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("d19c7259-28ac-4730-a2dd-4a75f24fded9")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/IParser.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | namespace NGeoNames.Parsers 3 | { 4 | /// 5 | /// Defines a generalized parser to parse geoname.org files of a specific type. 6 | /// 7 | /// The type of objects to parse. 8 | public interface IParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | bool HasComments { get; } 14 | 15 | /// 16 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 17 | /// 18 | int SkipLines { get; } 19 | 20 | /// 21 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 22 | /// 23 | int ExpectedNumberOfFields { get; } 24 | 25 | /// 26 | /// Gets the to use when reading/parsing the file/stream. 27 | /// 28 | Encoding Encoding { get; } 29 | 30 | /// 31 | /// Gets an array of chars that define one or more fieldseparators. 32 | /// 33 | char[] FieldSeparators { get; } 34 | 35 | /// 36 | /// Parses the specified fields into an object of type T. 37 | /// 38 | /// The fields to be parsed. 39 | /// An object of type T parsed from the file/stream. 40 | T Parse(string[] fields); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NGeoNames/Entities/AlternateNameV2.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents an alternate name for a specific or . This entity 5 | /// represents a newer version of the type, with new From and To properties that describe 6 | /// the historical usage of the name. 7 | /// 8 | /// 9 | /// The property of the object is a short 10 | /// version of the data without links and postal codes but with ASCUU transliterations. 11 | /// 12 | public class AlternateNameV2 : AlternateName 13 | { 14 | /// 15 | /// Gets/sets the start date for the historical period during which this name was used. 16 | /// 17 | /// 18 | /// Note that this property is a string and may contain all sorts of 'data' like "1938-10-03", 19 | /// "19 February 2008", "201806", "397", "قولەی کانی ماران" and even links to wikipedia. 20 | /// 21 | public string From { get; set; } 22 | 23 | /// 24 | /// Gets/sets the end date for the historical period during which this name was used. 25 | /// 26 | /// 27 | /// Note that this property is a string and may contain all sorts of 'data' like "1938-10-03", 28 | /// "19 February 2008", "201806", "397", "قولەی کانی ماران" and even links to wikipedia. 29 | /// 30 | public string To { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /NGeoNames/Parsers/UserTagParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing a object from a string-array. 7 | /// 8 | public class UserTagParser : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return false; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 2; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into a object. 36 | /// 37 | /// The fields/data representing a to parse. 38 | /// Returns a new object. 39 | public override UserTag Parse(string[] fields) 40 | { 41 | return new UserTag 42 | { 43 | GeoNameId = StringToInt(fields[0]), 44 | Tag = fields[1] 45 | }; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/ContinentParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing a object from a string-array. 7 | /// 8 | public class ContinentParser : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return true; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 3; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into a object. 36 | /// 37 | /// The fields/data representing a to parse. 38 | /// Returns a new object. 39 | public override Continent Parse(string[] fields) 40 | { 41 | return new Continent 42 | { 43 | Code = fields[0], 44 | Name = fields[1], 45 | GeoNameId = StringToInt(fields[2]) 46 | }; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /NGeoNames/NGeoNames.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net45;net451;net452;net46;net461;net462;net47;net471;netstandard2.0;netcoreapp2.0 5 | RobIII 6 | RobIII 7 | NGeoNames 8 | true 9 | NGeoNames 10 | (C) 2014 - 2018 Devcorner.nl 11 | true 12 | https://raw.githubusercontent.com/RobThree/NGeoNames/master/LICENSE 13 | https://github.com/RobThree/NGeoNames 14 | reverse-geocoding geocoding geocode reverse-geocode geocoder reverse-geocoder maps 15 | * Add support for AlternateNamesV2 16 | Provides classes and methods for downloading, reading/parsing and utilizing files from GeoNames.org dumps. 17 | https://raw.githubusercontent.com/RobThree/NGeoNames/master/icon.png 18 | 1.5.1 19 | 20 | 21 | 22 | TRACE;RELEASE;NETSTANDARD2_0 23 | 24 | 25 | 26 | bin\Release\net45\NGeoNames.xml 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/HierarchyParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing a object from a string-array. 7 | /// 8 | public class HierarchyParser : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return false; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 3; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into a object. 36 | /// 37 | /// The fields/data representing a to parse. 38 | /// Returns a new object. 39 | public override HierarchyNode Parse(string[] fields) 40 | { 41 | return new HierarchyNode 42 | { 43 | ParentId = StringToInt(fields[0]), 44 | ChildId = StringToInt(fields[1]), 45 | Type = fields[2] 46 | }; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/Admin1CodeParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing an object from a string-array. 7 | /// 8 | public class Admin1CodeParser : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return false; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 4; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into an object. 36 | /// 37 | /// The fields/data representing an to parse. 38 | /// Returns a new object. 39 | public override Admin1Code Parse(string[] fields) 40 | { 41 | return new Admin1Code 42 | { 43 | Code = fields[0], 44 | Name = fields[1], 45 | NameASCII = fields[2], 46 | GeoNameId = StringToInt(fields[3]) 47 | }; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/Admin2CodeParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing an object from a string-array. 7 | /// 8 | public class Admin2CodeParser : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return false; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 4; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into an object. 36 | /// 37 | /// The fields/data representing an to parse. 38 | /// Returns a new object. 39 | public override Admin2Code Parse(string[] fields) 40 | { 41 | return new Admin2Code 42 | { 43 | Code = fields[0], 44 | Name = fields[1], 45 | NameASCII = fields[2], 46 | GeoNameId = StringToInt(fields[3]) 47 | }; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/FeatureClassParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing a object from a string-array. 7 | /// 8 | public class FeatureClassParser : BaseParser 9 | { 10 | 11 | private static readonly char[] fcodesep = new[] { '.' }; 12 | 13 | /// 14 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 15 | /// 16 | public override bool HasComments 17 | { 18 | get { return true; } 19 | } 20 | 21 | /// 22 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 23 | /// 24 | public override int SkipLines 25 | { 26 | get { return 0; } 27 | } 28 | 29 | /// 30 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 31 | /// 32 | public override int ExpectedNumberOfFields 33 | { 34 | get { return 2; } 35 | } 36 | 37 | /// 38 | /// Parses the specified data into a object. 39 | /// 40 | /// The fields/data representing a to parse. 41 | /// Returns a new object. 42 | public override FeatureClass Parse(string[] fields) 43 | { 44 | return new FeatureClass 45 | { 46 | Class = fields[0], 47 | Description = fields[1] 48 | }; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/ISOLanguageCodeParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | namespace NGeoNames.Parsers 3 | { 4 | /// 5 | /// Provides methods for parsing an object from a string-array. 6 | /// 7 | public class ISOLanguageCodeParser : BaseParser 8 | { 9 | /// 10 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 11 | /// 12 | public override bool HasComments 13 | { 14 | get { return false; } 15 | } 16 | 17 | /// 18 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 19 | /// 20 | public override int SkipLines 21 | { 22 | get { return 1; } 23 | } 24 | 25 | /// 26 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 27 | /// 28 | public override int ExpectedNumberOfFields 29 | { 30 | get { return 4; } 31 | } 32 | 33 | /// 34 | /// Parses the specified data into an object. 35 | /// 36 | /// The fields/data representing an to parse. 37 | /// Returns a new object. 38 | public override ISOLanguageCode Parse(string[] fields) 39 | { 40 | return new ISOLanguageCode 41 | { 42 | ISO_639_3 = fields[0], 43 | ISO_639_2 = fields[1], 44 | ISO_639_1 = fields[2], 45 | LanguageName = fields[3] 46 | }; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/FeatureCodeParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System; 3 | 4 | namespace NGeoNames.Parsers 5 | { 6 | /// 7 | /// Provides methods for parsing a object from a string-array. 8 | /// 9 | public class FeatureCodeParser : BaseParser 10 | { 11 | 12 | private static readonly char[] fcodesep = new[] { '.' }; 13 | 14 | /// 15 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 16 | /// 17 | public override bool HasComments => false; 18 | 19 | /// 20 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 21 | /// 22 | public override int SkipLines => 0; 23 | 24 | /// 25 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 26 | /// 27 | public override int ExpectedNumberOfFields => 3; 28 | 29 | /// 30 | /// Parses the specified data into a object. 31 | /// 32 | /// The fields/data representing a to parse. 33 | /// Returns a new object. 34 | public override FeatureCode Parse(string[] fields) 35 | { 36 | var d = StringToArray(fields[0], fcodesep); 37 | return new FeatureCode 38 | { 39 | Class = d[0].Equals("null", StringComparison.OrdinalIgnoreCase) ? null : d[0], 40 | Code = d.Length == 2 ? d[1] : null, 41 | Name = fields[1], 42 | Description = fields[2] 43 | }; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/TimeZoneParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System.Globalization; 3 | 4 | namespace NGeoNames.Parsers 5 | { 6 | /// 7 | /// Provides methods for parsing a object from a string-array. 8 | /// 9 | public class TimeZoneParser : BaseParser 10 | { 11 | /// 12 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 13 | /// 14 | public override bool HasComments 15 | { 16 | get { return false; } 17 | } 18 | 19 | /// 20 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 21 | /// 22 | public override int SkipLines 23 | { 24 | get { return 1; } 25 | } 26 | 27 | /// 28 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 29 | /// 30 | public override int ExpectedNumberOfFields 31 | { 32 | get { return 5; } 33 | } 34 | 35 | /// 36 | /// Parses the specified data into a object. 37 | /// 38 | /// The fields/data representing a to parse. 39 | /// Returns a new object. 40 | public override TimeZone Parse(string[] fields) 41 | { 42 | return new TimeZone 43 | { 44 | CountryCode = fields[0], 45 | TimeZoneId = StringToTimeZone(fields[1]), 46 | GMTOffset = StringToFloat(fields[2]), 47 | DSTOffset = StringToFloat(fields[3]), 48 | RawOffset = StringToFloat(fields[4]) 49 | }; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/PostalcodeParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System.Globalization; 3 | 4 | namespace NGeoNames.Parsers 5 | { 6 | /// 7 | /// Provides methods for parsing a object from a string-array. 8 | /// 9 | public class PostalcodeParser : BaseParser 10 | { 11 | /// 12 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 13 | /// 14 | public override bool HasComments 15 | { 16 | get { return false; } 17 | } 18 | 19 | /// 20 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 21 | /// 22 | public override int SkipLines 23 | { 24 | get { return 0; } 25 | } 26 | 27 | /// 28 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 29 | /// 30 | public override int ExpectedNumberOfFields 31 | { 32 | get { return 12; } 33 | } 34 | 35 | /// 36 | /// Parses the specified data into a object. 37 | /// 38 | /// The fields/data representing a to parse. 39 | /// Returns a new object. 40 | public override Postalcode Parse(string[] fields) 41 | { 42 | return new Postalcode 43 | { 44 | CountryCode = fields[0], 45 | PostalCode = fields[1], 46 | PlaceName = fields[2], 47 | AdminName = new[] { fields[3], fields[5], fields[7] }, 48 | AdminCode = new[] { fields[4], fields[6], fields[8] }, 49 | Latitude = fields[9].Length > 0 ? StringToDouble(fields[9]) : double.NaN, 50 | Longitude = fields[10].Length > 0 ? StringToDouble(fields[10]) : double.NaN, 51 | Accuracy = fields[11].Length > 0 ? (int?)StringToInt(fields[11]) : null 52 | }; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /NGeoNames/Entities/Postalcode.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents an postal code. 5 | /// 6 | public class Postalcode : IGeoLocation 7 | { 8 | /// 9 | /// Gets/sets the ISO-3166 2-letter country code of the . 10 | /// 11 | public string CountryCode { get; set; } 12 | 13 | /// 14 | /// Gets/sets the postal code. 15 | /// 16 | public string PostalCode { get; set; } 17 | 18 | /// 19 | /// Gets/sets the name of the place. 20 | /// 21 | public string PlaceName { get; set; } 22 | 23 | /// 24 | /// Gets/sets 1 up to 3 admin names (indexed 0 to 2) of the . 25 | /// 26 | /// AdminName[0]: 1st order subdivision (state). 27 | /// AdminName[1]: 2nd order subdivision (county/province). 28 | /// AdminName[2]: 3rd order subdivision (community). 29 | /// 30 | /// 31 | public string[] AdminName { get; set; } 32 | 33 | /// 34 | /// Gets/sets 1 up to 3 admin codes (indexed 0 to 2) of the . 35 | /// 36 | /// AdminCode[0]: 1st order subdivision (state). 37 | /// AdminCode[1]: 2nd order subdivision (county/province). 38 | /// AdminCode[2]: 3rd order subdivision (community). 39 | /// 40 | /// 41 | public string[] AdminCode { get; set; } 42 | 43 | /// 44 | /// Gets/sets the latitude in decimal degrees. 45 | /// 46 | public double Latitude { get; set; } 47 | 48 | /// 49 | /// Gets/sets the longitude in decimal degrees. 50 | /// 51 | public double Longitude { get; set; } 52 | 53 | /// 54 | /// Gets/sets the accuracy of / from 1 = estimated to 6 = centroid. 55 | /// 56 | public int? Accuracy { get; set; } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/AlternateNameParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing an object from a string-array. 7 | /// 8 | public class AlternateNameParser : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return false; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 8; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into an object. 36 | /// 37 | /// The fields/data representing an to parse. 38 | /// Returns a new object. 39 | public override AlternateName Parse(string[] fields) 40 | { 41 | return new AlternateName 42 | { 43 | Id = StringToInt(fields[0]), 44 | GeoNameId = StringToInt(fields[1]), 45 | ISOLanguage = fields[2].Length <= 3 ? fields[2] : null, 46 | Type = fields[2].Length <= 3 ? null : fields[2], 47 | Name = fields[3], 48 | IsPreferredName = BoolToString(fields[4]), 49 | IsShortName = BoolToString(fields[5]), 50 | IsColloquial = BoolToString(fields[6]), 51 | IsHistoric = BoolToString(fields[7]) 52 | }; 53 | } 54 | 55 | private static bool BoolToString(string value) 56 | { 57 | return value.Equals("1"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/AlternateNameParserV2.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Parsers 4 | { 5 | /// 6 | /// Provides methods for parsing an object from a string-array. 7 | /// 8 | public class AlternateNameParserV2 : BaseParser 9 | { 10 | /// 11 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 12 | /// 13 | public override bool HasComments 14 | { 15 | get { return false; } 16 | } 17 | 18 | /// 19 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 20 | /// 21 | public override int SkipLines 22 | { 23 | get { return 0; } 24 | } 25 | 26 | /// 27 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 28 | /// 29 | public override int ExpectedNumberOfFields 30 | { 31 | get { return 10; } 32 | } 33 | 34 | /// 35 | /// Parses the specified data into an object. 36 | /// 37 | /// The fields/data representing an to parse. 38 | /// Returns a new object. 39 | public override AlternateNameV2 Parse(string[] fields) 40 | { 41 | return new AlternateNameV2 42 | { 43 | Id = StringToInt(fields[0]), 44 | GeoNameId = StringToInt(fields[1]), 45 | ISOLanguage = fields[2].Length <= 3 ? fields[2] : null, 46 | Type = fields[2].Length <= 3 ? null : fields[2], 47 | Name = fields[3], 48 | IsPreferredName = BoolToString(fields[4]), 49 | IsShortName = BoolToString(fields[5]), 50 | IsColloquial = BoolToString(fields[6]), 51 | IsHistoric = BoolToString(fields[7]), 52 | From = fields[8], 53 | To = fields[9] 54 | }; 55 | } 56 | 57 | private static bool BoolToString(string value) 58 | { 59 | return value.Equals("1"); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /NGeoNamesTests/GeoFileReaderTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames; 3 | using System; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace NGeoNamesTests 8 | { 9 | [TestClass] 10 | public class GeoFileReaderTests 11 | { 12 | [TestMethod] 13 | public void GeoFileReader_ParsesFileCorrectly1() 14 | { 15 | var gf = new GeoFileReader(); 16 | var target = gf.ReadRecords(@"testdata\test_geofilereadercustom1.txt", new CustomParser(9, 1, new[] { ',' }, Encoding.UTF8, true)).ToArray(); 17 | Assert.AreEqual(2, target.Length); 18 | } 19 | 20 | [TestMethod] 21 | public void GeoFileReader_ParsesFileCorrectly2() 22 | { 23 | var gf = new GeoFileReader(); 24 | var target = gf.ReadRecords(@"testdata\test_geofilereadercustom2.txt", new CustomParser(4, 0, new[] { '!' }, Encoding.UTF8, false)).ToArray(); 25 | Assert.AreEqual(3, target.Length); 26 | } 27 | 28 | [TestMethod] 29 | [ExpectedException(typeof(NotSupportedException))] 30 | public void GeoFileReader_ThrowsOnFailureWhenAutodetectingFileType() 31 | { 32 | //When filetype == autodetect and an unknown extension is used an exception should be thrown 33 | var gf = new GeoFileReader(); 34 | var target = gf.ReadRecords(@"testdata\invalid.ext", new CustomParser(5, 0, new[] { '\t' }, Encoding.UTF8, false)).ToArray(); 35 | } 36 | 37 | [TestMethod] 38 | public void GeoFileReader_DoesNotThrowOnInvalidExtensionButSpecifiedFileType() 39 | { 40 | //When filetype is specified and an unknown extension is used it should be read fine 41 | var gf = new GeoFileReader(); 42 | var target = gf.ReadRecords(@"testdata\invalid.ext", FileType.Plain, new CustomParser(5, 0, new[] { '\t' }, Encoding.UTF8, false)).ToArray(); 43 | } 44 | 45 | [TestMethod] 46 | [ExpectedException(typeof(NotSupportedException))] 47 | public void GeoFileReader_ThrowsOnUnknownSpecifiedFileType() 48 | { 49 | //When and unknown filetype is specified an exception should be thrown 50 | var gf = new GeoFileReader(); 51 | var target = gf.ReadRecords(@"testdata\invalid.ext", (FileType)999, new CustomParser(5, 0, new[] { '\t' }, Encoding.UTF8, false)).ToArray(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/ExtendedGeoNameParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System; 3 | using System.Globalization; 4 | 5 | namespace NGeoNames.Parsers 6 | { 7 | /// 8 | /// Provides methods for parsing an object from a string-array. 9 | /// 10 | public class ExtendedGeoNameParser : BaseParser 11 | { 12 | /// 13 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 14 | /// 15 | public override bool HasComments => false; 16 | 17 | /// 18 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 19 | /// 20 | public override int SkipLines => 0; 21 | 22 | /// 23 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 24 | /// 25 | public override int ExpectedNumberOfFields => 19; 26 | 27 | /// 28 | /// Parses the specified data into an object. 29 | /// 30 | /// The fields/data representing an to parse. 31 | /// Returns a new object. 32 | public override ExtendedGeoName Parse(string[] fields) 33 | { 34 | return new ExtendedGeoName 35 | { 36 | Id = StringToInt(fields[0]), 37 | Name = fields[1], 38 | NameASCII = fields[2], 39 | AlternateNames = StringToArray(fields[3]), 40 | Latitude = StringToDouble(fields[4]), 41 | Longitude = StringToDouble(fields[5]), 42 | FeatureClass = fields[6], 43 | FeatureCode = fields[7], 44 | CountryCode = fields[8], 45 | AlternateCountryCodes = StringToArray(fields[9]), 46 | Admincodes = new[] { fields[10], fields[11], fields[12], fields[13] }, 47 | Population = StringToLong(fields[14]), 48 | Elevation = fields[15].Length > 0 ? (int?)StringToInt(fields[15]) : null, 49 | Dem = StringToInt(fields[16]), 50 | Timezone = StringToTimeZone(fields[17]), 51 | ModificationDate = StringToDateTime(fields[18]) 52 | }; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /NGeoNames/Composers/GeoNameComposer.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | 3 | namespace NGeoNames.Composers 4 | { 5 | /// 6 | /// Provides methods for composing a string representing a . 7 | /// 8 | public class GeoNameComposer : BaseComposer 9 | { 10 | /// 11 | /// Gets wether the instance of the uses extended fileformat (19 fields) or 12 | /// "compact format" (4 fields: Id, Name, Latitude and Longitude). 13 | /// 14 | public bool UseExtendedFileFormat { get; private set; } 15 | 16 | /// 17 | /// Initializes a new instance of the class with default values (extended fileformat, 19 fields). 18 | /// 19 | public GeoNameComposer() 20 | : this(true) { } 21 | 22 | /// 23 | /// Initializes a new instance of the class with specified file format. 24 | /// 25 | /// 26 | /// When this parameter is true, the (default) file format (19 fields) will be assumed for geoname data, 27 | /// when this parameter is false, the "compact file format" (4 fields: Id, Name, Latitude and Longitude) 28 | /// will be assumed. 29 | /// 30 | public GeoNameComposer(bool useextendedfileformat) 31 | { 32 | UseExtendedFileFormat = useextendedfileformat; 33 | } 34 | 35 | /// 36 | /// Composes the specified into a string. 37 | /// 38 | /// The to be composed into a string. 39 | /// A string representing the specified . 40 | public override string Compose(GeoName value) 41 | { 42 | if (UseExtendedFileFormat) 43 | { 44 | return string.Join(FieldSeparator.ToString(), value.Id, value.Name, null, null, DoubleToString(value.Latitude), DoubleToString(value.Longitude), 45 | null, null, null, null, null, null, null, null, null, null, null, null, null); 46 | } 47 | else 48 | { 49 | return string.Join(FieldSeparator.ToString(), value.Id, value.Name, DoubleToString(value.Latitude), DoubleToString(value.Longitude)); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/CountryInfoParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System.Globalization; 3 | 4 | namespace NGeoNames.Parsers 5 | { 6 | /// 7 | /// Provides methods for parsing a object from a string-array. 8 | /// 9 | public class CountryInfoParser : BaseParser 10 | { 11 | /// 12 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 13 | /// 14 | public override bool HasComments => true; 15 | 16 | /// 17 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 18 | /// 19 | public override int SkipLines => 0; 20 | 21 | /// 22 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 23 | /// 24 | public override int ExpectedNumberOfFields => 19; 25 | 26 | /// 27 | /// Parses the specified data into a object. 28 | /// 29 | /// The fields/data representing a to parse. 30 | /// Returns a new object. 31 | public override CountryInfo Parse(string[] fields) 32 | { 33 | return new CountryInfo 34 | { 35 | ISO_Alpha2 = fields[0], 36 | ISO_Alpha3 = fields[1], 37 | ISO_Numeric = fields[2], 38 | FIPS = fields[3], 39 | Country = fields[4], 40 | Capital = fields[5], 41 | Area = fields[6].Length > 0 ? (float?)float.Parse(fields[6], CultureInfo.InvariantCulture) : null, 42 | Population = StringToInt(fields[7]), 43 | Continent = fields[8], 44 | Tld = fields[9], 45 | CurrencyCode = fields[10], 46 | CurrencyName = fields[11], 47 | Phone = fields[12].Length > 0 && fields[12].StartsWith("+") ? fields[12] : "+" + fields[12], 48 | PostalCodeFormat = fields[13], 49 | PostalCodeRegex = fields[14], 50 | Languages = StringToArray(fields[15]), 51 | GeoNameId = fields[16].Length > 0 ? (int?)int.Parse(fields[16], CultureInfo.InvariantCulture) : null, 52 | Neighbours = StringToArray(fields[17]), 53 | EquivalentFipsCode = fields[18] 54 | }; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /NGeoNames/Entities/AlternateName.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents an alternate name for a specific or . 5 | /// 6 | /// 7 | /// The property of the object is a short 8 | /// version of the data without links and postal codes but with ASCUU transliterations. 9 | /// 10 | public class AlternateName 11 | { 12 | /// 13 | /// Gets/sets the Id of the . 14 | /// 15 | public int Id { get; set; } 16 | 17 | /// 18 | /// Gets/sets the geoname database Id of the or the 19 | /// refers to. 20 | /// 21 | public int GeoNameId { get; set; } 22 | 23 | /// 24 | /// Gets/sets the ISO 639 language code 2- or 3-characters. 25 | /// 26 | public string ISOLanguage { get; set; } 27 | 28 | /// 29 | /// Gets/sets the type of the ; this can be of, but is not limited to, type 'post' 30 | /// for postal codes, 'iata', 'icao' and 'faac' for airport codes, 'fr_1793' for French Revolution names, 31 | /// 'abbr' for abbreviation and 'link' for a website. 32 | /// 33 | public string Type { get; set; } 34 | 35 | /// 36 | /// Gets/sets the or name variant or the value of the specified . 37 | /// 38 | public string Name { get; set; } 39 | 40 | /// 41 | /// Gets/sets whether this is an official/preferred name. 42 | /// 43 | public bool IsPreferredName { get; set; } 44 | 45 | /// 46 | /// Gets/sets whether this is a short name like 'California' for 'State of California'. 47 | /// 48 | public bool IsShortName { get; set; } 49 | 50 | /// 51 | /// Gets/sets whether this is a colloquial or slang term. 52 | /// 53 | public bool IsColloquial { get; set; } 54 | 55 | /// 56 | /// Gets/sets whether this is historic and was used in the past 57 | /// 58 | public bool IsHistoric { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /NGeoNames.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NGeoNames", "NGeoNames\NGeoNames.csproj", "{A5BC5A09-1185-465A-971B-DB86DBD73A84}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NGeoNamesTests", "NGeoNamesTests\NGeoNamesTests.csproj", "{A099E590-8239-4589-9355-E004D5F790C8}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5AA06099-00A1-4527-944E-1A69E498D1FE}" 11 | ProjectSection(SolutionItems) = preProject 12 | icon.png = icon.png 13 | LICENSE = LICENSE 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DumpTester", "DumpTester\DumpTester.csproj", "{F3407F70-D016-40CD-A394-B56FACA6307B}" 18 | EndProject 19 | Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "NGeoNamesHelp", "NGeoNames.Documentation\NGeoNamesHelp.shfbproj", "{D6A78785-3434-4544-A495-B201AFBD0964}" 20 | ProjectSection(ProjectDependencies) = postProject 21 | {A5BC5A09-1185-465A-971B-DB86DBD73A84} = {A5BC5A09-1185-465A-971B-DB86DBD73A84} 22 | EndProjectSection 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {A5BC5A09-1185-465A-971B-DB86DBD73A84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {A5BC5A09-1185-465A-971B-DB86DBD73A84}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {A5BC5A09-1185-465A-971B-DB86DBD73A84}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {A5BC5A09-1185-465A-971B-DB86DBD73A84}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {A099E590-8239-4589-9355-E004D5F790C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {A099E590-8239-4589-9355-E004D5F790C8}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {A099E590-8239-4589-9355-E004D5F790C8}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {A099E590-8239-4589-9355-E004D5F790C8}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {F3407F70-D016-40CD-A394-B56FACA6307B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {F3407F70-D016-40CD-A394-B56FACA6307B}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {F3407F70-D016-40CD-A394-B56FACA6307B}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {D6A78785-3434-4544-A495-B201AFBD0964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {D6A78785-3434-4544-A495-B201AFBD0964}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {D6A78785-3434-4544-A495-B201AFBD0964}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {81473F55-FA05-4A6E-BDE1-C481DD88A3B0} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /NGeoNamesTests/EntitiesTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames.Entities; 3 | 4 | namespace NGeoNamesTests 5 | { 6 | [TestClass] 7 | public class EntitiesTests 8 | { 9 | [TestMethod] 10 | public void DistanceTo_ReturnsCorrectResult() 11 | { 12 | //http://en.wikipedia.org/w/index.php?title=Great-circle_distance&oldid=414838052 13 | var a = new GeoName { Latitude = 36.1172, Longitude = -86.6672 }; //Nashville International Airport (BNA) in Nashville, TN, USA: N 36°7.2', W 86°40.2' 14 | var b = new GeoName { Latitude = 33.9344, Longitude = -118.4 }; //Los Angeles International Airport (LAX) in Los Angeles, CA, USA: N 33°56.4', W 118°24.0' 15 | 16 | var x1 = a.DistanceTo(b); // DistanceTo(GeoName) overload 17 | var x2 = a.DistanceTo(b.Latitude, b.Longitude); // DistanceTo(double, double) overload 18 | var actual = 2887260; // According to wikipedia 19 | 20 | //We use a slightly different radius of the earth than the wikipedia example uses (6372.8 in wikipedia vs. 6371 which is most commonly used) 21 | Assert.AreEqual(actual, x1, actual * .0005); // All we want is to be within .05% of the "actual" (according to wikipedia) value 22 | Assert.AreEqual(actual, x2, actual * .0005); // All we want is to be within .05% of the "actual" (according to wikipedia) value 23 | 24 | //Actually, we're only 0,0135% off... 25 | } 26 | 27 | [TestMethod] 28 | public void DistanceTo_WrapsAroundLongitudeCorrectly() 29 | { 30 | var a = new GeoName { Latitude = 51.377020, Longitude = 179.431888, Name = "Amchitka Island" }; 31 | var b = new GeoName { Latitude = 51.272322, Longitude = -179.134396, Name = "Amatignak Island" }; 32 | 33 | var actual = 100300; // +/- a bit; checked with http://www.freemaptools.com/measure-distance.htm and (classic) google maps measure tool 34 | 35 | var result = a.DistanceTo(b); 36 | Assert.AreEqual(actual, result, actual * .0005); // All we want is to be within .05% of the "actual" (according to our own measurements) value 37 | } 38 | 39 | [TestMethod] 40 | public void DistanceTo_WorksOnAllIGeoLocationObjects() 41 | { 42 | var gn = new GeoName { Latitude = 50.0333, Longitude = 16.2833 }; 43 | var en = new ExtendedGeoName { Latitude = 55.5075, Longitude = 31.85 }; 44 | var pc = new Postalcode { Latitude = 51.5558, Longitude = 5.6903 }; 45 | 46 | var a = gn.DistanceTo(en); 47 | var b = gn.DistanceTo(pc); 48 | var c = gn.DistanceTo(gn); 49 | 50 | var d = en.DistanceTo(gn); 51 | var e = en.DistanceTo(pc); 52 | var f = en.DistanceTo(en); 53 | 54 | var g = pc.DistanceTo(gn); 55 | var h = pc.DistanceTo(en); 56 | var i = pc.DistanceTo(pc); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /DumpTester/DumpTester.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F3407F70-D016-40CD-A394-B56FACA6307B} 8 | Exe 9 | Properties 10 | DumpTester 11 | DumpTester 12 | v4.5.1 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Designer 52 | 53 | 54 | 55 | 56 | {a5bc5a09-1185-465a-971b-db86dbd73a84} 57 | NGeoNames 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /NGeoNamesTests/FileUtil.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames; 3 | using NGeoNames.Parsers; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace NGeoNamesTests 10 | { 11 | internal class FileUtil 12 | { 13 | // Compares two data files using a GenericEntity to easily compare actual values without bothering with newline differences, 14 | // comments etc. nor trying to "understand" what they mean 15 | public static void EnsureFilesAreFunctionallyEqual(string src, string dst, int expectedfields, int skiplines, char[] fieldseparators, Encoding encoding, bool hascomments) 16 | { 17 | var parser_in = new GenericParser(expectedfields, skiplines, fieldseparators, encoding, hascomments); 18 | var parser_out = new GenericParser(expectedfields, 0, fieldseparators, encoding, false); 19 | 20 | var expected = new GeoFileReader().ReadRecords(src, FileType.Plain, parser_in).ToArray(); 21 | var actual = new GeoFileReader().ReadRecords(dst, FileType.Plain, parser_out).ToArray(); 22 | 23 | CollectionAssert.AreEqual(expected, actual, new GenericEntityComparer()); 24 | } 25 | 26 | private class GenericParser : IParser 27 | { 28 | public bool HasComments { get; private set; } 29 | public int SkipLines { get; private set; } 30 | public int ExpectedNumberOfFields { get; private set; } 31 | public Encoding Encoding { get; private set; } 32 | public char[] FieldSeparators { get; private set; } 33 | 34 | public GenericParser(int expectedfields, int skiplines, char[] fieldseparators, Encoding encoding, bool hascomments) 35 | { 36 | SkipLines = skiplines; 37 | ExpectedNumberOfFields = expectedfields; 38 | FieldSeparators = fieldseparators; 39 | Encoding = encoding; 40 | HasComments = hascomments; 41 | } 42 | 43 | public GenericEntity Parse(string[] fields) 44 | { 45 | Assert.AreEqual(ExpectedNumberOfFields, fields.Length); 46 | return new GenericEntity { Data = fields }; 47 | } 48 | } 49 | 50 | private class GenericEntity 51 | { 52 | public string[] Data { get; set; } 53 | } 54 | 55 | private class GenericEntityComparer : IComparer, IComparer 56 | { 57 | public int Compare(GenericEntity x, GenericEntity y) 58 | { 59 | int r = x.Data.Length.CompareTo(y.Data.Length); 60 | if (r != 0) 61 | return r; 62 | 63 | for (int i = 0; i < x.Data.Length && r == 0; i++) 64 | r = x.Data[i].CompareTo(y.Data[i]); 65 | return r; 66 | } 67 | 68 | public int Compare(object x, object y) 69 | { 70 | return Compare(x as GenericEntity, y as GenericEntity); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /NGeoNamesTests/GeoFileWriterTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames; 3 | using NGeoNames.Composers; 4 | using NGeoNames.Entities; 5 | using System; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace NGeoNamesTests 10 | { 11 | [TestClass] 12 | public class GeoFileWriterTests 13 | { 14 | private static readonly CustomEntity[] testvalues = new[] { 15 | new CustomEntity { Data = new [] { "Data L1☃F1", "Data L1☃F2", "Data L1☃F3"}}, 16 | new CustomEntity { Data = new [] { "Data L2☃F1", "Data L2☃F2", "Data L2☃F3"}}, 17 | new CustomEntity { Data = new [] { "Data L3☃F1", "Data L3☃F2", "Data L3☃F3"}}, 18 | }; 19 | 20 | [TestMethod] 21 | public void GeoFileWriter_ComposesFileCorrectly1() 22 | { 23 | new GeoFileWriter().WriteRecords(@"testdata\test_geofilewritercustom1.txt", testvalues, new CustomComposer(Encoding.UTF8, ',')); 24 | 25 | var gf = new GeoFileReader(); 26 | var target = gf.ReadRecords(@"testdata\test_geofilewritercustom1.txt", new CustomParser(3, 0, new[] { ',' }, Encoding.UTF8, false)).ToArray(); 27 | Assert.AreEqual(3, target.Length); 28 | CollectionAssert.AreEqual(testvalues, target, new CustomEntityComparer()); 29 | } 30 | 31 | [TestMethod] 32 | public void GeoFileWriter_ComposesFileCorrectly2() 33 | { 34 | new GeoFileWriter().WriteRecords(@"testdata\test_geofilewritercustom2.txt", testvalues, new CustomComposer(Encoding.UTF7, '!')); 35 | 36 | var gf = new GeoFileReader(); 37 | var target = gf.ReadRecords(@"testdata\test_geofilewritercustom2.txt", new CustomParser(3, 0, new[] { '!' }, Encoding.UTF7, false)).ToArray(); 38 | Assert.AreEqual(3, target.Length); 39 | CollectionAssert.AreEqual(testvalues, target, new CustomEntityComparer()); 40 | } 41 | 42 | [TestMethod] 43 | [ExpectedException(typeof(NotSupportedException))] 44 | public void GeoFileWriter_ThrowsOnFailureWhenAutodetectingFileType() 45 | { 46 | //When filetype == autodetect and an unknown extension is used an exception should be thrown 47 | new GeoFileWriter().WriteRecords(@"testdata\invalid.out.ext", testvalues, new CustomComposer(Encoding.UTF8, '\t')); 48 | } 49 | 50 | [TestMethod] 51 | public void GeoFileWriter_DoesNotThrowOnInvalidExtensionButSpecifiedFileType() 52 | { 53 | //When filetype is specified and an unknown extension is used it should be written fine 54 | new GeoFileWriter().WriteRecords(@"testdata\invalid.out.ext", testvalues, new CustomComposer(Encoding.UTF8, '\t'), FileType.Plain); 55 | } 56 | 57 | [TestMethod] 58 | [ExpectedException(typeof(NotSupportedException))] 59 | public void GeoFileWriter_ThrowsOnUnknownSpecifiedFileType() 60 | { 61 | //When and unknown filetype is specified an exception should be thrown 62 | new GeoFileWriter().WriteRecords(@"testdata\invalid.out.ext", testvalues, new CustomComposer(Encoding.UTF8, '\t'), (FileType)999); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_custom.txt: -------------------------------------------------------------------------------- 1 | These 2 | Lines 3 | Should 4 | Be 5 | Skipped 6 | 2759794+JgM-Amsterdam+JgM-Amsterdam+JgM-AMS,Amesterdam,Amesterdao,Amesterd+AOM-o,Amistardam,Amstardam,Amstedam,Amstelodamum,Amsterdam,Amsterdama,Amsterdamas,Amsterdami,Amsterdamo,Amsterdams,Amsterdan,Amsterntam,Amst+AOg-dam,Amszterdam,Damsko,Gorad Amstehrdam,I-Amsterdami,a mu si te dan,aimstardaima,amasataradama,amastaradama,amastararyama,amseuteleudam,amstardama,amstartam,amstrdam,amusuterudamu,anstardyam,emstaradyama,xamstexrdam,y+BtU-mst+BtU-rdam,+AMA-msterdam,+AME-msterdam,+AME-msterdan,+A4YDvAPDA8QDtQPBA70DxAOxA7w-,+BBAEPARBBEIENQRABDQEMAQ8-,+BBMEPgRABDAENA- +BBAEPARBBEIETQRABDQEMAQ8-,+BTEFdAV9BX8FZQWABWQFYQV0-,+BdAF3gXhBdgF4gXoBdMF0AXd-,+BdAF3gXhBdgF6AXTBd0-,+BiIGRQYzBioGMQYvBicGRQ-,+BiMGRQYzBioGMQYvBicGRQ-,+BiYGJwZFBjMGKgbQBjEGLwYnBkU-,+BiYG1QZFBjMGKgbVBjEGLwYnBkU-,+BicGRQYzBioGMQYvBicGRQ-,+BicGRQYzBnwGMQaJBicGRQ-,+BicGzAZFBjMGeQYxBogGzAZF-,+BxAHIQcjBxsHKgcVBxAHIQ-,+CQUgDQlFCS4JTQk4CU0JHwkwCSEJRQku-,+CQYJLglNCTgJTQkfCTAJTQkhCS4-,+CQ8JLglNCTgJTQkkCTAJJglNCS8JPgku-,+CRAJLglNCTgJTQkfCTAJTQkhCUgJLg-,+CYYJrgm4Cc0Jnwm+CbAJoQm+Ca4-,+CgUKLgo4CiQKMAomCi4-,+CwYLLgs3C00LHwswCyELPAtNC18LPgsu-,+C4YLrgvNC7gLzQufC7ALzQufC64LzQ-,+DIYMggy4DM0MnwywDM0MoQzNDK8MvgyuDM0-,+DQYNAg04DU0NMQ1NDTENfA0hDT4NAg-,+DYgNuA3KDcMNyg2nDbsNyg2pDdENuA3K-,+Di0OMQ4hDioOQA4VDi0OIw5MDhQOMQ4h-,+D2gPeg9YDwsPZg9yDwsPSg9iDwsPTA9YDw0-,+ECEQGRA6EAUQEBAsEBIQGRA6EBkQPBAtEC8QNw-,+ENAQ2xDhEOIQ1BDgENMQ0BDbENg-,+EqASHRI1EnASLRLzEh0-,+MKIw4DC5MMYw6zDAMOA-,+lj9ZxmWvcnlOOQ-,+xVTCpNFMuXSy9CYD-52.37403+JgM-4.88969+JgM-P+JgM-PPLC+JgM-NL+JgMmAw-07+JgM-0363+JgMmAyYD-741636+JgMmAw-13+JgM-Europe/Amsterdam+JgM-2011-06-16 7 | +ACM-Comments should be skipped 8 | +ACM-as well... 9 | 2759794+JgM-Amsterdam+JgM-Amsterdam+JgM-AMS,Amesterdam,Amesterdao,Amesterd+AOM-o,Amistardam,Amstardam,Amstedam,Amstelodamum,Amsterdam,Amsterdama,Amsterdamas,Amsterdami,Amsterdamo,Amsterdams,Amsterdan,Amsterntam,Amst+AOg-dam,Amszterdam,Damsko,Gorad Amstehrdam,I-Amsterdami,a mu si te dan,aimstardaima,amasataradama,amastaradama,amastararyama,amseuteleudam,amstardama,amstartam,amstrdam,amusuterudamu,anstardyam,emstaradyama,xamstexrdam,y+BtU-mst+BtU-rdam,+AMA-msterdam,+AME-msterdam,+AME-msterdan,+A4YDvAPDA8QDtQPBA70DxAOxA7w-,+BBAEPARBBEIENQRABDQEMAQ8-,+BBMEPgRABDAENA- +BBAEPARBBEIETQRABDQEMAQ8-,+BTEFdAV9BX8FZQWABWQFYQV0-,+BdAF3gXhBdgF4gXoBdMF0AXd-,+BdAF3gXhBdgF6AXTBd0-,+BiIGRQYzBioGMQYvBicGRQ-,+BiMGRQYzBioGMQYvBicGRQ-,+BiYGJwZFBjMGKgbQBjEGLwYnBkU-,+BiYG1QZFBjMGKgbVBjEGLwYnBkU-,+BicGRQYzBioGMQYvBicGRQ-,+BicGRQYzBnwGMQaJBicGRQ-,+BicGzAZFBjMGeQYxBogGzAZF-,+BxAHIQcjBxsHKgcVBxAHIQ-,+CQUgDQlFCS4JTQk4CU0JHwkwCSEJRQku-,+CQYJLglNCTgJTQkfCTAJTQkhCS4-,+CQ8JLglNCTgJTQkkCTAJJglNCS8JPgku-,+CRAJLglNCTgJTQkfCTAJTQkhCUgJLg-,+CYYJrgm4Cc0Jnwm+CbAJoQm+Ca4-,+CgUKLgo4CiQKMAomCi4-,+CwYLLgs3C00LHwswCyELPAtNC18LPgsu-,+C4YLrgvNC7gLzQufC7ALzQufC64LzQ-,+DIYMggy4DM0MnwywDM0MoQzNDK8MvgyuDM0-,+DQYNAg04DU0NMQ1NDTENfA0hDT4NAg-,+DYgNuA3KDcMNyg2nDbsNyg2pDdENuA3K-,+Di0OMQ4hDioOQA4VDi0OIw5MDhQOMQ4h-,+D2gPeg9YDwsPZg9yDwsPSg9iDwsPTA9YDw0-,+ECEQGRA6EAUQEBAsEBIQGRA6EBkQPBAtEC8QNw-,+ENAQ2xDhEOIQ1BDgENMQ0BDbENg-,+EqASHRI1EnASLRLzEh0-,+MKIw4DC5MMYw6zDAMOA-,+lj9ZxmWvcnlOOQ-,+xVTCpNFMuXSy9CYD-52.37403+JgM-4.88969+JgM-P+JgM-PPLC+JgM-NL+JgMmAw-07+JgM-0363+JgMmAyYD-741636+JgMmAw-13+JgM-Europe/Amsterdam+JgM-2011-06-16 10 | +ACM-and our separator, 11 | +ACM-snowy the snowman (+JgM-) should work too -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # MSTest test Results 20 | [Tt]est[Rr]esult*/ 21 | [Bb]uild[Ll]og.* 22 | 23 | #NUNIT 24 | *.VisualState.xml 25 | TestResult.xml 26 | 27 | # Build Results of an ATL Project 28 | [Dd]ebugPS/ 29 | [Rr]eleasePS/ 30 | dlldata.c 31 | 32 | *_i.c 33 | *_p.c 34 | *_i.h 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.svclog 55 | *.scc 56 | 57 | # Chutzpah Test files 58 | _Chutzpah* 59 | 60 | # Visual C++ cache files 61 | ipch/ 62 | *.aps 63 | *.ncb 64 | *.opensdf 65 | *.sdf 66 | *.cachefile 67 | 68 | # Visual Studio profiler 69 | *.psess 70 | *.vsp 71 | *.vspx 72 | 73 | # TFS 2012 Local Workspace 74 | $tf/ 75 | 76 | # Guidance Automation Toolkit 77 | *.gpState 78 | 79 | # ReSharper is a .NET coding add-in 80 | _ReSharper*/ 81 | *.[Rr]e[Ss]harper 82 | *.DotSettings.user 83 | 84 | # JustCode is a .NET coding addin-in 85 | .JustCode 86 | 87 | # TeamCity is a build add-in 88 | _TeamCity* 89 | 90 | # DotCover is a Code Coverage Tool 91 | *.dotCover 92 | 93 | # NCrunch 94 | *.ncrunch* 95 | _NCrunch_* 96 | .*crunch*.local.xml 97 | 98 | # MightyMoose 99 | *.mm.* 100 | AutoTest.Net/ 101 | 102 | # Web workbench (sass) 103 | .sass-cache/ 104 | 105 | # Installshield output folder 106 | [Ee]xpress/ 107 | 108 | # DocProject is a documentation generator add-in 109 | DocProject/buildhelp/ 110 | DocProject/Help/*.HxT 111 | DocProject/Help/*.HxC 112 | DocProject/Help/*.hhc 113 | DocProject/Help/*.hhk 114 | DocProject/Help/*.hhp 115 | DocProject/Help/Html2 116 | DocProject/Help/html 117 | 118 | # Click-Once directory 119 | publish/ 120 | 121 | # Publish Web Output 122 | *.[Pp]ublish.xml 123 | *.azurePubxml 124 | 125 | # NuGet Packages Directory 126 | packages/ 127 | ## TODO: If the tool you use requires repositories.config uncomment the next line 128 | #!packages/repositories.config 129 | 130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) 132 | !packages/build/ 133 | 134 | # Windows Azure Build Output 135 | csx/ 136 | *.build.csdef 137 | 138 | # Windows Store app package directory 139 | AppPackages/ 140 | 141 | # Others 142 | sql/ 143 | *.Cache 144 | ClientBin/ 145 | [Ss]tyle[Cc]op.* 146 | ~$* 147 | *~ 148 | *.dbmdl 149 | *.dbproj.schemaview 150 | *.pfx 151 | *.publishsettings 152 | node_modules/ 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | *.mdf 166 | *.ldf 167 | 168 | # Business Intelligence projects 169 | *.rdl.data 170 | *.bim.layout 171 | *.bim_*.settings 172 | 173 | # Microsoft Fakes 174 | FakesAssemblies/ 175 | /Help 176 | /.vs/ 177 | *.chm 178 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/GeoNameParser.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System; 3 | using System.Globalization; 4 | 5 | namespace NGeoNames.Parsers 6 | { 7 | /// 8 | /// Provides methods for parsing a object from a string-array. 9 | /// 10 | public class GeoNameParser : BaseParser 11 | { 12 | private static readonly char[] csv = { ',' }; 13 | 14 | private int _expectednumberoffields; 15 | 16 | /// 17 | /// Initializes a new instance of the class with default values (extended fileformat, 19 fields). 18 | /// 19 | public GeoNameParser() 20 | : this(true) { } 21 | 22 | /// 23 | /// Initializes a new instance of the class with specified file format. 24 | /// 25 | /// 26 | /// When this parameter is true, the (default) file format (19 fields) will be assumed for geoname data, 27 | /// when this parameter is false, the "compact file format" (4 fields: Id, Name, Latitude and Longitude) 28 | /// will be assumed. 29 | /// 30 | public GeoNameParser(bool useextendedfileformat) 31 | { 32 | _expectednumberoffields = useextendedfileformat ? 19 : 4; 33 | } 34 | 35 | /// 36 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 37 | /// 38 | public override bool HasComments 39 | { 40 | get { return false; } 41 | } 42 | 43 | /// 44 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 45 | /// 46 | public override int SkipLines 47 | { 48 | get { return 0; } 49 | } 50 | 51 | /// 52 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 53 | /// 54 | public override int ExpectedNumberOfFields { get { return _expectednumberoffields; } } 55 | 56 | /// 57 | /// Parses the specified data into a object. 58 | /// 59 | /// The fields/data representing a to parse. 60 | /// Returns a new object. 61 | public override GeoName Parse(string[] fields) 62 | { 63 | switch (ExpectedNumberOfFields) 64 | { 65 | case 4: 66 | return new GeoName 67 | { 68 | Id = StringToInt(fields[0]), 69 | Name = fields[1], 70 | Latitude = StringToDouble(fields[2]), 71 | Longitude = StringToDouble(fields[3]), 72 | }; 73 | case 19: 74 | return new GeoName 75 | { 76 | Id = StringToInt(fields[0]), 77 | Name = fields[1], 78 | Latitude = StringToDouble(fields[4]), 79 | Longitude = StringToDouble(fields[5]), 80 | }; 81 | } 82 | throw new NotSupportedException(string.Format("Unsupported number of fields: {0}", ExpectedNumberOfFields)); 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /NGeoNames/Entities/CountryInfo.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Represents information about a country. 5 | /// 6 | public class CountryInfo 7 | { 8 | /// 9 | /// Gets/sets the ISO-3166 2-letter country code. 10 | /// 11 | public string ISO_Alpha2 { get; set; } 12 | 13 | /// 14 | /// Gets/sets the ISO-3166 3-letter country code. 15 | /// 16 | public string ISO_Alpha3 { get; set; } 17 | 18 | /// 19 | /// Gets/sets the ISO-3166 numeric country code. 20 | /// 21 | public string ISO_Numeric { get; set; } 22 | 23 | /// 24 | /// Gets/sets the Federal Information Processing Standards code. 25 | /// 26 | public string FIPS { get; set; } 27 | 28 | /// 29 | /// Gets/sets the name of the country. 30 | /// 31 | public string Country { get; set; } 32 | 33 | /// 34 | /// Gets/sets the capital of the country. 35 | /// 36 | public string Capital { get; set; } 37 | 38 | /// 39 | /// Gets/sets the area in km² of the country. 40 | /// 41 | public float? Area { get; set; } 42 | 43 | /// 44 | /// Gets/sets the population of the country. 45 | /// 46 | public int Population { get; set; } 47 | 48 | /// 49 | /// Gets/sets the code of the country. 50 | /// 51 | public string Continent { get; set; } 52 | 53 | /// 54 | /// Gets/sets the TLD (Top-Level Domain) of the country. 55 | /// 56 | public string Tld { get; set; } 57 | 58 | /// 59 | /// Gets/sets the currency code of the country. 60 | /// 61 | public string CurrencyCode { get; set; } 62 | 63 | /// 64 | /// Gets/sets the currency name of the country. 65 | /// 66 | public string CurrencyName { get; set; } 67 | 68 | /// 69 | /// Gets/sets the international dialing code of the country. 70 | /// 71 | public string Phone { get; set; } 72 | 73 | /// 74 | /// Gets/sets the postal code format of the country. 75 | /// 76 | public string PostalCodeFormat { get; set; } 77 | 78 | /// 79 | /// Gets/sets the regex to match a postal code of the country. 80 | /// 81 | public string PostalCodeRegex { get; set; } 82 | 83 | /// 84 | /// Gets/sets the associated language(s) of the country. 85 | /// 86 | public string[] Languages { get; set; } 87 | 88 | /// 89 | /// Gets/sets the 's geoname database Id. 90 | /// 91 | public int? GeoNameId { get; set; } 92 | 93 | /// 94 | /// Gets/sets the ISO-3166 2-letter country code(s) of the countries neighbouring the country. 95 | /// 96 | public string[] Neighbours { get; set; } 97 | 98 | /// 99 | /// Gets/sets the Equivalent FIPS code of the country. 100 | /// 101 | /// 102 | /// A country (as defined by ISO) might not have any associated FIPS Code. It also happens that several ISO 103 | /// countries have the same FIPS code (e.g. : Finland, and aalen island). In these cases, Finland would have 104 | /// the original FIPS code, and aalen island would have this code as its equivalent FIPS code 105 | /// 106 | public string EquivalentFipsCode { get; set; } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /NGeoNames/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34209 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace NGeoNames.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NGeoNames.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to #See http://download.geonames.org/export/dump/readme.txt 65 | ///AF Africa 6255146 66 | ///AS Asia 6255147 67 | ///EU Europe 6255148 68 | ///NA North America 6255149 69 | ///OC Oceania 6255151 70 | ///SA South America 6255150 71 | ///AN Antarctica 6255152. 72 | /// 73 | internal static string continentCodes { 74 | get { 75 | return ResourceManager.GetString("continentCodes", resourceCulture); 76 | } 77 | } 78 | 79 | /// 80 | /// Looks up a localized string similar to #See http://www.geonames.org/export/codes.html 81 | ///A country, state, region, ... 82 | ///H stream, lake, ... 83 | ///L parks, area, ... 84 | ///P city, village, ... 85 | ///R road, railroad 86 | ///S spot, building, farm 87 | ///T mountain, hill, rock, ... 88 | ///U undersea 89 | ///V forest, heath, .... 90 | /// 91 | internal static string featureClasses_en { 92 | get { 93 | return ResourceManager.GetString("featureClasses_en", resourceCulture); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /NGeoNames/Entities/ExtendedGeoName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NGeoNames.Entities 4 | { 5 | /// 6 | /// Represents a GeoName record with all data represented. 7 | /// 8 | /// 9 | public class ExtendedGeoName : GeoName 10 | { 11 | /// 12 | /// Gets/sets the name of the . 13 | /// 14 | /// 15 | /// Non-ASCII values have been found in the data; it is unfortunately (currently) *NOT* guaranteed that this 16 | /// property contains ASCII-only strings. 17 | /// 18 | public string NameASCII { get; set; } 19 | 20 | /// 21 | /// Gets/sets the alternatenames / ASCII names of the . 22 | /// 23 | /// See . 24 | public string[] AlternateNames { get; set; } 25 | 26 | /// 27 | /// Gets/sets the code of the . 28 | /// 29 | public string FeatureClass { get; set; } 30 | 31 | /// 32 | /// Gets/sets the code of the . 33 | /// 34 | public string FeatureCode { get; set; } 35 | 36 | /// 37 | /// Gets/sets the ISO-3166 2-letter country code of the . 38 | /// 39 | public string CountryCode { get; set; } 40 | 41 | /// 42 | /// Gets/sets the alternate ISO-3166 2-letter country codes. 43 | /// 44 | public string[] AlternateCountryCodes { get; set; } 45 | 46 | private string[] _admincodes; 47 | /// 48 | /// Gets/sets 1 up to 4 admin codes (indexed 0 to 3) of the . 49 | /// 50 | /// Admincode[0]: FIPS code (subject to change to iso code), see remarks and . 51 | /// Admincode[1]: code for the second administrative division (a county in the US). See . 52 | /// Admincode[2]: code for third level administrative division. 53 | /// Admincode[3]: code for fourth level administrative division. 54 | /// 55 | /// 56 | /// 57 | /// Most Admincode[0] codes are FIPS codes. ISO codes are used for US, CH, BE and ME. UK and Greece are using 58 | /// an additional level between country and FIPS code. The code '00' stands for general features where no 59 | /// specific Admincode[0] code is defined. 60 | /// 61 | public string[] Admincodes { 62 | get { return _admincodes; } 63 | set 64 | { 65 | if (value.Length != 4) 66 | throw new ArgumentOutOfRangeException("Admincodes array must be of length 4"); 67 | _admincodes = value; 68 | } 69 | } 70 | 71 | /// 72 | /// Gets/sets the population of the . 73 | /// 74 | public long Population { get; set; } 75 | 76 | /// 77 | /// Gets/sets the elevation (in meters) of the . 78 | /// 79 | public int? Elevation { get; set; } 80 | 81 | /// 82 | /// Gets/sets the Digital Elevation Model, srtm3 or gtopo30, average elevation of 3''x3'' (ca 90mx90m) or 83 | /// 30''x30'' (ca 900mx900m) area in meters of the . 84 | /// 85 | /// 86 | /// srtm processed by cgiar/ciat 87 | /// 88 | public int Dem { get; set; } 89 | 90 | /// 91 | /// Gets/sets the id of the . 92 | /// 93 | public string Timezone { get; set; } 94 | 95 | /// 96 | /// Gets/sets the date of last modification of the . 97 | /// 98 | public DateTime ModificationDate { get; set; } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /NGeoNamesTests/ReverseGeoCodeTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames; 3 | using NGeoNames.Entities; 4 | using System.Linq; 5 | 6 | namespace NGeoNamesTests 7 | { 8 | [TestClass] 9 | public class ReverseGeoCodeTests 10 | { 11 | [TestMethod] 12 | public void ReverseGeoCode_RadialSearch_ReturnsCorrectResults() 13 | { 14 | // Read a file with data with points in and around London in a 20Km radius 15 | var data = GeoFileReader.ReadExtendedGeoNames(@"testdata\test_GB.txt").ToArray(); 16 | var rg = new ReverseGeoCode(data); 17 | var center = rg.CreateFromLatLong(51.5286416, 0); //Exactly at 0 longitude so we test left/right of prime meridian 18 | 19 | Assert.AreEqual(47, data.Length); //File should contain 47 records total 20 | 21 | var expected_ids = new[] { 2640729, 2639577, 2642465, 2637627, 2633709, 2643339, 2634677, 2636503, 2652053, 2654710, 2643743, 2646003, 2643741, 2653941, 6690870, 2655775, 2651621, 2650497, 2656194, 2653266, 2648657, 2637433, 2652618, 2646057 }; 22 | 23 | // Search from the/a center in London for all points in a 10Km radius 24 | var searchresults = rg.RadialSearch(center, 100000.0).ToArray(); 25 | // Number of results should match length of expected_id array 26 | Assert.AreEqual(expected_ids.Length, searchresults.Length); 27 | // Check if each result is in the expected results array 28 | foreach (var r in searchresults) 29 | Assert.IsTrue(expected_ids.Contains(r.Id)); 30 | } 31 | 32 | [TestMethod] 33 | public void ReverseGeoCode_RadialSearch_ReturnsMaxCountResults() 34 | { 35 | // Read a file with data with points in and around London in a 20Km radius 36 | var data = GeoFileReader.ReadExtendedGeoNames(@"testdata\test_GB.txt").ToArray(); 37 | var rg = new ReverseGeoCode(data); 38 | var center = rg.CreateFromLatLong(51.5286416, 0); //Exactly at 0 longitude so we test left/right of prime meridian 39 | var maxresults = 10; 40 | 41 | Assert.AreEqual(47, data.Length); //File should contain 47 records total 42 | 43 | var expected_ids = new[] { 2643741, 2646003, 2643743, 6690870, 2651621, 2655775, 2636503, 2634677, 2656194, 2653266 }; 44 | Assert.AreEqual(maxresults, expected_ids.Length); 45 | 46 | // Search from the/a center in London for all points in a 10Km radius, allowing only maxresults results 47 | var searchresults = rg.RadialSearch(center, 100000.0, maxresults).ToArray(); 48 | // Number of results should match length of expected_id array 49 | Assert.AreEqual(expected_ids.Length, searchresults.Length); 50 | // Check if each result is in the expected results array 51 | foreach (var r in searchresults) 52 | Assert.IsTrue(expected_ids.Contains(r.Id)); 53 | } 54 | 55 | [TestMethod] 56 | public void ReverseGeoCode_NearestNeighbourSearch_ReturnsCorrectResults() 57 | { 58 | // Read a file with data with points in and around London in a 20Km radius 59 | var data = GeoFileReader.ReadExtendedGeoNames(@"testdata\test_GB.txt").ToArray(); 60 | var rg = new ReverseGeoCode(data); 61 | var center = rg.CreateFromLatLong(51.5286416, 0); //Exactly at 0 longitude so we test left/right of prime meridian 62 | 63 | Assert.AreEqual(47, data.Length); //File should contain 47 records total 64 | 65 | var expected_ids = new[] { 2640729, 2639577, 2642465, 2637627, 2633709, 2643339, 2634677, 2636503, 2652053, 2654710, 2643743, 2646003, 2643741, 2653941, 6690870, 2655775, 2651621, 2650497, 2656194, 2653266, 2648657, 2637433, 2652618, 2646057 }; 66 | 67 | // Search from the/a center in London for the first X points (where X == expected_ids.length) 68 | var searchresults = rg.NearestNeighbourSearch(center, expected_ids.Length).ToArray(); 69 | // Number of results should match length of expected_id array 70 | Assert.AreEqual(expected_ids.Length, searchresults.Length); 71 | // Check if each result is in the expected results array 72 | foreach (var r in searchresults) 73 | Assert.IsTrue(expected_ids.Contains(r.Id)); 74 | } 75 | 76 | /// 77 | /// https://github.com/RobThree/NGeoNames/issues/1 78 | /// 79 | [TestMethod] 80 | public void Geonames_MultipleEntities() 81 | { 82 | var rgc1 = new ReverseGeoCode(); 83 | var rgc2 = new ReverseGeoCode(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /NGeoNames.Documentation/NGeoNamesHelp.shfbproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | Debug 8 | AnyCPU 9 | 2.0 10 | {d6a78785-3434-4544-a495-b201afbd0964} 11 | 2017.9.26.0 12 | 14 | Documentation 15 | Documentation 16 | Documentation 17 | 18 | Help\ 19 | NGeoNames 20 | en-US 21 | http://robiii.nl 22 | %28C%29 2014 - 2018 Devcorner.nl 23 | rob%40devcorner.nl 24 | Rob Janssen 25 | NGeoNames documentation 26 | True 27 | NGeoNames documentation 28 | False 29 | False 30 | 31 | 32 | 33 | 34 | This library provides classes for downloading, reading and parsing, writing and composing files from GeoNames.org and provides %28reverse%29 geocoding methods like NearestNeighbourSearch%28%29 and RadialSearch%28%29 on the downloaded dataset%28s%29. 35 | 1.5.1 36 | 2 37 | False 38 | Standard 39 | Blank 40 | VS2013 41 | Guid 42 | AboveNamespaces 43 | 44 | The NGeoNames.Entities namespace contains classes that represent the entities defined by geonames.org and are dictated by their respective "dump files". 45 | The NGeoNames namespace contains classes that allow you to download, read and parse, compose and write geonames.org "dump files" and that can be used to do offline reverse geocoding. 46 | The NGeoNames.Composers namespace contains interfaces and classes that allow you to compose and write geoname.org compatible "dump files". 47 | The NGeoNames.Parsers namespace contains interfaces and classes that allow you to read and parse geoname.org compatible "dump files". 48 | 49 | ms.vsipcc+, ms.vsexpresscc+ 50 | Hierarchical 51 | Msdn 52 | True 53 | 54 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /NGeoNames/GeoUtil.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System; 3 | 4 | namespace NGeoNames 5 | { 6 | /// 7 | /// Utility class. 8 | /// 9 | public static class GeoUtil 10 | { 11 | private const double RADIUSOFEARTH = 6371000; //Radius of the earth, in METERS 12 | private const double METERSPERMILE = 1609.344; //Number of kilometers in an "international" mile (http://en.wikipedia.org/wiki/Mile) 13 | private const double METERSPERYARD = 0.9144; //Number of meters in an "international" yard (http://en.wikipedia.org/wiki/Yard) 14 | 15 | /// 16 | /// Converts degrees to radians. 17 | /// 18 | /// Degrees. 19 | /// Radians. 20 | internal static double Deg2Rad(double deg) 21 | { 22 | return (Math.PI / 180.0) * deg; 23 | } 24 | 25 | /// 26 | /// Calculates the distance from the source to destination location (in meters). 27 | /// 28 | /// The source location. 29 | /// The destination location. 30 | /// The radius of the earth in meters (default: 6371000). 31 | /// Returns the distance, in meters, from source to destination. 32 | /// 33 | /// Note that we use the International 34 | /// System of Units (SI); units of distance are specified in meters. If you want to use imperial system (e.g. 35 | /// miles, nautical miles, yards, foot and whathaveyou's) you need to convert from/to meters. You can use the 36 | /// helper methods / and 37 | /// / for quick conversion. 38 | /// 39 | internal static double DistanceTo(IGeoLocation src, IGeoLocation dest, double radiusofearth = RADIUSOFEARTH) 40 | { 41 | var dLat = GeoUtil.Deg2Rad(dest.Latitude - src.Latitude); 42 | var dLon = GeoUtil.Deg2Rad(dest.Longitude - src.Longitude); 43 | return radiusofearth * (2 * Math.Asin(Math.Min(1, Math.Sqrt(Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(GeoUtil.Deg2Rad(src.Latitude)) * Math.Cos(GeoUtil.Deg2Rad(dest.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2))))); 44 | } 45 | 46 | /// 47 | /// Calculates a coordinate from a GeoName (latitude, longitude, altitude) 48 | /// 49 | /// The GeoName to determine the coordinate for. 50 | /// Radius of the earth, in meters. 51 | /// Returns a coordinate (represented as an array of 3 doubles). 52 | /// This method is for internal use (for the KdTree) only. 53 | internal static double[] GetCoord(IGeoLocation n, double radiusofearth = RADIUSOFEARTH) 54 | { 55 | return new[] { 56 | radiusofearth * Math.Cos(GeoUtil.Deg2Rad(n.Latitude)) * Math.Cos(GeoUtil.Deg2Rad(n.Longitude)), 57 | radiusofearth * Math.Cos(GeoUtil.Deg2Rad(n.Latitude)) * Math.Sin(GeoUtil.Deg2Rad(n.Longitude)), 58 | radiusofearth * Math.Sin(GeoUtil.Deg2Rad(n.Latitude)) 59 | }; 60 | } 61 | 62 | /// 63 | /// Converts meters to miles 64 | /// 65 | /// The number of meters to convert to miles. 66 | /// Returns the number of miles. 67 | public static double MetersToMiles(double meters) 68 | { 69 | return meters / METERSPERMILE; 70 | } 71 | 72 | /// 73 | /// Converts miles to meters 74 | /// 75 | /// The number of miles to convert to meters. 76 | /// Returns the number of meters. 77 | public static double MilesToMeters(double miles) 78 | { 79 | return miles * METERSPERMILE; 80 | } 81 | 82 | /// 83 | /// Converts meters to yards 84 | /// 85 | /// The number of meters to convert to yards. 86 | /// Returns the number of yards. 87 | public static double MetersToYards(double meters) 88 | { 89 | return meters / METERSPERYARD; 90 | } 91 | 92 | /// 93 | /// Converts yards to meters 94 | /// 95 | /// The number of yards to convert to meters. 96 | /// Returns the number of meters. 97 | public static double YardsToMeters(double yards) 98 | { 99 | return yards * METERSPERYARD; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /NGeoNames/GeoExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace NGeoNames.Entities 2 | { 3 | /// 4 | /// Provides geolocation related extension methods. 5 | /// 6 | public static class GeoExtensionMethods 7 | { 8 | /// 9 | /// Calculates the distance from this instance to destination location (in meters). 10 | /// 11 | /// The to apply the method to. 12 | /// The latitude of the destination point. 13 | /// The longitude of the destination point. 14 | /// Returns the distance, in meters, from this instance to destination. 15 | /// 16 | /// Note that we use the International 17 | /// System of Units (SI); units of distance are specified in meters. If you want to use imperial system (e.g. 18 | /// miles, nautical miles, yards, foot and other units) you need to convert from/to meters. You can use the 19 | /// helper methods / and 20 | /// / for quick conversion. 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | public static double DistanceTo(this IGeoLocation loc, double lat, double lng) 27 | { 28 | return GeoUtil.DistanceTo(loc, new GeoName { Latitude = lat, Longitude = lng }); 29 | } 30 | 31 | /// 32 | /// Calculates the distance from this instance to destination location (in meters). 33 | /// 34 | /// The to apply the method to. 35 | /// The latitude of the destination point. 36 | /// The longitude of the destination point. 37 | /// The radius of the earth in meters (default: 6371000). 38 | /// Returns the distance, in meters, from this instance to destination. 39 | /// 40 | /// Note that we use the International 41 | /// System of Units (SI); units of distance are specified in meters. If you want to use imperial system (e.g. 42 | /// miles, nautical miles, yards, foot and other units) you need to convert from/to meters. You can use the 43 | /// helper methods / and 44 | /// / for quick conversion. 45 | /// 46 | /// 47 | /// 48 | /// 49 | /// 50 | public static double DistanceTo(this IGeoLocation loc, double lat, double lng, double radiusofearthinmeters) 51 | { 52 | return GeoUtil.DistanceTo(loc, new GeoName { Latitude = lat, Longitude = lng }, radiusofearthinmeters); 53 | } 54 | 55 | /// 56 | /// Calculates the distance from the this instance to destination location (in meters). 57 | /// 58 | /// The to apply the method to. 59 | /// The destination location. 60 | /// Returns the distance, in meters, from this instance to destination. 61 | /// 62 | /// Note that we use the International 63 | /// System of Units (SI); units of distance are specified in meters. If you want to use imperial system (e.g. 64 | /// miles, nautical miles, yards, foot and other units) you need to convert from/to meters. You can use the 65 | /// helper methods / and 66 | /// / for quick conversion. 67 | /// 68 | /// 69 | /// 70 | /// 71 | /// 72 | public static double DistanceTo(this IGeoLocation src, IGeoLocation dst) 73 | { 74 | return GeoUtil.DistanceTo(src, dst); 75 | } 76 | 77 | /// 78 | /// Calculates the distance from this instance to destination location (in meters). 79 | /// 80 | /// The to apply the method to. 81 | /// The destination location. 82 | /// The radius of the earth in meters (default: 6371000). 83 | /// Returns the distance, in meters, from this instance to destination. 84 | /// 85 | /// Note that we use the International 86 | /// System of Units (SI); units of distance are specified in meters. If you want to use imperial system (e.g. 87 | /// miles, nautical miles, yards, foot and other units) you need to convert from/to meters. You can use the 88 | /// helper methods / and 89 | /// / for quick conversion. 90 | /// 91 | /// 92 | /// 93 | /// 94 | /// 95 | public static double DistanceTo(this IGeoLocation src, IGeoLocation dst, double radiusofearthinmeters) 96 | { 97 | return GeoUtil.DistanceTo(src, dst, radiusofearthinmeters); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /NGeoNames/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\continentCodes.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 123 | 124 | 125 | ..\Resources\featureClasses_en.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 126 | 127 | -------------------------------------------------------------------------------- /NGeoNames/Composers/BaseComposer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Text; 5 | 6 | namespace NGeoNames.Composers 7 | { 8 | /// 9 | /// Provides an (abstract) baseclass for composers. 10 | /// 11 | /// The type of objects to compose. 12 | public abstract class BaseComposer : IComposer 13 | { 14 | /// 15 | /// Defines the default fieldseparator (default: \t). 16 | /// 17 | public static readonly char DEFAULTFIELDSEPARATOR = '\t'; 18 | 19 | /// 20 | /// Defines the default (default: UTF8). 21 | /// 22 | public static readonly Encoding DEFAULTENCODING = Encoding.UTF8; 23 | 24 | /// 25 | /// Gets the to use when writing/composing the file/stream. 26 | /// 27 | public Encoding Encoding { get; set; } 28 | 29 | /// 30 | /// Gets the fieldseparator to use when writing/composing the file/stream. 31 | /// 32 | public char FieldSeparator { get; set; } 33 | 34 | /// 35 | /// Initializes a composer with and . 36 | /// 37 | public BaseComposer() 38 | : this(DEFAULTENCODING, DEFAULTFIELDSEPARATOR) { } 39 | 40 | /// 41 | /// Composes the specified object into a string. 42 | /// 43 | /// The object to be composed into a string. 44 | /// A string representing the specified object. 45 | public abstract string Compose(T value); 46 | 47 | /// 48 | /// Initializes a composer with the specified and fieldseparator. 49 | /// 50 | /// the composer will use. 51 | /// Field separator to use when composing the file/stream. 52 | public BaseComposer(Encoding encoding, char fieldseparator) 53 | { 54 | Encoding = encoding; 55 | FieldSeparator = fieldseparator; 56 | } 57 | 58 | /// 59 | /// Concatenates the elements of an array, using the specified separator between each element, to form a single 60 | /// delimited value. 61 | /// 62 | /// The values to put in delimited format. 63 | /// The string to use as a separator. 64 | /// 65 | /// A string that consists of the elements of values delimited by the separator string. If values is an empty 66 | /// array, the method returns . 67 | /// 68 | protected string ArrayToValue(IEnumerable values, string separator = ",") { 69 | if (values == null) 70 | return null; 71 | return string.Join(separator, values); 72 | } 73 | 74 | /// 75 | /// Returns the desired element of a string array, or, when the index is out of bounds, . 76 | /// 77 | /// The array to return the element from. 78 | /// The index of the element to get. 79 | /// 80 | /// Returns the desired element of a string array or, when the index is out of bounds, . 81 | /// 82 | protected string GetArrayValue(string[] values, int index) 83 | { 84 | return GetArrayValue(values, index, null); 85 | } 86 | 87 | /// 88 | /// Returns the desired element of an array, or, when the index is out of bounds, the specified 89 | /// . 90 | /// 91 | /// The type of the value returned. 92 | /// The array to return the element from. 93 | /// The index of the element to get. 94 | /// The default value to return when the index is out of bounds. 95 | /// 96 | /// Returns the desired element of a string array or, when the index is out of bounds, the specified 97 | /// . 98 | /// 99 | protected TVal GetArrayValue(TVal[] values, int index, TVal defaultValue) 100 | { 101 | if (values != null && index < values.Length) 102 | return values[index]; 103 | return defaultValue; 104 | } 105 | 106 | /// 107 | /// Converts a float value to a string with at least 1 digit after the decimal point (e.g. 1 becomes 1.0). 108 | /// 109 | /// The value to convert. 110 | /// A string representing the value. 111 | protected string FloatToString(float value) 112 | { 113 | return value.ToString("0.0#", CultureInfo.InvariantCulture); 114 | } 115 | 116 | /// 117 | /// Converts a double value to a string with at least 1 digit after the decimal point (e.g. 1 becomes 1.0). 118 | /// 119 | /// The value to convert. 120 | /// A string representing the value. 121 | protected string DoubleToString(double value) 122 | { 123 | if (double.IsNaN(value)) 124 | return null; 125 | return value.ToString("0.0#######", CultureInfo.InvariantCulture); 126 | } 127 | 128 | /// 129 | /// Converts a timezone string to a timezonestring for geoname files (where a space is converted to an underscore). 130 | /// 131 | /// The value to convert. 132 | /// A string representing the value. 133 | protected string TimeZoneToString(string value) 134 | { 135 | return value?.Replace(" ", "_"); 136 | } 137 | 138 | /// 139 | /// Converts a datetime to a string using the specified format. 140 | /// 141 | /// The value to convert. 142 | /// The format to use. 143 | /// A string representing the value. 144 | protected string DateTimeToString(DateTime value, string format = "yyyy-MM-dd") 145 | { 146 | return value.ToString(format, CultureInfo.InvariantCulture); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /NGeoNames/Parsers/BaseParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace NGeoNames.Parsers 6 | { 7 | /// 8 | /// Provides an (abstract) baseclass for parsers. 9 | /// 10 | /// The type of objects to parse. 11 | public abstract class BaseParser : IParser 12 | { 13 | /// 14 | /// Internal constant for String->String[] conversion 15 | /// 16 | private static readonly char[] csv = { ',' }; 17 | 18 | /// 19 | /// Defines the default fieldseparator(s) (default: \t). 20 | /// 21 | public static readonly char[] DEFAULTFIELDSEPARATORS = { '\t' }; 22 | 23 | /// 24 | /// Defines the default (default: UTF8). 25 | /// 26 | public static readonly Encoding DEFAULTENCODING = Encoding.UTF8; 27 | 28 | /// 29 | /// Gets the to use when reading/parsing the file/stream. 30 | /// 31 | public Encoding Encoding { get; set; } 32 | 33 | /// 34 | /// Gets an array of chars that define one or more fieldseparators. 35 | /// 36 | public char[] FieldSeparators { get; set; } 37 | 38 | /// 39 | /// Gets wether the file/stream has (or is expected to have) comments (lines starting with "#"). 40 | /// 41 | public abstract bool HasComments { get; } 42 | 43 | /// 44 | /// Gets the number of lines to skip when parsing the file/stream (e.g. 'headers' etc.). 45 | /// 46 | public abstract int SkipLines { get; } 47 | 48 | /// 49 | /// Gets the number of fields the file/stream is expected to have; anything else will cause a . 50 | /// 51 | public abstract int ExpectedNumberOfFields { get; } 52 | 53 | /// 54 | /// Parses the specified fields into an object of type T. 55 | /// 56 | /// The fields to be parsed. 57 | /// An object of type T parsed from the file/stream. 58 | public abstract T Parse(string[] fields); 59 | 60 | /// 61 | /// Initializes a parser with and . 62 | /// 63 | public BaseParser() 64 | : this(DEFAULTENCODING, DEFAULTFIELDSEPARATORS) { } 65 | 66 | /// 67 | /// Initializes a parser with the specified and fieldseparator(s). 68 | /// 69 | /// the parser will use. 70 | /// Field separator(s) to use when parsing the file/stream. 71 | public BaseParser(Encoding encoding, char[] fieldseparators) 72 | { 73 | Encoding = encoding; 74 | FieldSeparators = fieldseparators; 75 | } 76 | 77 | /// 78 | /// Converts a string into an integer. 79 | /// 80 | /// A string containing the number to convert. 81 | /// An integer equivalent to the number contained in . 82 | protected int StringToInt(string value) 83 | { 84 | return int.Parse(value); 85 | } 86 | 87 | /// 88 | /// Converts a string into a long. 89 | /// 90 | /// A string containing the number to convert. 91 | /// A long equivalent to the number contained in . 92 | protected long StringToLong(string value) 93 | { 94 | return long.Parse(value); 95 | } 96 | 97 | /// 98 | /// Converts a comma-separated string into an array of strings. 99 | /// 100 | /// A string containing the values separated by a comma. 101 | /// A string array equivalent to the values contained in . 102 | protected string[] StringToArray(string value) 103 | { 104 | return StringToArray(value, csv); 105 | } 106 | 107 | /// 108 | /// Converts a delimited string into an array of strings. 109 | /// 110 | /// A string containing the values separated by a delimiter. 111 | /// The delimiter(s) of the string. 112 | /// A string array equivalent to the values contained in . 113 | protected string[] StringToArray(string value, char[] delimiter) 114 | { 115 | return value.Split(delimiter, StringSplitOptions.RemoveEmptyEntries); 116 | } 117 | 118 | /// 119 | /// Converts a string into a float. 120 | /// 121 | /// A string containing the number to convert. 122 | /// A float equivalent to the number contained in . 123 | protected float StringToFloat(string value) 124 | { 125 | return float.Parse(value, CultureInfo.InvariantCulture); 126 | } 127 | 128 | /// 129 | /// Converts a string into a double. 130 | /// 131 | /// A string containing the number to convert. 132 | /// A double equivalent to the number contained in . 133 | protected double StringToDouble(string value) 134 | { 135 | return double.Parse(value, CultureInfo.InvariantCulture); 136 | } 137 | 138 | /// 139 | /// Converts a string into a datetime. 140 | /// 141 | /// A string containing the datetime to convert. 142 | /// The format 143 | /// A DateTime equivalent to the datetime contained in . 144 | protected DateTime StringToDateTime(string value, string format = "yyyy-MM-dd") 145 | { 146 | return DateTime.ParseExact(value, format, CultureInfo.InvariantCulture); 147 | } 148 | 149 | /// 150 | /// Converts a string to a timezone from geoname files (where a space is represented by an underscore). 151 | /// 152 | /// The value to convert. 153 | /// A string representing the value. 154 | protected string StringToTimeZone(string value) 155 | { 156 | return value.Replace("_", " "); 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /NGeoNamesTests/ComposerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using NGeoNames; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NGeoNamesTests 7 | { 8 | [TestClass] 9 | public class ComposerTests 10 | { 11 | [TestMethod] 12 | public void Admin1CodesComposer_ComposesFileCorrectly() 13 | { 14 | var src = @"testdata\test_admin1CodesASCII.txt"; 15 | var dst = @"testdata\test_admin1CodesASCII.out.txt"; 16 | 17 | GeoFileWriter.WriteAdmin1Codes(dst, GeoFileReader.ReadAdmin1Codes(src)); 18 | 19 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 4, 0, new[] { '\t' }, Encoding.UTF8, false); 20 | } 21 | 22 | [TestMethod] 23 | public void Admin2CodesComposer_ComposesFileCorrectly() 24 | { 25 | var src = @"testdata\test_admin2Codes.txt"; 26 | var dst = @"testdata\test_admin2Codes.out.txt"; 27 | 28 | GeoFileWriter.WriteAdmin2Codes(dst, GeoFileReader.ReadAdmin2Codes(src)); 29 | 30 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 4, 0, new[] { '\t' }, Encoding.UTF8, false); 31 | } 32 | 33 | [TestMethod] 34 | public void AlternateNamesComposer_ComposesFileCorrectly() 35 | { 36 | var src = @"testdata\test_alternateNames.txt"; 37 | var dst = @"testdata\test_alternateNames.out.txt"; 38 | 39 | GeoFileWriter.WriteAlternateNames(dst, GeoFileReader.ReadAlternateNames(src)); 40 | 41 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 8, 0, new[] { '\t' }, Encoding.UTF8, false); 42 | } 43 | 44 | [TestMethod] 45 | public void AlternateNamesComposerV2_ComposesFileCorrectly() 46 | { 47 | var src = @"testdata\test_alternateNamesV2.txt"; 48 | var dst = @"testdata\test_alternateNamesV2.out.txt"; 49 | 50 | GeoFileWriter.WriteAlternateNamesV2(dst, GeoFileReader.ReadAlternateNamesV2(src)); 51 | 52 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 10, 0, new[] { '\t' }, Encoding.UTF8, false); 53 | } 54 | 55 | [TestMethod] 56 | public void ContinentComposer_ComposesFileCorrectly() 57 | { 58 | var src = @"testdata\test_continentCodes.txt"; 59 | var dst = @"testdata\test_continentCodes.out.txt"; 60 | 61 | GeoFileWriter.WriteContinents(dst, GeoFileReader.ReadContinents(src)); 62 | 63 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 3, 0, new[] { '\t' }, Encoding.UTF8, true); 64 | } 65 | 66 | [TestMethod] 67 | public void CountryInfoComposer_ComposesFileCorrectly() 68 | { 69 | // We use a *slightly* different file (test_countryInfo2 instead of test_countryInfo) because the 70 | // CountryInfoParser "fixes" phonenumbers with a missing + (e.g. 31 vs. +31); this way the files 71 | // would always differ; test_countryInfo2 has these values fixed 72 | var src = @"testdata\test_countryInfo2.txt"; 73 | var dst = @"testdata\test_countryInfo2.out.txt"; 74 | 75 | GeoFileWriter.WriteCountryInfo(dst, GeoFileReader.ReadCountryInfo(src)); 76 | 77 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 19, 0, new[] { '\t' }, Encoding.UTF8, true); 78 | } 79 | 80 | [TestMethod] 81 | public void ExtendedGeoNamesComposer_ComposesFileCorrectly() 82 | { 83 | var src = @"testdata\test_extendedgeonames.txt"; 84 | var dst = @"testdata\test_extendedgeonames.out.txt"; 85 | 86 | GeoFileWriter.WriteExtendedGeoNames(dst, GeoFileReader.ReadExtendedGeoNames(src)); 87 | 88 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 19, 0, new[] { '\t' }, Encoding.UTF8, false); 89 | } 90 | 91 | [TestMethod] 92 | public void FeatureClassComposer_ComposesFileCorrectly() 93 | { 94 | var src = @"testdata\test_featureClasses_en.txt"; 95 | var dst = @"testdata\test_featureClasses_en.out.txt"; 96 | 97 | GeoFileWriter.WriteFeatureClasses(dst, GeoFileReader.ReadFeatureClasses(src)); 98 | 99 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 2, 0, new[] { '\t' }, Encoding.UTF8, true); 100 | } 101 | 102 | [TestMethod] 103 | public void FeatureCodeComposer_ComposesFileCorrectly() 104 | { 105 | var src = @"testdata\test_featureCodes_en.txt"; 106 | var dst = @"testdata\test_featureCodes_en.out.txt"; 107 | 108 | GeoFileWriter.WriteFeatureCodes(dst, GeoFileReader.ReadFeatureCodes(src)); 109 | 110 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 3, 0, new[] { '\t' }, Encoding.UTF8, true); 111 | } 112 | 113 | [TestMethod] 114 | public void GeoNamesComposerSimple_ComposesFileCorrectly() 115 | { 116 | // In this test we test the "compact file format" (e.g. GeoName, not ExtendedGeoName) 117 | var src = @"testdata\test_geonames_simple.txt"; 118 | var dst = @"testdata\test_geonames_simple.out.txt"; 119 | 120 | GeoFileWriter.WriteGeoNames(dst, GeoFileReader.ReadGeoNames(src, false), false); 121 | 122 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 4, 0, new[] { '\t' }, Encoding.UTF8, false); 123 | } 124 | 125 | [TestMethod] 126 | public void GeoNamesComposerExtended_ComposesFileCorrectly() 127 | { 128 | // In this test we test the "extended file format" (e.g. ExtendedGeoName, not GeoName) 129 | // But since GeoName cannot provide all values, all other properties should be null/empty when writing 130 | var src = @"testdata\test_geonames_ext.txt"; 131 | var dst = @"testdata\test_geonames_ext.out.txt"; 132 | 133 | GeoFileWriter.WriteGeoNames(dst, GeoFileReader.ReadGeoNames(src, true), true); 134 | 135 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 19, 0, new[] { '\t' }, Encoding.UTF8, false); 136 | } 137 | 138 | [TestMethod] 139 | public void HierarchyComposer_ComposesFileCorrectly() 140 | { 141 | var src = @"testdata\test_hierarchy.txt"; 142 | var dst = @"testdata\test_hierarchy.out.txt"; 143 | 144 | GeoFileWriter.WriteHierarchy(dst, GeoFileReader.ReadHierarchy(src)); 145 | 146 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 3, 0, new[] { '\t' }, Encoding.UTF8, false); 147 | } 148 | 149 | [TestMethod] 150 | public void ISOLanguageCodeComposer_ComposesFileCorrectly() 151 | { 152 | var src = @"testdata\test_iso-languagecodes.txt"; 153 | var dst = @"testdata\test_iso-languagecodes.out.txt"; 154 | 155 | GeoFileWriter.WriteISOLanguageCodes(dst, GeoFileReader.ReadISOLanguageCodes(src)); 156 | 157 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 4, 1, new[] { '\t' }, Encoding.UTF8, false); 158 | } 159 | 160 | [TestMethod] 161 | public void TimeZoneComposer_ComposesFileCorrectly() 162 | { 163 | var src = @"testdata\test_timeZones.txt"; 164 | var dst = @"testdata\test_timeZones.out.txt"; 165 | 166 | GeoFileWriter.WriteTimeZones(dst, GeoFileReader.ReadTimeZones(src)); 167 | 168 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 5, 1, new[] { '\t' }, Encoding.UTF8, false); 169 | } 170 | 171 | [TestMethod] 172 | public void UserTagComposer_ComposesFileCorrectly() 173 | { 174 | var src = @"testdata\test_userTags.txt"; 175 | var dst = @"testdata\test_userTags.out.txt"; 176 | 177 | GeoFileWriter.WriteUserTags(dst, GeoFileReader.ReadUserTags(src)); 178 | 179 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 2, 0, new[] { '\t' }, Encoding.UTF8, false); 180 | } 181 | 182 | [TestMethod] 183 | public void PostalcodeComposer_ComposesFileCorrectly() 184 | { 185 | var src = @"testdata\test_postalCodes.txt"; 186 | var dst = @"testdata\test_postalCodes.out.txt"; 187 | 188 | GeoFileWriter.WritePostalcodes(dst, GeoFileReader.ReadPostalcodes(src)); 189 | 190 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 12, 0, new[] { '\t' }, Encoding.UTF8, false); 191 | } 192 | 193 | [TestMethod] 194 | public void CustomComposer_ComposesFileCorrectly() 195 | { 196 | var src = @"testdata\test_custom.txt"; 197 | var dst = @"testdata\test_custom.out.txt"; 198 | 199 | new GeoFileWriter().WriteRecords(dst, new GeoFileReader().ReadRecords(src, new CustomParser(19, 5, new[] { '☃' }, Encoding.UTF7, true)), new CustomComposer(Encoding.UTF7, '☃')); 200 | 201 | FileUtil.EnsureFilesAreFunctionallyEqual(src, dst, 19, 5, new[] { '☃' }, Encoding.UTF7, true); 202 | } 203 | 204 | [TestMethod] 205 | public void Composer_HandlesGZippedFilesCorrectly() 206 | { 207 | var src = @"testdata\test_extendedgeonames.txt"; 208 | var dst = @"testdata\test_extendedgeonames.out.gz"; 209 | 210 | GeoFileWriter.WriteExtendedGeoNames(dst, GeoFileReader.ReadExtendedGeoNames(src)); 211 | 212 | Assert.AreEqual(7, GeoFileReader.ReadExtendedGeoNames(dst).Count()); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /NGeoNamesTests/NGeoNamesTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {A099E590-8239-4589-9355-E004D5F790C8} 7 | Library 8 | Properties 9 | NGeoNamesTests 10 | NGeoNamesTests 11 | v4.5.1 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {a5bc5a09-1185-465a-971b-db86dbd73a84} 68 | NGeoNames 69 | 70 | 71 | 72 | 73 | PreserveNewest 74 | 75 | 76 | PreserveNewest 77 | 78 | 79 | PreserveNewest 80 | 81 | 82 | 83 | PreserveNewest 84 | 85 | 86 | PreserveNewest 87 | 88 | 89 | PreserveNewest 90 | 91 | 92 | 93 | 94 | PreserveNewest 95 | 96 | 97 | PreserveNewest 98 | 99 | 100 | PreserveNewest 101 | 102 | 103 | PreserveNewest 104 | 105 | 106 | PreserveNewest 107 | 108 | 109 | PreserveNewest 110 | 111 | 112 | PreserveNewest 113 | 114 | 115 | PreserveNewest 116 | 117 | 118 | PreserveNewest 119 | 120 | 121 | PreserveNewest 122 | 123 | 124 | PreserveNewest 125 | 126 | 127 | PreserveNewest 128 | 129 | 130 | PreserveNewest 131 | 132 | 133 | PreserveNewest 134 | 135 | 136 | PreserveNewest 137 | 138 | 139 | PreserveNewest 140 | 141 | 142 | PreserveNewest 143 | 144 | 145 | PreserveNewest 146 | 147 | 148 | PreserveNewest 149 | 150 | 151 | PreserveNewest 152 | 153 | 154 | PreserveNewest 155 | 156 | 157 | 158 | 159 | 160 | 161 | False 162 | 163 | 164 | False 165 | 166 | 167 | False 168 | 169 | 170 | False 171 | 172 | 173 | 174 | 175 | 176 | 177 | 184 | -------------------------------------------------------------------------------- /NGeoNamesTests/Testdata/test_extendedgeonames.txt: -------------------------------------------------------------------------------- 1 | 6474170 Reial Pirineus Reial Pirineus 42.5405 1.7313 S HTL AD 0 2136 Europe/Andorra 2009-06-28 2 | 2771781 Mauer Mauer Mauer,Mauer bei Wien 48.15071 16.26856 A ADM4 AT AT 09 900 923 2771781 0 244 Europe/Vienna 2013-05-13 3 | 2212709 Sahara Sahara Deserto di Sahara,Great Desert,Sahara,Sahara Desert,Sahra 26 13 T DSRT DZ DZ,LY,MR,TN,EG,MA,EH,ML,NE,TD,SD 00 0 535 Africa/Algiers 2011-08-06 4 | 3277605 Bosnia and Herzegovina Bosnia and Herzegovina An Bhoisnia agus Heirseagovein,An Bhoisnia agus Heirseagóvéin,An Bhoisnia-Heirseagaivein,An Bhoisnia-Heirseagaivéin,Bhosnia le Herzegovina,Bo-xni-a Hec-xe-go-vi-na,Bo-xni-a Hec-xe-go-vi-na (Bosnia va Herzegovina),Bos'nija i Gercagavina,Bosenia me Hesegowina,Bosini mpe Hezegovine,Bosini mpé Hezegovine,Bosiniya na Herigozevine,Bosiniya na Herizegovina,Bosmudin boln Khercegudin Orn,Bosna,Bosna Hersek,Bosna a Hercegovina,Bosna a Hercegowina,Bosna agus Hearsagobhana,Bosna doo Hetsog Bikeyah,Bosna dóó Hetsog Bikéyah,Bosna i Hercegovina,Bosna i Khercegovina,Bosna in Hercegovina,Bosna kap Hercegovina,Bosna u Hersek,Bosna va Hercegovina,Bosna ve Hersek,Bosna và Hercegovina,Bosna-Gercegovina,Bosna-Hersek,Bosnaen e Haerzegovaen,Bosneja i Hercegovina,Bosneje er Hercuogovena,Bosni,Bosni a Gercegovina a,Bosni aemae Gercegovinae,Bosni ak Erzegovin,Bosni ba Gercegovina,Bosni ba Khercegovina,Bosni tata Gercegovina,Bosnia,Bosnia & Herzegovina,Bosnia - Erzegobine,Bosnia - Hercegovina - Bosna i Khercegovina,Bosnia - Hercegovina - Босна и Херцеговина,Bosnia Ercegovina,Bosnia Erzegovina,Bosnia Herzegovina,Bosnia Herzogovina,Bosnia Hèrzègovina,Bosnia a Hercegovina,Bosnia a Herzegovina,Bosnia aamma Herzegovina,Bosnia and Hercegovina,Bosnia and Herzegovina,Bosnia as Herzegovina,Bosnia at Herzegovina,Bosnia ati Herjegofina,Bosnia dan Herzegovina,Bosnia e Ercegovina,Bosnia e Erzegovina,Bosnia e Erzegòvina,Bosnia e Hercegovina,Bosnia e Hertsegovina,Bosnia e Herzegovina,Bosnia ed Erzegovina,Bosnia et Herzegovina,Bosnia ev Hercʻegovina,Bosnia ha Herzegovina,Bosnia i Gercagavina,Bosnia i Gercogovina,Bosnia i Hercegovina,Bosnia i Hercegowina,Bosnia i Hersegovina,Bosnia ihuan Hertzegovina,Bosnia ja Hercegovina,Bosnia ja Hertsegoviina,Bosnia ja Hertsegovina,Bosnia ja Herzegovina,Bosnia jeung Herzegovina,Bosnia jeung Hérzégovina,Bosnia kai Erzegobine,Bosnia ken Herzegovina,Bosnia kple Herzergovina nutome,Bosnia ma Herzegovina,Bosnia na Erzegovina,Bosnia na Herzegovina,Bosnia na Hezegovina,Bosnia ne Hɛzegovina,Bosnia og Hercegovina,Bosnia og Hersegovina,Bosnia si Hertegovina,Bosnia sy Herzegovina,Bosnia ta Gercogovina,Bosnia tan Hersegobina,Bosnia ug Herzegovina,Bosnia y Hercegovina,Bosnia y Herzegovina,Bosnia è Erzegovina,Bosnia īhuān Hertzegovina,Bosnia și Herțegovina,Bosnia-Ercegovina,Bosnia-Erzegovina,Bosnia-Hercegovina,Bosnia-Hersegovina,Bosnia-Hertsegovina,Bosnia-Herzegovina,Bosnia-ha-Herzegovina,Bosnie Herzegovina,Bosnie an Herzegovinae,Bosnie en Herzegovina,Bosnie en Herzegowina,Bosnie-Erzegovine,Bosnie-Hercegovina,Bosnie-Herzegovena,Bosnie-Herzegovina,Bosnie-Herzegovine,Bosnie-Herzegowina,Bosnie-Herzégovine,Bosnie-Hèrzègovena,Bosnie-Érzégovine,Bosniehreh Gercegovinehreh,Bosnien an Herzegowina,Bosnien och Hercegovina,Bosnien un Herzegowina,Bosnien und Herzegowina,Bosnien-Hercegovina,Bosnien-Herzegowina,Bosnii Hersegowiin,Bosnii da Gercegovin,Bosnii na Herzegovinni,Bosnij da Gercegovina,Bosnija,Bosnija bla Gercegovina,Bosnija da Gercegovina,Bosnija di Khercegovina,Bosnija i Gercegovina,Bosnija ir Hercegovina,Bosnija no Gercegovina,Bosnija un Hercegovina,Bosnija uonna Khercegovina,Bosnijo e Hercegowina,Bosnikondre,Bosnio kaj Hercegovino,Bosnio-Hercegovino,Bosniska a Hercegowina,Bosniska-Hercegowinska,Bosniya,Bosniya Harzagobina,Bosniya Hersigoviina,Bosniya ham Gertsegovina,Bosniya hem Hertegovina,Bosniya hem Herțegovina,Bosniya u Herzegovina,Bosniya va Gersegovina,Bosniya və Herseqovina,Bosniya və Herzokovina,Bosniya we Gersegowina,Bosniya û Herzegovîna,Bosnië Herzegovina,Bosnië en Herzegovina,Bosnië en Herzegowina,Bosnië-Hercegovina,Bosnië-Herzegovina,Bosnië-Herzegowina,Bosniýa we Gersegowina,Bosni–Hercegovina,Bosnja dhe Hercegovina,Bosnje,Bosnya a Hersegowina,Bosnya asin Hersegobina,Bosnya ngan Hersegovina,Bosnän e Härzegovän,Bosnía og Hersegóvína,Bosnïi na Herzegovînni,Bosnėjė ėr Hercuogovėna,Bossnije-Haezzejovina,Bosznia es Hercegovina,Bosznia és Hercegovina,Bosznia-Hercegovina,Boteniya me Erdegobina,Boziniya Hezegovina,Bozni-Ɛrizigovini,Boznia ne Herzegovina,Boznija Herzegovina,Boznija u Herzegovina,Boßnije-Häzzejovina,Bośnia i Hercegowina,Bośńa a Hercegowina,Bożnija u Ħerżegovina,Bożnija Ħerżegovina,Busna-Hirsiquwina,Bòsnia Erzegovina,Bòsnia e Ercegovina,Bòsnia e Erzegòvina,Bòsnia i Hercegovina,Bòsnia-Erçegòvina,Bòsnijô ë Hercegòwina,Bósnia Ercegovina,Bósnia e Herzegovina,Bósnia-Herzegóvina,Bósníà àti Hẹrjẹgòfínà,Bô-xni-a Héc-xê-gô-vi-na,Bô-xni-a Héc-xê-gô-vi-na (Bosnia và Herzegovina),IBhosinya ne Hezegovi,Mbosini ne Hezegovine,Narodna Republika Bosna i Hercegovina,Orileede Bosinia ati Etisegofina,Orílẹ́ède Bọ̀síníà àti Ẹtisẹgófínà,People's Republic of Bosnia and Hercegovina,People’s Republic of Bosnia and Hercegovina,Po-su-ni-a lau Het-set-ko-vi-na,Pongia-Herekomina,Posinia mo Hesikovinia,Posinia mo Hesikōvinia,Pô-sṳ-nì-â lâu Het-set-kô-vì-ná,Pōngia-Herekōmina,Republic of Bosnia and Herzegovina,Republika Bosna i Hercegovina,Socialist Republic of Bosnia and Hercegovina,Socijalisticka Republika Bosna i Hercegovina,Socijalistička Republika Bosna i Hercegovina,Vonia ha Hesegovina,Vosnia kai Erzegovini,albwsnh w alhrsk,albwsnt w alhrsk,albwsnt walhrsk,basaniya baro harjegobhina,basaniya o harjegobhina,basaniya'o harjegobhina,basniya,basniya mariyu hirjigovina,bo si ni ya,bo si ni ya he hei sai ge wei na,bo si ni ya he hei shan gong he guo,bosani'a ate harazegovina,bosani'a ebam harjagobhina,bosani'a o harjagobhina,bosaniya harjigovina,boseunia heleuchegobina,boseuniaheleuchegobina,bosnia da hertsegovina,bosnia do hertsegovina,bosniya ane harjhegovina,bosniya ani harjegovina,bosniya ani harjhagovhina,bosniya aura harazegovina,bosniya aura harzegovina,bosniya mariyu herjegovina,bosniya mattu harjegovina,bosniya mattu herjegovina,bosniya ra harjagobhina,bosniya ra harjagobhiniya,bosniya va harjagovina,bsny w hrzgwyn,bwsny hrzgwwyn,bwsny w hrzgwyn,bwsnyh whrzgwbynh,bwsnyyە vە ھېrsېgwvyna,bwsnʾ whrtsgwbynʾ,i-Bosnia ne-Herzegovina,pocuniya ercekovina,posniya marrum hersikovina,Βοσνία - Ερζεγοβίνη,Βοσνία και Ερζεγοβίνη,Босмудин болн Херцегудин Орн,Босна,Босна и Херцеговина,Босна-Герцеговина,Босни æмæ Герцеговинæ,Босни а Герцеговина а,Босни ба Герцеговина,Босни ба Херцеговина,Босни тата Герцеговина,Босний да Герцеговина,Босниэрэ Герцеговинэрэ,Босния,Босния бла Герцеговина,Босния ва Ҳерсеговина,Босния да Герцеговина,Босния ди Херцеговина,Босния және Герцеговина,Босния и Герцеговина,Босния но Герцеговина,Босния уонна Херцеговина,Босния һәм Герцеговина,Боснія та Герцоговина,Боснія і Герцагавіна,Боснія і Герцеговина,Боснія і Герцеґовина,Боснія і Герцоговина,Босьнія і Герцагавіна,Բոսնիա և Հերցեգովինա,Բոսնիա-Հերցեգովինա,באסניע און הערצעגאווינע,בוסניה והרצגובינה,البوسنة و الهرسك,البوسنة والهرسك,البوسنه و الهرسك,بسنی و هرزگوین,بوسنىيە ۋە ھېرسېگوۋىنا,بوسنی هرزگووین,بوسنی و هرزگوین,بوسنیا اور ہرزیگووینا,بوسنیا تے ہرزیگووینا,بوسنیا و ہرزیگووینا,بۆسنیا و ھەرزەگۆڤینا,ܒܘܣܢܐ ܘ ܗܪܣܟ,ܒܘܣܢܐ ܘܗܪܬܣܓܘܒܝܢܐ,ބޮސްނިޔާ އެންޑް ހެރްޒިގޮވީނާ,बास्निया,बॉस्निया आणि हर्झगोव्हिना,बॉस्निया और हर्ज़ेगोविना,बोसनिया हर्जिगोविना,बोस्निया अणि हर्जेगोविना,बोस्निया और हरज़ेगोविना,बोस्निया र हर्जगोभिना,बोस्निया र हर्जगोभिनिया,बोस्निया व हर्जगोविना,বসনিয়া ও হার্জেগোভিনা,বসনিয়া বারো হার্জেগোভিনা,বসনিয়াও হার্জেগোভিনা,ਬੋਸਨੀਆ ਅਤੇ ਹਰਜ਼ੇਗੋਵੀਨਾ,બોસ્નિયા અને હર્ઝેગોવિના,ବୋସନିଆ ଏବଂ ହର୍ଜଗୋଭିନା,ବୋସନିଆ ଓ ହର୍ଜଗୋଭିନା,பொசுனியா எர்செகோவினா,போஸ்னியா மற்றும் ஹெர்ஸிகோவினா,బాస్నియా మరియు హీర్జిగోవినా,బోస్నియా మరియు హెర్జెగొవీనా,ಬೊಸ್ನಿಯ ಮತ್ತು ಹೆರ್ಜೆಗೊವಿನ,ಬೋಸ್ನಿಯಾ ಮತ್ತು ಹರ್ಜೆಗೋವಿನಾ,ബോസ്നിയ ഹെർസെഗോവിന,ബോസ്നിയയും ഹെര്‍സഗോവിനയും,බොස්නියා සහ හර්සගෝවිනා,බොස්නියාව සහ හර්සගොවීනාව,บอสเนียและเฮอร์เซโกวีนา,ประเทศบอสเนียและเฮอร์เซโกวีนา,ບັອດສເນຍ ແລະ ເຮີດໂກວິເນຍ,ປະເທດບົດສະນີແຮກເຊໂກວີນ,བོསྣི་ཡ་དང་ཧརྫོ་གོ་ཝི་ན།,བྷོསུ་ནིཡ་དང་ཧར་ཛེ་གྷོ་ཝི་ན།,ဘော့စနီးယား နှင့် ဟာဇီဂိုဘီးနား,ဘော့စနီးယားနှင့် ဟာဇီဂိုဗီးနားနိုင်ငံ,ბოსნია და ჰერცეგოვინა,ბოსნია დო ჰერცეგოვინა,ቦስኒያ እና ሄርዞጎቪኒያ,ቦስኒያና ሄርጸጎቪና,ᏆᏍᏂᏯ ᎠᎴ ᎲᏤᎪᏫᎾ,បូស្ន៉ី,ボスニア・ヘルツェゴビナ,ボスニア・ヘルツェゴビナ共和国,波斯尼亚,波斯尼亚和黑塞哥维那,波斯尼亚和黑山共和国,波斯尼亞,보스니아 헤르체고비나,보스니아헤르체고비나 44.25 17.83333 A PCLI BA 00 4590000 876 Europe/Sarajevo 2013-12-01 5 | 3427213 Zárate Zarate General J.F. Uriburu,General Jose F. Uriburu,General José F. Uriburu,General Uriburu,Sarate,Saratė,Zarate,Zárate,Зарате -34.09814 -59.02858 P PPLA2 AR 01 3427212 88781 28 America/Argentina/Buenos_Aires 2014-03-07 6 | 6255147 Asia Asia Aasia,Asia,Asie,Asien,Asya,Asía,Azie,Azija,Azio,Azië,Azja,Azsia,Chau A,Châu Á,ajia,an Aise,an Áise,asia,asya,ayshya,esiya mahadvipa,xecheiy,ya zhou,Àsia,Ásia,Ázsia,Āzija,Ασία,Азия,Азія,אסיה,آسيا,ایشیا,एशिया महाद्वीप,เอเชีย,アジア,亚洲,아시아 29.84064 89.29688 L CONT 3812366000 5101 Australia/Perth 2014-06-10 7 | 1283416 Mount Everest Mount Everest Ai-fo-lo-ssu Feng,Bantay Everest,Beinn Everest,Bukid Everest,Bundok Everest,Chomo-lungma,Chomolangma Hong,Chomolongma,Chomolungma,Chu-muk-long-ma-fung,Chû-mu̍k-lòng-mâ-fûng,Ciyaye Everest,Comolungmo,Csomolungma,Djomo-lungma,Dzhomolungma,Dzhomolungmae,Dzomolungma,Džomolungma,Ehverest,Everest,Everest Dagi,Everest Dağı,Everest dagi,Everest dağı,Everestfjall,Everestius mons,Everests,GJinh Everest,Gara Dzhamalungma,Gunung Everest,Jomolungma,Lapchi-kang,Mali Everest,Menez Everest,Menydh Everest,Mont Everest,Monte Everest,Monto Everest,Mount Everest,Mount Jolmo Lungma,Mt. Everest,Mynydd Everest,Peak XV,Qomolangma,Qomolangma Feng,Sagar-Matha,Sagarmatha,Sagarmāthā,Sheng-mu Feng,Shengmu Feng,Shèngmǔ Fēng,Sliabh Everest,Zhomolungma,Zhumulangma Feng,Zhūmùlǎngmǎ Fēng,awrst,ayfyryst,debadurga,ebeleseuteu san,eberesuto,evarecuttu cikaram,evaresta parvata,evarestu parvatam,evharesta,jbl afrst,jomolungma,kwh awrst,ma'unta aivaraisata,ma'unta ebharesta,ma'unta evaresta,maunt evarest,phu khea xef wexr res,sagaramatha,sagaramatha ancala,yxd khea xe wexrest,zhu mu lang ma feng,Çiyayê Everest,Éverest,Ĉomolungmo,Đỉnh Everest,Έβερεστ,Гара Джамалунгма,Джомолунгмæ,Джомолунгма,Дьомолууҥма,Еверест,Жомолунгма,Монт Еверест,Эверест,Էվերեստ,אוורסט,עווערעסט,ئېۋېرېست,اورست,ايفيريست,جبل إفرست,ماؤنٹ ایورسٹ,چیای ئێڤەرست,کوه اورست,އެވަރެސްޓް ފަރުބަދަ,एवरेस्ट पर्वत,एव्हरेस्ट,सगरमाथा,सगरमाथा अञ्चल,দেবদুর্গ,মাউন্ট এভারেস্ট,ਮਾਊਂਟ ਐਵਰੈਸਟ,માઉન્ટ એવરેસ્ટ,ମାଉଣ୍ଟ ଏଭରେଷ୍ଟ,எவரெசுட்டு சிகரம்,ఎవరెస్టు పర్వతం,ಮೌಂಟ್ ಎವರೆಸ್ಟ್,എവറസ്റ്റ്‌ കൊടുമുടി,එවරස්ට් කන්ද,ภูเขาเอฟเวอร์เรส,ยอดเขาเอเวอเรสต์,ཇོ་མོ་གླང་མ།,ဧဝရတ်တောင်,ევერესტი,ჯომოლუნგმა,ኤቨረስት ተራራ,エベレスト,珠穆朗玛峰,珠穆朗瑪峰,에베레스트 산 27.98791 86.92529 T MT NP 00 0 8848 8794 Asia/Kathmandu 2010-08-04 8 | -------------------------------------------------------------------------------- /DumpTester/Program.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames; 2 | using System; 3 | using System.Configuration; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Text.RegularExpressions; 8 | 9 | namespace DumpTester 10 | { 11 | /// 12 | /// Quick'n'Dirty program to download all dumpfiles and iterate over all of them to ensure parsing works for 13 | /// all files. Use at own risk! 14 | /// 15 | /// 16 | /// This code is full of hacks / shortcuts / sloppy work; it's NOT intended to be used in production. This project 17 | /// is a simple "brute force" approach to test all of the files (by forcing the parsers to parse each record) made 18 | /// available by geonames.org. 19 | /// 20 | class Program 21 | { 22 | private static string Dump_DownloadDirectory = ConfigurationManager.AppSettings["dump_downloaddirectory"]; 23 | private static string Postal_DownloadDirectory = ConfigurationManager.AppSettings["postal_downloaddirectory"]; 24 | 25 | static void Main(string[] args) 26 | { 27 | //Test GeoName dumps 28 | var dumpdownloader = GeoFileDownloader.CreateGeoFileDownloader(); 29 | var dumpfiles = GetDumps(dumpdownloader); 30 | 31 | dumpfiles.AsParallel().ForAll(g => 32 | { 33 | Console.WriteLine("Download: {0}", g.Filename); 34 | dumpdownloader.DownloadFile(g.Filename, Dump_DownloadDirectory); 35 | Console.WriteLine("Testing {0}: {1}", g.Filename, g.Test(Path.Combine(Dump_DownloadDirectory, g.Filename))); 36 | }); 37 | 38 | //Test Postalcode dumps 39 | var postalcodedownloader = GeoFileDownloader.CreatePostalcodeDownloader(); 40 | var postalcodefiles = GetCountryPostalcodes(postalcodedownloader); 41 | 42 | postalcodefiles.AsParallel().ForAll(g => 43 | { 44 | Console.WriteLine("Download: {0}", g.Filename); 45 | postalcodedownloader.DownloadFile(g.Filename, Postal_DownloadDirectory); 46 | Console.WriteLine("Testing {0}: {1}", g.Filename, g.Test(Path.Combine(Postal_DownloadDirectory, g.Filename))); 47 | }); 48 | 49 | Console.WriteLine("Testing ASCII fields"); 50 | DumpASCIILies(Dump_DownloadDirectory); 51 | 52 | Console.WriteLine("All done!"); 53 | } 54 | 55 | private static GeoFile[] GetCountryPostalcodes(GeoFileDownloader downloader) 56 | { 57 | var w = new WebClient(); 58 | var document = w.DownloadString(downloader.BaseUri); 59 | 60 | var countries = new Regex("href=\"([A-Z]{2}.zip)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) 61 | .Matches(document) 62 | .Cast() 63 | .Select(m => new GeoFile { Filename = m.Groups[1].Value, Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadPostalcodes(fn).Count(); }) }); 64 | 65 | return new[] { 66 | new GeoFile { Filename = "allCountries.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadPostalcodes(fn).Count(); }) } 67 | }.Union(countries.OrderBy(m => m.Filename)).ToArray(); 68 | } 69 | 70 | private static GeoFile[] GetDumps(GeoFileDownloader downloader) 71 | { 72 | return new[] { 73 | new GeoFile { Filename = "admin1CodesASCII.txt", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadAdmin1Codes(fn).Count(); }) }, 74 | new GeoFile { Filename = "admin2Codes.txt", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadAdmin2Codes(fn).Count(); }) }, 75 | new GeoFile { Filename = "allCountries.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadExtendedGeoNames(fn).Count(); }) }, 76 | new GeoFile { Filename = "alternateNames.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadAlternateNames(fn).Count(); }) }, 77 | new GeoFile { Filename = "alternateNamesV2.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadAlternateNamesV2(fn).Count(); }) }, 78 | new GeoFile { Filename = "cities1000.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadExtendedGeoNames(fn).Count(); }) }, 79 | new GeoFile { Filename = "cities15000.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadExtendedGeoNames(fn).Count(); }) }, 80 | new GeoFile { Filename = "cities5000.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadExtendedGeoNames(fn).Count(); }) }, 81 | new GeoFile { Filename = "countryInfo.txt", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadCountryInfo(fn).Count(); }) }, 82 | //Featurecodes are downloaded by GetCountryDumps() 83 | new GeoFile { Filename = "hierarchy.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadHierarchy(fn).Count(); }) }, 84 | new GeoFile { Filename = "iso-languagecodes.txt", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadISOLanguageCodes(fn).Count(); }) }, 85 | new GeoFile { Filename = "no-country.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadExtendedGeoNames(fn).Count(); }) }, 86 | new GeoFile { Filename = "timeZones.txt", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadTimeZones(fn).Count(); }) }, 87 | new GeoFile { Filename = "userTags.zip", Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadUserTags(fn).Count(); }) }, 88 | }.Union(GetCountryDumps(downloader)).ToArray(); 89 | } 90 | 91 | private static GeoFile[] GetCountryDumps(GeoFileDownloader downloader) 92 | { 93 | var w = new WebClient(); 94 | var document = w.DownloadString(downloader.BaseUri); 95 | 96 | var countries = new Regex("href=\"([A-Z]{2}.zip)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) 97 | .Matches(document) 98 | .Cast() 99 | .Select(m => new GeoFile { Filename = m.Groups[1].Value, Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadExtendedGeoNames(fn).Count(); }) }); 100 | 101 | var featurecodes = new Regex("href=\"(featureCodes_[A-Z]{2}.txt)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase) 102 | .Matches(document) 103 | .Cast() 104 | .Select(m => new GeoFile { Filename = m.Groups[1].Value, Test = (f) => ExecuteTest(f, (fn) => { return GeoFileReader.ReadFeatureCodes(fn).Count(); }) }); 105 | 106 | return countries.Union(featurecodes).OrderBy(m => m.Filename).ToArray(); 107 | } 108 | 109 | private static string ExecuteTest(string filename, Func test) 110 | { 111 | try 112 | { 113 | //Haaaack 114 | var file = filename.Replace(".zip", ".txt"); 115 | 116 | //Haaaaaaaaaack 117 | //if (file.EndsWith("no-country.txt")) 118 | // file = file.Replace("no-country.txt", "null.txt"); 119 | 120 | return string.Format("{0} records OK", test(file)); 121 | } 122 | catch (Exception ex) 123 | { 124 | return string.Format("FAILED: {0}", ex.Message); 125 | } 126 | } 127 | 128 | private static void DumpASCIILies(string logpath) 129 | { 130 | using (var lw = File.CreateText(Path.Combine(logpath, "_asciilies.log"))) 131 | { 132 | 133 | //Test for fields that claim to contain ASCII only but contain non-ASCII data anyways 134 | var nonasciifilter = new Regex("[^\x20-\x7F]", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 135 | var geofilefilter = new Regex("^[A-Z]{2}.txt$", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 136 | 137 | lw.WriteLine("The following files contain entries that claim to contain ASCII only but contain non-ASCII data anyways:"); 138 | 139 | var extgeofiles = new[] { "allCountries", "cities1000", "cities5000", "cities15000", "no-country" } 140 | .Select(f => Path.Combine(Dump_DownloadDirectory, f + ".txt")) 141 | .Union(Directory.GetFiles(Dump_DownloadDirectory, "*.txt") 142 | .Where(f => geofilefilter.IsMatch(Path.GetFileName(f))) 143 | ); 144 | 145 | var lies = extgeofiles.AsParallel() 146 | .SelectMany(f => GeoFileReader.ReadExtendedGeoNames(f) 147 | .Where(e => nonasciifilter.IsMatch(e.NameASCII)) 148 | .Select(i => new NonASCIIEntry { FileName = f, Id = i.Id, Value = i.NameASCII }) 149 | ).Union( 150 | GeoFileReader.ReadAdmin1Codes(Path.Combine(Dump_DownloadDirectory, "admin1CodesASCII.txt")).AsParallel() 151 | .Where(c => nonasciifilter.IsMatch(c.NameASCII)) 152 | .Select(i => new NonASCIIEntry { FileName = "admin1CodesASCII.txt", Id = i.GeoNameId, Value = i.NameASCII }) 153 | ).Union( 154 | GeoFileReader.ReadAdmin2Codes(Path.Combine(Dump_DownloadDirectory, "admin2Codes.txt")).AsParallel() 155 | .Where(c => nonasciifilter.IsMatch(c.NameASCII)) 156 | .Select(i => new NonASCIIEntry { FileName = "admin2Codes.txt", Id = i.GeoNameId, Value = i.NameASCII }) 157 | ); 158 | 159 | foreach (var l in lies.OrderBy(l => l.FileName).ThenBy(l => l.Value)) 160 | { 161 | lw.WriteLine(string.Join("\t", Path.GetFileName(l.FileName), l.Id, l.Value)); 162 | }; 163 | 164 | } 165 | } 166 | } 167 | 168 | class GeoFile 169 | { 170 | public string Filename { get; set; } 171 | public Func Test { get; set; } 172 | 173 | public GeoFile() 174 | { 175 | this.Test = (f) => { throw new NotImplementedException(); }; 176 | } 177 | } 178 | 179 | class NonASCIIEntry 180 | { 181 | public string FileName { get; set; } 182 | public string Value { get; set; } 183 | public int Id { get; set; } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /NGeoNames/ReverseGeoCode.cs: -------------------------------------------------------------------------------- 1 | using NGeoNames.Entities; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace NGeoNames 6 | { 7 | /// 8 | /// Provides reverse geocoding methods. 9 | /// 10 | /// The type of elements in ReverseGeoCoder. 11 | public class ReverseGeoCode 12 | where T : IGeoLocation, new() 13 | { 14 | private KdTree.KdTree _tree; 15 | 16 | /// 17 | /// Initializes a new, empty object. 18 | /// 19 | public ReverseGeoCode() 20 | : this(Enumerable.Empty()) { } 21 | 22 | /// 23 | /// Initializes a new object, populating the internal structures with the provided 24 | /// s. 25 | /// 26 | /// 27 | /// An IEnumerable{T} of to populate the with. 28 | /// 29 | /// 30 | /// This constructor will, internally, call the method when the internal structure is initialized. 31 | /// 32 | public ReverseGeoCode(IEnumerable nodes) 33 | { 34 | _tree = new KdTree.KdTree(3, new DoubleMath()); 35 | AddRange(nodes); 36 | 37 | Balance(); 38 | } 39 | 40 | /// 41 | /// Adds a node to the internal structure. 42 | /// 43 | /// The to add. 44 | public void Add(T node) 45 | { 46 | _tree.Add(GeoUtil.GetCoord(node), node); 47 | } 48 | 49 | 50 | /// 51 | /// Adds the specified nodes to the internal structure. 52 | /// 53 | /// An IEnumerable{T} of s to add. 54 | public void AddRange(IEnumerable nodes) 55 | { 56 | foreach (var i in nodes) 57 | Add(i); 58 | } 59 | 60 | /// 61 | /// Balance the internal structure (KD-tree). 62 | /// 63 | /// 64 | /// This method only needs to be called when all nodes have been added; it *can* be called earlier but is 65 | /// not of much use. Note that the calls this 66 | /// method automatically; no need to call it when using this constructor. 67 | /// 68 | public void Balance() 69 | { 70 | if (_tree.Count > 0) 71 | _tree.Balance(); 72 | } 73 | 74 | /// 75 | /// Performs a radial search on the nodes from a specified center limiting the number of results. 76 | /// 77 | /// The latitude of the search center. 78 | /// The longitude of the search center. 79 | /// The maximum number of results to return. 80 | /// Returns up to maxcount nodes matching the radial search. 81 | public IEnumerable RadialSearch(double lat, double lng, int maxcount) 82 | { 83 | return RadialSearch(CreateFromLatLong(lat, lng), maxcount); 84 | } 85 | 86 | /// 87 | /// Performs a radial search on the nodes from a specified center limiting the number of results. 88 | /// 89 | /// The center of the search. 90 | /// The maximum number of results to return. 91 | /// Returns up to maxcount nodes matching the radial search. 92 | public IEnumerable RadialSearch(T center, int maxcount) 93 | { 94 | return RadialSearch(center, double.MaxValue, maxcount); 95 | } 96 | 97 | /// 98 | /// Performs a radial search on the nodes from a specified center within a given radius. 99 | /// 100 | /// The latitude of the search center. 101 | /// The longitude of the search center. 102 | /// The radius to search in. 103 | /// Returns the nodes matching the radial search. 104 | public IEnumerable RadialSearch(double lat, double lng, double radius) 105 | { 106 | return RadialSearch(CreateFromLatLong(lat, lng), radius); 107 | } 108 | 109 | /// 110 | /// Performs a radial search on the nodes from a specified center within a given radius. 111 | /// 112 | /// The center of the search. 113 | /// The radius to search in. 114 | /// Returns the nodes matching the radial search. 115 | public IEnumerable RadialSearch(T center, double radius) 116 | { 117 | return RadialSearch(center, radius, _tree.Count); 118 | } 119 | 120 | /// 121 | /// Performs a radial search on the nodes from a specified center within a given radius limiting the number of 122 | /// results. 123 | /// 124 | /// The latitude of the search center. 125 | /// The longitude of the search center. 126 | /// The radius to search in. 127 | /// The maximum number of results to return. 128 | /// Returns up to maxcount nodes matching the radial search. 129 | public IEnumerable RadialSearch(double lat, double lng, double radius, int maxcount) 130 | { 131 | return RadialSearch(CreateFromLatLong(lat, lng), radius, maxcount); 132 | } 133 | 134 | /// 135 | /// Performs a radial search on the nodes from a specified center within a given radius limiting the number of 136 | /// results. 137 | /// 138 | /// The center of the search. 139 | /// The radius to search in. 140 | /// The maximum number of results to return. 141 | /// Returns up to maxcount nodes matching the radial search. 142 | public IEnumerable RadialSearch(T center, double radius, int maxcount) 143 | { 144 | return _tree.RadialSearch(GeoUtil.GetCoord(center), radius, maxcount).Select(v => v.Value); 145 | } 146 | 147 | /// 148 | /// Performs a nearest neighbour search on the nodes from a specified center. 149 | /// 150 | /// The latitude of the search center. 151 | /// The longitude of the search center. 152 | /// Returns nodes in matching order of the nearest neighbour search. 153 | public IEnumerable NearestNeighbourSearch(double lat, double lng) 154 | { 155 | return NearestNeighbourSearch(CreateFromLatLong(lat, lng), _tree.Count); 156 | } 157 | 158 | /// 159 | /// Performs a nearest neighbour search on the nodes from a specified center limiting the number of results. 160 | /// 161 | /// The latitude of the search center. 162 | /// The longitude of the search center. 163 | /// The maximum number of results to return. 164 | /// Returns up to maxcount nodes in matching order of the nearest neighbour search. 165 | public IEnumerable NearestNeighbourSearch(double lat, double lng, int maxcount) 166 | { 167 | return NearestNeighbourSearch(CreateFromLatLong(lat, lng), maxcount); 168 | } 169 | 170 | /// 171 | /// Performs a nearest neighbour search on the nodes from a specified center. 172 | /// 173 | /// The center of the search. 174 | /// Returns nodes in matching order of the nearest neighbour search. 175 | public IEnumerable NearestNeighbourSearch(T center) 176 | { 177 | return NearestNeighbourSearch(center, _tree.Count); 178 | } 179 | 180 | /// 181 | /// Performs a nearest neighbour search on the nodes from a specified center limiting the number of results. 182 | /// 183 | /// The center of the search. 184 | /// The maximum number of results to return. 185 | /// Returns up to maxcount nodes in matching order of the nearest neighbour search. 186 | public IEnumerable NearestNeighbourSearch(T center, int maxcount) 187 | { 188 | return _tree.GetNearestNeighbours(GeoUtil.GetCoord(center), maxcount).Select(v => v.Value); 189 | } 190 | 191 | /// 192 | /// Creates a from a latitude and longitude. 193 | /// 194 | /// The latitude of the object. 195 | /// The longitude of the object. 196 | /// Returns a , or derived. 197 | public T CreateFromLatLong(double lat, double lng) 198 | { 199 | return new T() 200 | { 201 | Latitude = lat, 202 | Longitude = lng 203 | }; 204 | } 205 | } 206 | 207 | /// 208 | /// This class is for internal use (Kd-Tree) only. 209 | /// 210 | internal class DoubleMath : KdTree.Math.TypeMath 211 | { 212 | public override double Add(double a, double b) { return a + b; } 213 | public override bool AreEqual(double a, double b) { return a == b; } 214 | public override int Compare(double a, double b) { return a.CompareTo(b); } 215 | public override double MaxValue { get { return double.MaxValue; } } 216 | public override double MinValue { get { return double.MinValue; } } 217 | public override double Multiply(double a, double b) { return a * b; } 218 | public override double NegativeInfinity { get { return double.NegativeInfinity; } } 219 | public override double PositiveInfinity { get { return double.PositiveInfinity; } } 220 | public override double Subtract(double a, double b) { return a - b; } 221 | public override double Zero { get { return 0; } } 222 | public override double DistanceSquaredBetweenPoints(double[] a, double[] b) 223 | { 224 | double distance = Zero; 225 | int dimensions = a.Length; 226 | 227 | // Return the absolute distance between 2 hyper points 228 | for (var dimension = 0; dimension < dimensions; dimension++) 229 | { 230 | double distOnThisAxis = Subtract(a[dimension], b[dimension]); 231 | double distOnThisAxisSquared = Multiply(distOnThisAxis, distOnThisAxis); 232 | 233 | distance = Add(distance, distOnThisAxisSquared); 234 | } 235 | 236 | return distance; 237 | } 238 | } 239 | } 240 | --------------------------------------------------------------------------------