Wikipedia:Projekt DotNetWikiBot Framework/Lsjbot/Make-Geonames – Wikipedia
Hoppa till innehållet
Från Wikipedia
Wikipedia:Projekt DotNetWikiBot Framework
Lsjbot
//Originally from [[Wikipedia:Projekt DotNetWikiBot Framework/Lsjbot/Makespecies]]
//Extensively modified by Lsj

using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Threading;
using System.Web;
using System.Net;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;

//using Catfood.Shapefile;

//using System.Math;

using DotNetWikiBot;

class MyBot : Bot
public static string geonamesfolder = @"C:\dotnwb3\Geonames\"; //will be modified according to computer name
public static string extractdir = @"O:\dotnwb3\extract\"; //will be modified according to computer name

public static string botname = "Lsjbot";
public static string makelang = "sv";
//public static string makecountry = "AD,AE,AF,AG,AI,AL,AM,AO,AQ,AR,AS,AT,AU,AW,AX,AZ,BA,BB,BD,BE,BF,BG,BH,BI,BJ,BL,BM,BN,BO,BQ,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CD,CF,CG,CH,CI,CK,CL,CM,CN,CO,CR,CU,CV,CW,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,ES,ET,FI,FJ,FK,FM,FO,FR,GA,GB,GD,GE,GF,GG,GH,GI,GL,GM,GN,GP,GQ,GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IM,IN,IO,IQ,IR,IS,IT,JE,JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,XK,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,LT,LU,LV,LY,MA,MC,MD,ME,MF,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV,MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,PH,PK,PL,PM,PN,PR,PS,PT,PW,PY,QA,RE,RO,RS,RU,RW,SA,SB,SC,SD,SS,SE,SG,SH,SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SX,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TL,TM,TN,TO,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,WS,YE,YT,ZA,ZM,ZW"; //Can be comma-separated list. Must be same number of components in the following three strings.
//public static string makecountry = "AG,MT,MK,SS,BH,BT,LU,AD,AE,AF,AI,AL,AM,AO,AQ,AR,AS,AT,AU,AW,AX,AZ,BA,BB,BD,BE,BF,BG,BI,BJ,BL,BM,BN,BO,BQ,BR,BS,BV,BW,BY,BZ,CA,CC,CD,CF,CG,CH,CI,CK,CL,CM,CN,CO,CR,CU,CV,CW,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,ES,ET,FI,FJ,FK,FM,FO,FR,GA,GB,GD,GE,GF,GG,GH,GI,GL,GM,GN,GP,GQ,GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IM,IN,IO,IQ,IR,IS,IT,JE,JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,XK,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,LT,LU,LV,LY,MA,MC,MD,ME,MF,MG,MH,ML,MM,MN,MO,MP,MQ,MR,MS,MU,MV,MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,PH,PK,PL,PM,PN,PR,PS,PT,PW,PY,QA,RE,RO,RS,RU,RW,SA,SB,SC,SD,SE,SG,SH,SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SX,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TL,TM,TN,TO,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,WS,YE,YT,ZA,ZM,ZW"; //Can be comma-separated list. Must be same number of components in the following three strings.
public static string makecountry = "CA";//,AU,AW,AX,AZ";//"RU,UA,BY,RS,MK,KZ,KG,MN,BG,TJ";
public static string makecountryname = "";//"Afghanistan,Albania,Antarctica,Armenia,Andorra,Burundi,Nicaragua,Bahamas,Macedonia,South Sudan,Bhutan,Luxembourg,Malta,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr,fr"; //Används av Makefork, samma engelska namnform som i namefork-filen
//public static string makecountrywiki = "en";//"ru,ua,be,sr,mk,kk,ky,mn,bg,tg";
//public static string makecountrywiki ="ca,ar,fa,en,en,sq,hy,pt,en,es,en,de,en,nl,fi,az,bs,en,bn,nl,fr,bg,ar,fr,fr,fr,en,ms,es,nl,pt,en,dz,en,en,be,en,en,ms,fr,fr,fr,de,fr,en,es,en,zh,es,es,es,pt,nl,en,el,cs,de,fr,da,en,es,ar,es,et,ar,ar,aa,es,am,fi,en,en,en,fo,fr,fr,en,en,ka,fr,en,en,en,kl,en,fr,fr,es,el,en,es,en,pt,en,zh,en,es,hr,ht,hu,id,en,he,en,en,en,ar,fa,is,it,en,en,ar,ja,en,ky,km,en,ar,en,ko,ko,sq,ar,en,kk,lo,ar,en,de,si,en,en,lt,lb,lv,ar,ar,fr,ro,sr,fr,fr,mh,mk,fr,my,mn,zh,fi,fr,ar,en,mt,en,dv,ny,es,ms,pt,en,fr,fr,en,en,es,nl,no,ne,na,ni,en,ar,es,es,fr,en,tl,ur,pl,fr,en,en,ar,pt,pa,es,ar,fr,ro,sr,ru,rw,ar,en,en,ar,en,sv,cm,en,sl,no,sk,en,it,fr,so,nl,pt,es,nl,ar,en,en,fr,fr,fr,th,tg,tk,te,tk,ar,to,tr,en,tv,zh,sw,uk,en,en,en,es,uz,la,en,es,en,en,vi,bi,wl,sm,ar,fr,zu,en,en"; //Används för iw
public static int resume_at = -1; //-1: run from start; gnid to resume at.
public static int stop_at = -1; //-1: run to end; gnid to stop at.
public static string resume_at_fork = ""; //resume at for fork pages; empty string to run from start
public static string testprefix = "";
//public static string nameforkdate = "20150803";
public static char createclass = ' '; //create only articles with this feature class
public static string createfeature = ""; //create only articles with this feature code
public static string createexceptfeature = ""; //create all articles EXCEPT with this feature code
public static int createunit = -1; //create only articles for place in admin.unit with gnid=createunit; <0 to make all
public static int createexceptunit = -1; //create only articles for place NOT in admin.unit with gnid=createexceptunit; <0 to make all

//======================================================
// Flags setting the main task for a run.
// Unpredictable results if multiple flags are true.
//======================================================
public static bool makearticles = false; //create wiki pages
public static bool makespecificarticles = false; //create specific wiki pages, one by one
public static bool remakearticleset = false; //remake a selected set of articles; overwrite must be true
public static bool altnamesonly = false; //create namefork file
public static bool makefork = false; //create fork pages on wiki
public static bool checkdoubles = false; //create artname file
public static bool checkwikidata = false; //create wikidata-XX files
public static bool makeislands = false; //create islands-XX files
public static bool makelakes = false; //create lakes-XX files
public static bool makeranges = false; //create ranges-XX files
public static bool verifygeonames = false; //doublecheck geonames against wiki
public static bool verifywikidata = false; //doublecheck wikidata
public static bool verifyislands = false; //verify island files, removing duplicates
public static bool verifylakes = false; //verify island files, removing duplicates
public static bool makealtitude = false; //make altitude_XX files
public static bool maketranslit = false; //make translit_XX files
public static bool worldmaponly = false; //create world map
public static bool statisticsonly = false; //gather statistics without actually making anything; can be combined with other options
public static bool savefeaturelink =false; //save a list of what feature codes link to
public static bool savewikilinks = false; //save a list of what the bot wikilinks to
public static bool saveadmlinks = false; //save a list of what the bot wikilinks to
public static bool manualcheck = false; //check name matches manually, if true
public static bool listnative = false; //list article names from native wiki of a country
public static bool forkduplicates = false; //make namefork-duplicates file
public static bool fixsizecats = false; //fix size categories
public static bool testnasa = false; //test nasa data
public static bool retrofitnasa = true; // retrofit existing articles with nasa data

public static bool prefergeonamespop = true; //use population figures from GeoNames rather than wiki
public static bool makedoubles = true; //if suspected double, make article as a copy in doubleprefix folder
public static bool overwrite = false; //if article already exists, overwrite with a new version (if not human-edited)

public static bool reallymake = true; //if false, do dry run without actually loading from or saving to wiki; can be combined with other options
public static bool pauseaftersave = false; //if true, wait for keypress after saving each article

public static bool firstround = true;

public static int maxread = 100000000; //Set to a small number for a limited test run, or to 100000000 for a full run

public class geonameclass //main class for GeoNames entries
public string Name = ""; //main name
public string Name_ml = ""; //name in makelang language
public string asciiname = ""; //name in plain ascii
public List altnames = new List(); //alternative names
public double latitude = 0.0;
public double longitude = 0.0;
public char featureclass;
public string featurecode;
//public string countrycode = "XX"; //GeonameID of country in adm[0]!
public List altcountry = new List();
public List children = new List(); //index of children
public List features = new List(); //index of features in adm-unit
public int[] adm = new int[5]; // adm[0] = country, adm[1] = province, etc.
public long population = 0; //pop according to geonames
public long population_wd = 0; //pop according to wikipedia
public string population_wd_iw = ""; //language from which pop is taken
public int elevation = 0;
public int elevation_vp = 0;
public int prominence = 0;
public double width = 0; //largest horizontal dimension in km
public int island = 0; //if located on island, gnid of island
public int inlake = 0; //if located in lake, gnid of lake
public int inrange = 0; //if mountain part of range, gnid of range
public List atlakes = new List(); //near the shore of lake(s) in list
public double area = 0.0; //area from wikipedia

public int dem = 0;
public string tz = "";
public string moddate = "1901-01-01";

public string articlename = ""; //article name, with disambiguation
public string unfixedarticlename = ""; //article name before fixing oddities
public string oldarticlename = ""; //article name in previous bot run
public string artname2 = ""; //article name in other bot language, for interwiki linking
public int wdid = -1; //wikidata id

public class forkclass //class for entries in a fork page
public int geonameid = 0;
public string featurecode = "";
public string[] admname = new string[3];
public double latitude = 0.0;
public double longitude = 0.0;
public string realname = "*";
public int wdid = -1; //wikidata id
public string iso = "XX"; //country iso code
public string featurename = "";

public class altnameclass //class for alternate names from GeoNames
public int altid = 0;
public string altname = "";
public int ilang = -1;
public string wikilink = "";
public bool official = false;
public bool shortform = false;
public bool colloquial = false;
public bool historic = false;

public class countryclass //class for country data
public string Name = ""; //main name
public string Name_ml = ""; //name in makelang language
public string asciiname = ""; //name in plain ascii
public List altnames = new List(); //alternative names
public string iso = "XX";
public string iso3 = "XXX";
public int isonumber = 0;
public string fips = "XX";
public string capital = "";
public int capital_gnid = 0;
public double area = 0.0;
public long population = 0;
public string continent = "EU";
public string tld = ".xx";
public string currencycode = "USD";
public string currencyname = "Dollar";
public string phone = "1-999";
public string postalcode = "#####";
public string nativewiki = "";
public List languages = new List();
public List bordering = new List();

public class langclass
public string iso3 = "";
public string iso2 = "";
public Dictionary name = new Dictionary(); //name of language in different languages. Iso -> name.

public class admclass //names of administrative entities by level, one admclass for each country
public string[] label = new string[5];
public string[] det = new string[5];
public int maxadm = 5;

public class tzclass //time zone class
public string offset = "0";
public string summeroffset = "0";
public string rawoffset = "0";
public string tzname = "";
public string tzsummer = "";
public string tzfull = "";
public string tzfullsummer = "";

public class islandclass //data for each island
public double area = 0;
public double kmew = 0;
public double kmns = 0;
public List onisland = new List(); //list of GeoNames id of entities located on the island.

public class rangeclass //data for each MTS/HLLS
public double length = 0;
public string orientation = "....";
public double angle = 0; //polar angle of long axis (radians). 0 or pi = EW, pi/2 or 3pi/2 = NS etc.
public double kmew = 0;
public double kmns = 0;
public int maxheight = 0; //highest point; gnid of peak if negative, height if positive
public double hlat = 999; //latitude/longitude of highest point
public double hlon = 999;
public List inrange = new List(); //list of GeoNames id of mountains in the range.

public class lakeclass //data for each lake
public double area = 0;
public double kmew = 0;
public double kmns = 0;
public List inlake = new List(); //list of GeoNames id of entities located in the lake (mainly islands).
public List atlake = new List(); //list of GeoNames id of entities located around the lake.

public class Disambigclass //class for disambiguation in article names
public bool existsalready = false;
public bool country = false;
public bool adm1 = false;
public bool adm2 = false;
public bool latlong = false;
public bool fcode = false;
public forkclass fork = new forkclass();

public class wdminiclass //minimal wikidata entry needed for verifying Geonames-links
public int gnid = 0;
public double latitude = 9999.9;
public double longitude = 9999.9;
public List instance_of = new List();
public double dist = 9999.9;
public bool okdist = false;
public bool okclass = false;
public bool goodmatch = false;

public class existingclass //for existing geography articles on target wiki
public string articlename = "";
public double latitude = 9999.9;
public double longitude = 9999.9;

public class nasaclass
public int landcover = -1; //Landcover code 1-17 http://eospso.nasa.gov/sites/default/files/atbd/atbd_mod12.pdf
public int popdensity = -1; //people per square km
public int temp_average = -999; //average across months and day-night
public int temp_max = -999; //temp of hottest month
public int month_max = -999; //hottest month (1-12)
public int temp_min = -999; //temp of coldest month
public int month_min = -999; //coldest month
public int temp_daynight = -999; //average difference between day and night
public int rainfall = -999; //mm per year
public int rain_max = -999; //rain wettest month
public int rain_month_max = -999; //wettest month (1-12)
public int rain_min = 99999; //rain driest month
public int rain_month_min = -999; //driest month
public double rainfall_double = 0; //mm per year
public int koppen = -1;
public int[] month_temp_day = new int[13] { -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999 };
public int[] month_temp_night = new int[13] { -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999 };
public int[] month_rain = new int[13] { -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999 };

public class chinese_pop_class
public int adm1 = -1;
public long pop = -1;
public long malepop = -1;
public long femalepop = -1;
public long households = -1;
public long pop014 = -1;
public long pop1564 = -1;
public long pop65 = -1;

public class locatorclass //for locator maps
public string locatorname = ""; //country name in locator template on target wiki
public string locatorimage = "";//map image name
public string altlocator = "";
public double latmin = -999;
public double latmax = -999;
public double lonmin = -999;
public double lonmax = -999;
public bool loaded = false;
public string get_locator(double lat,double lon)
if (!loaded)
string templatename = mp(63, null) + mp(72, null).Replace("{{", "") + " " + locatorname;
Console.WriteLine(templatename);
//string imagename = "";
Page ltp = new Page(makesite, templatename);
tryload(ltp, 2);
if (ltp.Exists())
locatorimage = get_pictureparam(ltp);
latmax = get_edgeparam(ltp, "top");
latmin = get_edgeparam(ltp, "bottom");
lonmax = get_edgeparam(ltp, "right");
lonmin = get_edgeparam(ltp, "left");
loaded = true;

if (String.IsNullOrEmpty(altlocator))
if (String.IsNullOrEmpty(locatorname))
if (makelang == "sv")
return "Världen";
else
return "World";
else
return locatorname;

if (latmin < -99) //failed to get edges, probably complicated coordinates
return locatorname;

if (lat < latmin)
return altlocator;
if (lat > latmax)
return altlocator;
if (lon < lonmin)
return altlocator;
if (lon > lonmax)
return altlocator;
return locatorname;

public static Dictionary gndict = new Dictionary();
public static Dictionary countrydict = new Dictionary(); //from geoname ID to country info
public static Dictionary countryid = new Dictionary(); //from ISO code to geoname ID for countries
public static Dictionary countryml = new Dictionary(); // from English name to makelang name
public static Dictionary countryiso = new Dictionary(); // from English name to ISO
public static Dictionary locatordict = new Dictionary(); // from English name to locator map name
//public static Dictionary locatorimage = new Dictionary(); //from English name to locator map image
public static Dictionary> namefork = new Dictionary>(); //names with list of corresponding geonameid(s)
public static Dictionary> adm1dict = new Dictionary>();
public static Dictionary>> adm2dict = new Dictionary>>();
public static Dictionary>> latlong = new Dictionary>>(); //List of all places in same square degree
public static Dictionary>> exlatlong = new Dictionary>>(); //List of all places in same square degree
public static Dictionary featuredict = new Dictionary(); //From featurecode to feature name in makelang
public static Dictionary featureclassdict = new Dictionary(); //From featurecode to feature class
public static Dictionary geoboxdict = new Dictionary(); //From featurecode to geobox type
public static Dictionary geoboxtemplates = new Dictionary(); //From geobox type to geobox template
public static Dictionary categorydict = new Dictionary(); //from featurecode to category
public static Dictionary parentcategory = new Dictionary(); //from category to parent category
public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang
public static Dictionary catstatdict = new Dictionary(); //category statistics
public static Dictionary catnormdict = new Dictionary(); //category statistics
public static Dictionary featurepointdict = new Dictionary(); //true if feature is pointlike, false if extended
public static Dictionary islanddict = new Dictionary();
public static Dictionary rangedict = new Dictionary();
public static Dictionary lakedict = new Dictionary();
public static Dictionary wdgniddict = new Dictionary(); // from wdid to gnid
//public static Dictionary wdgnid = new Dictionary(); //from wikidata id to geonames id; negative counts duplicates
public static Dictionary catwdclass = new Dictionary(); //from category to appropriate wd top class
public static Dictionary> catwdinstance = new Dictionary>(); //from category to list of appropriate wd instance_of
public static Dictionary admcap = new Dictionary(); //names in makelang of capitals of various types of administrative units
public static Dictionary existingdict = new Dictionary(); //already existing articles
public static Dictionary ghostdict = new Dictionary(); //towns with no known population
public static Dictionary motherdict = new Dictionary(); //for overseas possessions: from territory ISO to mother country ISO
public static Dictionary specialfeaturedict = new Dictionary(); //for places that are exceptions to the usual feature labels
public static Dictionary> chinese_pop_dict = new Dictionary>();
public static Dictionary chinese_pop_dict2 = new Dictionary();
public static Dictionary iatadict = new Dictionary();
public static Dictionary icaodict = new Dictionary();

public static Dictionary> altdict = new Dictionary>();
public static List geoboxlist = new List(); //List of geobox types used
public static Dictionary langdict = new Dictionary(); //main language table
public static Dictionary langtoint = new Dictionary(); //from iso to integer code. Both iso2 and iso3 used as keys to the same int
public static List coordparams = new List(); //possible template parameters for latitude/longitude
public static Dictionary artnamedict = new Dictionary();
public static Dictionary oldartnamedict = new Dictionary();
public static Dictionary tzdict = new Dictionary(); //Time zone dictionary
public static Dictionary tznamedict = new Dictionary(); //from timezone offset to timezone acronym (standard time)
public static Dictionary tzsummerdict = new Dictionary(); //from timezone offset to timezone acronym (summer time)
public static Dictionary tzfulldict = new Dictionary(); //from timezone offset to timezone full name (standard time)
public static Dictionary tzfullsummerdict = new Dictionary(); //from timezone offset to timezone full name (summer time)
public static Dictionary propdict = new Dictionary(); //from wikidata property name to property id
public static string[] iwlang = { "en", "fr", "de", "es" };
public static Dictionary iwsites = new Dictionary();
public static long minimum_population = 100;
public static double minimum_area = 0.1;
public static int minimum_prominence = 50;
public static DateTime wdtime = new DateTime();
public static DateTime gnfiledate = new DateTime();
public static string dumpdate = "";
public static bool hasnotes = false;
public static Dictionary popvspop = new Dictionary(); //comparing population for same place, wd vs gn
public static Dictionary areavsarea = new Dictionary(); //comparing area for same place, wd vs gn
public static int nwdtot = 0;
public static Exception eglob;
public static bool locatoringeobox = (makelang == "sv"); //only works in Swedish!
public static List forktemplates = new List();
public static Dictionary featurearticle = new Dictionary();//from feature name to article name for feature
public static Dictionary alphabet_sv = new Dictionary();//from English name to Swedish name of alphabets
public static Dictionary funny_quotes = new Dictionary();
public static List donecountries = new List();
public static List donecats = new List();
public static Dictionary nasadict = new Dictionary();
public static Dictionary climatemismatchdict = new Dictionary();
public static int pausetime = 5; //time between saves, modified depending on task

public static Dictionary wdid_buffer = new Dictionary();
public static int resume_at_wdid = -1;

public static string mapfilecache = "NO CACHE";
public static int[,] mapcache = new int[3603,3603];

public static string doubleprefix = "Användare/Lsjbot/Dubletter";

public static Dictionary admdict = new Dictionary();
public static Dictionary admtodet = new Dictionary(); //from base form to determinate form of adm labels

public static Dictionary> existing_adm1 = new Dictionary>();

public static Page pconflict = null;
public static Page panomaly = null;
public static bool conflictheadline = false;
public static bool anomalyheadline = false;

public static hbookclass featurehist = new hbookclass();

public static int nedit = 0;
public static DateTime oldtime = DateTime.Now;

public static List refnamelist = new List();
public static string reflist = "";
public static string password = "";
public static string tabstring = "\t";
public static char tabchar = tabstring[0];

public static XmlDocument currentxml = new XmlDocument();
//public static XmlNode currentnode;
public static int wdid = -1;

public static Dictionary phrases = new Dictionary();
public static Site makesite;
public static Site ensite;
public static Site cmsite;
//public static Site wdsite;
public static Page pstats;

public static int badelevation = -99999;

public static CultureInfo culture = CultureInfo.CreateSpecificCulture("sv-SE");
public static CultureInfo culture_en = CultureInfo.CreateSpecificCulture("en-US");
public static NumberFormatInfo nfi = new CultureInfo("en-US", false).NumberFormat;
public static NumberFormatInfo nfi_en = new CultureInfo("en-US", false).NumberFormat;
public static NumberFormatInfo nfi_space = new CultureInfo("en-US", false).NumberFormat;

public class rdfclass
public int obj = -1;
public string objstring = "";
public int prop = -1;
public int objlink = -1;
public string value = "";

public class wdtreeclass
public List uplinks = new List();
public List downlinks = new List();

public static Dictionary wdtree = new Dictionary();

public class transliterationclass
private string defaultlang = "ru";
private string badreturn = "?";
private string contextdependent = "*";
private List vowels = new List();
public List badlist = new List();

private Dictionary> tldict = new Dictionary>();

public void Addchar(char fromchar, string tochars)
Addchar(fromchar, tochars, defaultlang, false);

public void Addchar(char fromchar, string tochars, bool isvowel)
Addchar(fromchar, tochars, defaultlang, isvowel);

public void Addchar(char fromchar, string tochars, string lang)
Addchar(fromchar, tochars, lang, false);

public void Addchar(char fromchar, string tochars, string lang, bool isvowel)
if (!tldict.ContainsKey(lang))
Dictionary csdict = new Dictionary();
tldict.Add(lang, csdict);
if (!tldict[lang].ContainsKey(fromchar))
tldict[lang].Add(fromchar, tochars);
if (isvowel)
if (!vowels.Contains(fromchar))
vowels.Add(fromchar);

private string Transchar(char fromchar, char contextbefore, char contextafter, string langparam)
if (Convert.ToInt32(fromchar) <= 0x0041) //punctuation etc.
return fromchar.ToString();

string lang = langparam;
if (!tldict.ContainsKey(lang)) //russian default language
lang = defaultlang;
if ( !tldict[lang].ContainsKey(fromchar))
lang = defaultlang;
if (!tldict[lang].ContainsKey(fromchar))
if (is_latin(fromchar.ToString()))
return fromchar.ToString();
if (!badlist.Contains(fromchar))
badlist.Add(fromchar);
return badreturn;

if (tldict[lang][fromchar] != contextdependent)
return tldict[lang][fromchar];
else //context-dependent:
List tszlist = new List {'С','с','Т','т','З','з'};
List jlist = new List { 'Ж', 'ж', 'Љ', 'љ', 'Ш', 'ш', 'Щ', 'щ', 'Й', 'й', 'Ч', 'ч'};

if (fromchar == 'Е')
if ((contextbefore == '#') || (vowels.Contains(contextbefore)))
return "Je";
else
return "E";
else if (fromchar == 'е')
if ((contextbefore == '#') || (vowels.Contains(contextbefore)))
return "je";
else
return "e";
else if (fromchar == 'Ё')
if (tszlist.Contains(contextbefore))
return "Io";
else if (jlist.Contains(contextbefore))
return "O";
else
return "Jo";
else if ((fromchar == 'ë') || (fromchar == 'ё'))
if (tszlist.Contains(contextbefore))
return "io";
else if (jlist.Contains(contextbefore))
return "o";
else
return "jo";
else if (fromchar == 'Ю')
if (tszlist.Contains(contextbefore))
return "Iu";
else if (jlist.Contains(contextbefore))
return "U";
else
return "Ju";
else if (fromchar == 'ю')
if (tszlist.Contains(contextbefore))
return "iu";
else if (jlist.Contains(contextbefore))
return "u";
else
return "ju";
else if (fromchar == 'Я')
if (tszlist.Contains(contextbefore))
return "Ia";
else if (jlist.Contains(contextbefore))
return "A";
else
return "Ja";
else if (fromchar == 'я')
if (tszlist.Contains(contextbefore))
return "ia";
else if (jlist.Contains(contextbefore))
return "a";
else
return "ja";
else
if (!badlist.Contains(fromchar))
badlist.Add(fromchar);

return badreturn;

public string Transliterate(string name,string lang)
char[] letters = name.ToCharArray();
string result = "";
for (int ic = 0; ic < letters.Length; ic++)
char contextbefore = '#';
if ( ic > 0 )
contextbefore = letters[ic-1];
char contextafter = '#';
if ( ic < letters.Length-1 )
contextafter = letters[ic+1];
result += Transchar(letters[ic],contextbefore,contextafter,lang);

return result;

public static transliterationclass cyrillic = new transliterationclass();

public static void make_translit()
int icountry = countryid[makecountry];
using (StreamWriter sw = new StreamWriter("translit-" + makecountry + ".txt"))
foreach (int gnid in altdict.Keys)
bool found = false;
string langtouse = countrydict[icountry].nativewiki;
foreach (altnameclass ac in altdict[gnid])
//if (countrydict[icountry].languages.Contains(ac.ilang))
if (langdict.ContainsKey(ac.ilang))
string langname = langdict[ac.ilang].iso2;
if (langname != langtouse)
continue;
string alphabet = get_alphabet(ac.altname);
//Console.WriteLine(ac.altname + ": " + alphabet);
if (alphabet == "cyrillic")
//Console.WriteLine("Transliteration: " + cyrillic.Transliterate(ac.altname, "ru"));
sw.WriteLine(gnid.ToString() + tabstring + ac.altname + tabstring + cyrillic.Transliterate(ac.altname, langname));
found = true;
if ((!found) && ((makecountry == "UA") || (makecountry == "BY")|| (makecountry == "KZ")|| (makecountry == "KG")|| (makecountry == "TJ")))
langtouse = "ru";
foreach (altnameclass ac in altdict[gnid])
//if (countrydict[icountry].languages.Contains(ac.ilang))
if (langdict.ContainsKey(ac.ilang))
string langname = langdict[ac.ilang].iso2;
if (langname != langtouse)
continue;
string alphabet = get_alphabet(ac.altname);
//Console.WriteLine(ac.altname + ": " + alphabet);
if (alphabet == "cyrillic")
//Console.WriteLine("Transliteration: " + cyrillic.Transliterate(ac.altname, "ru"));
sw.WriteLine(gnid.ToString() + tabstring + ac.altname + tabstring + cyrillic.Transliterate(ac.altname, langname));
found = true;
sw.Write("Badlist:");
foreach (char c in cyrillic.badlist)
sw.Write(c);
sw.WriteLine();
altdict.Clear();

public static void fill_cyrillic()
//Swedish transliteration!
cyrillic.Addchar('А', "A", true);
cyrillic.Addchar('а', "a", true);
cyrillic.Addchar('Б', "B");
cyrillic.Addchar('б', "b");
cyrillic.Addchar('В', "V");
cyrillic.Addchar('в', "v");
cyrillic.Addchar('Г', "H", "uk");
cyrillic.Addchar('г', "h", "uk");
cyrillic.Addchar('Г', "H", "be");
cyrillic.Addchar('г', "h", "be");
cyrillic.Addchar('Г', "G");
cyrillic.Addchar('г', "g");
cyrillic.Addchar('Ѓ', "Ǵ");
cyrillic.Addchar('ѓ', "ǵ");
cyrillic.Addchar('Ґ', "G");
cyrillic.Addchar('ґ', "g");
cyrillic.Addchar('Д', "D");
cyrillic.Addchar('д', "d");
cyrillic.Addchar('Ђ', "D");
cyrillic.Addchar('ђ', "đ");
cyrillic.Addchar('Ђ', "Dj","sr");
cyrillic.Addchar('ђ', "dj","sr");
cyrillic.Addchar('Е', "*", true);
cyrillic.Addchar('е', "*", true);
cyrillic.Addchar('Е', "E", "uk", true);
cyrillic.Addchar('е', "e", "uk", true);
cyrillic.Addchar('Е', "E", "bg", true);
cyrillic.Addchar('е', "e", "bg", true);
cyrillic.Addchar('Ё', "*", true);
cyrillic.Addchar('ë', "*", true);
cyrillic.Addchar('ё', "*", true);
cyrillic.Addchar('Є', "Je", true);//є
cyrillic.Addchar('є', "je", true);
cyrillic.Addchar('Ж', "Zj");
cyrillic.Addchar('ж', "zj");
cyrillic.Addchar('Ж', "Ž", "sr");
cyrillic.Addchar('ж', "Ž".ToLower(), "sr");
cyrillic.Addchar('Ж', "Ž", "mk");
cyrillic.Addchar('ж', "Ž".ToLower(), "mk");
cyrillic.Addchar('З', "Z");
cyrillic.Addchar('з', "z");
cyrillic.Addchar('Ѕ', "Dz", "mk");
cyrillic.Addchar('ѕ', "dz", "mk");
cyrillic.Addchar('И', "Y", "uk", true);
cyrillic.Addchar('и', "y", "uk", true);
cyrillic.Addchar('И', "Y", "be", true);
cyrillic.Addchar('и', "y", "be", true);
cyrillic.Addchar('И', "I", true);
cyrillic.Addchar('и', "i", true);
cyrillic.Addchar('Й', "J");
cyrillic.Addchar('й', "j");
cyrillic.Addchar('І', "I", true);
cyrillic.Addchar('і', "і", true);
cyrillic.Addchar('Ї', "Ji", true);
cyrillic.Addchar('ї', "ji", true);
cyrillic.Addchar('J', "J");
cyrillic.Addchar('j', "j");
cyrillic.Addchar('К', "K");//seemingly identical
cyrillic.Addchar('K', "K");//but different unicodes
cyrillic.Addchar('к', "k");
cyrillic.Addchar('Ќ', "Ḱ");
cyrillic.Addchar('ќ', "ḱ");
cyrillic.Addchar('Л', "L");
cyrillic.Addchar('л', "l");
cyrillic.Addchar('Љ', "Lj");
cyrillic.Addchar('љ', "lj");
cyrillic.Addchar('М', "M");
cyrillic.Addchar('м', "m");
cyrillic.Addchar('Н', "N");
cyrillic.Addchar('н', "n");
cyrillic.Addchar('Њ', "Nj");
cyrillic.Addchar('њ', "nj");
cyrillic.Addchar('О', "O", true);
cyrillic.Addchar('о', "o", true);
cyrillic.Addchar('o', "o", true);
cyrillic.Addchar('П', "P");
cyrillic.Addchar('п', "p");
cyrillic.Addchar('Р', "R");
cyrillic.Addchar('р', "r");
cyrillic.Addchar('С', "S");//seemingly identical
cyrillic.Addchar('C', "S");//but different unicodes
cyrillic.Addchar('с', "s");
cyrillic.Addchar('Т', "T");
cyrillic.Addchar('т', "t");
cyrillic.Addchar('Ћ', "Ć");
cyrillic.Addchar('ћ', "ć");
cyrillic.Addchar('У', "U", true);
cyrillic.Addchar('у', "u", true);
cyrillic.Addchar('Ў', "Ŭ", true);
cyrillic.Addchar('ў', "ŭ", true);
cyrillic.Addchar('Ф', "F");
cyrillic.Addchar('ф', "f");
cyrillic.Addchar('Х', "H", "sr");
cyrillic.Addchar('х', "h", "sr");
cyrillic.Addchar('Х', "H", "mk");
cyrillic.Addchar('х', "h", "mk");
cyrillic.Addchar('Х', "Ch");
cyrillic.Addchar('х', "ch");
cyrillic.Addchar('Ц', "Ts");
cyrillic.Addchar('ц', "ts");
cyrillic.Addchar('Ц', "C","sr");
cyrillic.Addchar('ц', "c","sr");
cyrillic.Addchar('Ц', "C","mk");
cyrillic.Addchar('ц', "c","mk");
cyrillic.Addchar('Ч', "Tj");
cyrillic.Addchar('ч', "tj");
cyrillic.Addchar('Ч', "Č","sr");
cyrillic.Addchar('ч', "Č".ToLower(),"sr");
cyrillic.Addchar('Ч', "Č","mk");
cyrillic.Addchar('ч', "Č".ToLower(),"mk");
cyrillic.Addchar('Џ', "Dž");
cyrillic.Addchar('џ', "dž");
cyrillic.Addchar('Ш', "Sj");
cyrillic.Addchar('ш', "sj");
cyrillic.Addchar('Ш', "Š","sr");
cyrillic.Addchar('ш', "Š".ToLower(),"sr");
cyrillic.Addchar('Щ', "Sjt", "bg");
cyrillic.Addchar('щ', "sjt", "bg");
cyrillic.Addchar('Щ', "Sjtj");
cyrillic.Addchar('щ', "sjtj");
cyrillic.Addchar('Ъ', "", true);
cyrillic.Addchar('ъ', "", true);
cyrillic.Addchar('Ъ', "", true);
cyrillic.Addchar('ъ', "", true);
cyrillic.Addchar('Ы', "Y", true);
cyrillic.Addchar('ы', "y", true);
cyrillic.Addchar('Ь', "", true);
cyrillic.Addchar('ь', "", true);
cyrillic.Addchar('Ѣ', "", true);
cyrillic.Addchar('ѣ', "", true);
cyrillic.Addchar('Э', "E", true);
cyrillic.Addchar('э', "e", true);
cyrillic.Addchar('Ю', "*", true);
cyrillic.Addchar('ю', "*", true);
cyrillic.Addchar('Я', "*", true);
cyrillic.Addchar('я', "*", true);
cyrillic.Addchar('Ө', "Ḟ");
cyrillic.Addchar('ө', "ḟ");
cyrillic.Addchar('Ѵ', "Ẏ");
cyrillic.Addchar('ѵ', "ẏ");
cyrillic.Addchar('Ѫ', "A", true);
cyrillic.Addchar('ѫ', "a", true);//“”
cyrillic.Addchar('“', "“");
cyrillic.Addchar('”', "”");
cyrillic.Addchar('«', "«");
cyrillic.Addchar('»', "»");
cyrillic.Addchar('’', "’");
cyrillic.Addchar('„', "„");
cyrillic.Addchar('´', "");//
cyrillic.Addchar('Ғ', "Gh","kk");
cyrillic.Addchar('ғ', "gh");
cyrillic.Addchar('Ə', "Ä","kk",true);
cyrillic.Addchar('ə', "ä","kk",true);
cyrillic.Addchar('İ', "I","kk",true);
cyrillic.Addchar('і', "i","kk",true);
cyrillic.Addchar('Қ', "Q","kk");
cyrillic.Addchar('қ', "q", "kk");
cyrillic.Addchar('қ', "q");
cyrillic.Addchar('Ң', "Ng", "kk");
cyrillic.Addchar('ң', "ng","kk");
cyrillic.Addchar('Ө', "Ö","kk",true);
cyrillic.Addchar('ө', "ö","kk",true);
cyrillic.Addchar('Ү', "Ü","kk",true);
cyrillic.Addchar('ү', "ü","kk",true);
cyrillic.Addchar('Ұ', "U","kk",true);
cyrillic.Addchar('ұ', "u","kk",true);
cyrillic.Addchar('Һ', "H","kk");
cyrillic.Addchar('һ', "h","kk");
cyrillic.Addchar('Ң', "Ng","ky");
cyrillic.Addchar('ң', "ng","ky");
cyrillic.Addchar('Ө', "Ö","ky",true);
cyrillic.Addchar('ө', "ö","ky",true);
cyrillic.Addchar('Ү', "Ü","ky",true);
cyrillic.Addchar('ү', "ü", "ky", true);
cyrillic.Addchar('γ', "ü", true);
cyrillic.Addchar('Ғ', "Gh","tg");
cyrillic.Addchar('ғ', "gh","tg");
cyrillic.Addchar('Ӣ', "Y","tg",true);
cyrillic.Addchar('ӣ', "y","tg",true);
cyrillic.Addchar('Қ', "Q","tg");
cyrillic.Addchar('қ', "q","tg");
cyrillic.Addchar('Ӯ', "Ö","tg",true);
cyrillic.Addchar('ӯ', "ö","tg",true);
cyrillic.Addchar('Ҳ', "H","tg");
cyrillic.Addchar('ҳ', "h","tg");
cyrillic.Addchar('Ҷ', "Dzj","tg");
cyrillic.Addchar('ҷ', "dzj","tg");
cyrillic.Addchar('ж', "j","mn");
cyrillic.Addchar('Ж', "J","mn");
cyrillic.Addchar('З', "Dz","mn");
cyrillic.Addchar('з', "dz","mn");
cyrillic.Addchar('Ы', "Ij","mn");
cyrillic.Addchar('ы', "ij","mn");
cyrillic.Addchar('Ө', "Ö", "mn", true);
cyrillic.Addchar('ө', "ö", "mn", true);
cyrillic.Addchar('Ү', "Ü", "mn", true);
cyrillic.Addchar('ү', "ü", "mn", true);//
cyrillic.Addchar('ј', "j");
cyrillic.Addchar('Ј', "J");
cyrillic.Addchar('ј', "j");
cyrillic.Addchar('ј', "j");
cyrillic.Addchar('ј', "j");

public static int LevenshteinDistance(string src, string dest)
//From http://www.codeproject.com/Articles/36869/Fuzzy-Search
//License CPOL (http://www.codeproject.com/info/cpol10.aspx)

int[,] d = new int[src.Length + 1, dest.Length + 1];
int i, j, cost;
char[] str1 = src.ToCharArray();
char[] str2 = dest.ToCharArray();

for (i = 0; i <= str1.Length; i++)
d[i, 0] = i;
for (j = 0; j <= str2.Length; j++)
d[0, j] = j;
for (i = 1; i <= str1.Length; i++)
for (j = 1; j <= str2.Length; j++)

if (str1[i - 1] == str2[j - 1])
cost = 0;
else
cost = 1;

d[i, j] =
Math.Min(
d[i - 1, j] + 1, // Deletion
Math.Min(
d[i, j - 1] + 1, // Insertion
d[i - 1, j - 1] + cost)); // Substitution

if ((i > 1) && (j > 1) && (str1[i - 1] ==
str2[j - 2]) && (str1[i - 2] == str2[j - 1]))
d[i, j] = Math.Min(d[i, j], d[i - 2, j - 2] + cost);

return d[str1.Length, str2.Length];

public static void test_levenshtein()
while (true)
Console.Write("string 1:");
string s1 = Console.ReadLine();
Console.Write("string 2:");
string s2 = Console.ReadLine();
int dist = LevenshteinDistance(s1, s2);
Console.WriteLine("Distance = " + dist.ToString());

public class hbookclass
private SortedDictionary shist = new SortedDictionary();
private SortedDictionary ihist = new SortedDictionary();
private SortedDictionary dhist = new SortedDictionary();

private const int MAXBINS = 202;
private double[] binlimits = new double[MAXBINS];
private double binmax = 100;
private double binmin = 0;
private double binwid = 0;
private int nbins = MAXBINS - 2;

public void Add(string key)
if (!shist.ContainsKey(key))
shist.Add(key, 1);
else
shist[key]++;

public void Add(char key)

if (!shist.ContainsKey(key.ToString()))
shist.Add(key.ToString(), 1);
else
shist[key.ToString()]++;

public void Add(int key)
if (!ihist.ContainsKey(key))
ihist.Add(key, 1);
else
ihist[key]++;

private int valuetobin(double key)
int bin = 0;
if (key > binmin)
if (key > binmax)
bin = nbins + 1;
else
bin = (int)((key - binmin) / binwid) + 1;
return bin;

private double bintomin(int bin)
if (bin == 0)
return binmin;
if (bin > nbins)
return binmax;
return binmin + (bin - 1) * binwid;

private double bintomax(int bin)
if (bin == 0)
return binmin;
if (bin > nbins)
return binmax;
return binmin + bin * binwid;

public void Add(double key)
int bin = valuetobin(key);
if (!ihist.ContainsKey(bin))
ihist.Add(bin, 1);
else
ihist[bin]++;

public void SetBins(double min, double max, int nb)
if (nbins > MAXBINS - 2)
Console.WriteLine("Too many bins. Max " + (MAXBINS - 2).ToString());
return;
else
binmax = max;
binmin = min;
nbins = nb;
binwid = (max - min) / nbins;
binlimits[0] = binmin;
for (int i = 1; i <= nbins; i++)
binlimits[i] = binmin + i * binwid;

for (int i = 0; i <= nbins + 1; i++)
if (!ihist.ContainsKey(i))
ihist.Add(i, 0);

public void PrintIHist()
int total = 0;
foreach (int key in ihist.Keys)
Console.WriteLine(key + ": " + ihist[key].ToString());
total += ihist[key];
Console.WriteLine("----Total : " + total.ToString());

public void PrintDHist()
int total = 0;
foreach (int key in ihist.Keys)
Console.WriteLine(bintomin(key).ToString() + " -- " + bintomax(key).ToString() + ": " + ihist[key].ToString());
total += ihist[key];
Console.WriteLine("----Total : " + total.ToString());

public void PrintSHist()
int total = 0;
foreach (string key in shist.Keys)
Console.WriteLine(key + ": " + shist[key].ToString());
total += shist[key];
Console.WriteLine("----Total : " + total.ToString());

//public class hbookclass
//{
// public Dictionary hist = new Dictionary();

// public void Add(string key)
// {
// if (!hist.ContainsKey(key))
// hist.Add(key, 1);
// else
// hist[key]++;

// }

// public void PrintHist()
// {
// int total = 0;
// foreach (string key in hist.Keys)
// {
// Console.WriteLine(key + ": " + hist[key].ToString());
// total += hist[key];
// }
// Console.WriteLine("----Total : "+total.ToString());
// }

// public void PrintLarge(int min)
// {
// int total = 0;
// foreach (string key in hist.Keys)
// {
// if (hist[key] > min)
// {
// Console.WriteLine(key + ": " + hist[key].ToString());
// total += hist[key];
// }

// }
// Console.WriteLine("----Total : "+total.ToString());
// }

//}

public static hbookclass fchist = new hbookclass();
public static hbookclass fcbad = new hbookclass();
public static hbookclass fcathist = new hbookclass();
public static hbookclass fclasshist = new hbookclass();
public static hbookclass evarhist = new hbookclass();
public static hbookclass slope1hist = new hbookclass();
public static hbookclass slope5hist = new hbookclass();
public static hbookclass slopermshist = new hbookclass();
public static hbookclass ndirhist = new hbookclass();
public static hbookclass nsameterrhist = new hbookclass();
public static hbookclass terrainhist = new hbookclass();
public static hbookclass terraintexthist = new hbookclass();
public static hbookclass elevdiffhist = new hbookclass();
public static hbookclass nwdhist = new hbookclass();
public static hbookclass foverrephist = new hbookclass();

public class statclass
public List sizelist = new List();
public int nart = 0;
public int nredirect = 0;
public int ncat = 0;
public int ndonecat = 0;
public int nbot = 0;
public int ntalk = 0;
public int nskip = 0;
public int milestone = 0;
public int milestone_interval = 1000;
public bool skipmilestone = false;
public int ntowait = 0;
public int nwaited = 0;

public void ClearStat()
sizelist.Clear();
nart = 0;
nredirect = 0;
ncat = 0;
nbot = 0;
ntalk = 0;
ndonecat = 0;
nskip = 0;

public int ArticleCount(Site countsite)

//string xmlSrc = countsite.PostDataAndGetResultHTM(countsite.site + "/w/api.php", "action=query&format=xml&meta=siteinfo&siprop=statistics");
string xmlSrc = countsite.PostDataAndGetResult(countsite.address + "/w/api.php", "action=query&format=xml&meta=siteinfo&siprop=statistics");

//Console.WriteLine(xmlSrc);

XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlSrc);
string ts = doc.GetElementsByTagName("statistics")[0].Attributes.GetNamedItem("articles").Value;

Console.WriteLine("ts = " + ts);

return Convert.ToInt32(ts);

public void SetMilestone(int mint, Site countsite)
milestone_interval = mint;

int ac = ArticleCount(countsite);

milestone = ((ac / milestone_interval) + 1) * milestone_interval;

Console.WriteLine("Articlecount = " + ac.ToString() + ", milestone = " + milestone.ToString());

if ((milestone - ac) > 100)
ntowait = (milestone - ac) / 2;
else
ntowait = 0;

nwaited = 0;

public void Addskip()
nskip++;

public void Adddonecat()
ndonecat++;

public void Add(string title, string text,bool savemilestone)
if (title.Contains(mp(1, null)))
ncat++;
else if (title.Contains(botname))
nbot++;
else if (text.Contains(mp(2, null)))
nredirect++;
else if (title.Contains(mp(38, null)))
ntalk++;
else if (!title.Contains(":"))
nart++;
sizelist.Add(text.Length);

nwaited++;
if (nwaited >= ntowait)
int ac = ArticleCount(makesite);

if (ac >= milestone)
Console.WriteLine("Milestone reached: ac = " + ac.ToString());
SetMilestone(milestone_interval, makesite);
if (savemilestone)
if (pstats == null)
pstats = new Page(makesite, mp(13, null) + botname + "/Statistik");
pstats.Load();
pstats.text += "\n\nMilstolpe: artikel #" + ac.ToString() + " är [[" + title + "]]\n\n";
trysave(pstats, 3);

Console.WriteLine("Articlecount = " + ac.ToString() + ", milestone = " + milestone.ToString());

if ((milestone - ac) > 10)
ntowait = (milestone - ac) / 2;
else if (!skipmilestone || ((milestone - ac) > 1))
ntowait = 0;
else
while (ac < milestone)
Console.WriteLine("Waiting for milestone...");
Thread.Sleep(60000);//milliseconds
ac = ArticleCount(makesite);
ntowait = 0;

nwaited = 0;

public string GetStat()
string s = "* Antal artiklar: " + (nart+nskip).ToString() + "\n";

//int sum = 0;

SortedDictionary hist = new SortedDictionary();

int isum = 0;
int mean = 0;
foreach (int i in sizelist)
isum += i;
if (hist.ContainsKey(i))
hist[i]++;
else
hist.Add(i, 1);

if (nart > 0)
mean = isum / nart;
else
return s;

int icum = 0;
int median = 0;
foreach (int i in hist.Keys)
icum += hist[i];
if (icum >= (nart / 2))
median = i;
break;

s += "** Medellängd: " + mean.ToString() + " bytes\n";
s += "** Medianlängd: " + median.ToString() + " bytes\n";

if ( nskip == 0 )
s += "* Antal kategorier: " + ncat.ToString() + "\n";
else
s += "* Antal kategorier: " + ndonecat.ToString() + "\n";
s += "* Antal omdirigeringar: " + nredirect.ToString() + "\n";
s += "* Antal diskussionssidor: " + ntalk.ToString() + "\n";
s += "* Antal anomalier: " + nbot.ToString() + "\n";
s += "\n";

return s;

public static statclass stats = new statclass();

public static string getgnidname(int gnid)
if (!gndict.ContainsKey(gnid))
return null;
else
return gndict[gnid].Name_ml;

public static string getartname(int gnid)
if (!gndict.ContainsKey(gnid))
return null;
else
return gndict[gnid].articlename.Replace("*","");

public static string fixcase(string ss)
string s = String.Copy(ss);
for (int i = 1; i < s.Length; i++)
if ((s[i - 1] != ' ') && (s[i - 1] != '.'))
s = s.Remove(i, 1).Insert(i, Char.ToLower(s[i]).ToString());
return s;

public static void read_phrases()
using (StreamReader sr = new StreamReader(geonamesfolder + "phraselist.txt"))

String headline = "";
headline = sr.ReadLine();

int icol = 0;
string[] langs = headline.Split(tabchar);
for (icol = 0; icol < langs.Length; icol++)
if (langs[icol] == makelang)
break;

while (!sr.EndOfStream)
String line = sr.ReadLine();
//Console.WriteLine(line);

string[] words = line.Split(tabchar);
if (words.Length < icol + 1)
continue;
//for (int jj = 1; jj < words.Length; jj++)
//{
// words[jj] = words[jj].Trim();
//}
int ip = tryconvert(words[0]);
phrases.Add(ip, words[icol]);

public static string mp(int np, string[] param)
if (phrases.Count == 0)
read_phrases();

int ip = 0;
string sret = phrases[np];
if (param != null)
foreach (string s in param)
ip++;
sret = sret.Replace("#" + ip.ToString(), s);

return sret;

public static string ReplaceOne(string textparam, string oldt, string newt, int position) //Replace ONE occurrence of oldt in textparam, the first one after position
string text = textparam;
int oldpos = text.IndexOf(oldt, position);
if (oldpos < 0)
return text;
text = text.Remove(oldpos, oldt.Length);
text = text.Insert(oldpos, newt);

return text;

public static List IndexOfAll(string text, string find) //Return a list with ALL occurrences of find in text
int start = 0;
int pos = 0;
List rl = new List();
do
pos = text.IndexOf(find, start);
if (pos >= 0)
start = pos + find.Length;
rl.Add(pos);
while (pos >= 0);

return rl;

public static void make_redirect(string frompage, string topage)
make_redirect(frompage, topage, "", -1);

public static void make_redirect(string frompage, string topage, string cat, int ilang)
if (String.IsNullOrEmpty(frompage))
return;
if (String.IsNullOrEmpty(topage))
return;

Page pred = new Page(makesite, frompage);
if (tryload(pred, 1))
if (!pred.Exists())
pred.text = "#" + mp(2, null) + " [[" + topage + "]]\n";

if (makelang == "sv")
if (langdict.ContainsKey(ilang))
pred.text += "{{Omdirigering på annat språk|" + langdict[ilang].iso3 + "}}\n";
if (!is_latin(remove_disambig(pred.title)))
string alph_sv = get_alphabet_sv(get_alphabet(remove_disambig(pred.title)));
Console.WriteLine(alph_sv);
if (!alph_sv.Contains("okänd"))
pred.text += "{{Sidnamn annan skrift|" + alph_sv + "}}\n";
else
Console.WriteLine(pred.title);
Console.WriteLine(remove_disambig(pred.title));
Console.WriteLine(alph_sv);
Console.ReadLine();

if (!String.IsNullOrEmpty(cat))
pred.AddToCategory(cat);
string ec = makesite.defaultEditComment;
makesite.defaultEditComment = mp(60, null) + " " + countryml[makecountryname] + " " + mp(2,null).ToLower();
trysave(pred, 2);
makesite.defaultEditComment = ec;

public static void make_redirect_override(Page pred, string topage, string cat, int ilang)

pred.text = "#" + mp(2, null) + " [[" + topage + "]]\n";

if (makelang == "sv")
if (langdict.ContainsKey(ilang))
pred.text += "{{Omdirigering på annat språk|" + langdict[ilang].iso3 + "}}\n";
if (!is_latin(remove_disambig(pred.title)))
string alph_sv = get_alphabet_sv(get_alphabet(remove_disambig(pred.title)));
if (!alph_sv.Contains("okänd"))
pred.text += "{{Sidnamn annan skrift|" + alph_sv + "}}\n";
else
Console.WriteLine(pred.title);
Console.WriteLine(remove_disambig(pred.title));
Console.WriteLine(alph_sv);
Console.ReadLine();

if (!String.IsNullOrEmpty(cat))
pred.AddToCategory(cat);

string ec = makesite.defaultEditComment;
makesite.defaultEditComment = mp(60, null) + " " + countryml[makecountryname] + " " + mp(2, null).ToLower();
trysave(pred, 2);
makesite.defaultEditComment = ec;

public static void romanian_redirect(string topage)
string frompage = topage;
Dictionary romchars = new Dictionary();
romchars.Add('ş', 'ș');
romchars.Add('ţ', 'ț');

foreach (char c in romchars.Keys)
frompage = frompage.Replace(c, romchars[c]);

if (frompage != topage)
make_redirect(frompage, topage, "", -1);

frompage = topage;
foreach (char c in romchars.Keys)
frompage = frompage.Replace(romchars[c], c);

if (frompage != topage)
make_redirect(frompage, topage, "", -1);

public static string initialcap(string orig)
if (String.IsNullOrEmpty(orig))
return "";

int initialpos = 0;
if (orig.IndexOf('|') > 0)
initialpos = orig.IndexOf('|') + 1;
else if (orig.IndexOf("[[") >= 0)
initialpos = orig.IndexOf("[[") + 2;
string s = orig.Substring(initialpos, 1);
s = s.ToUpper();
string final = orig;
final = final.Remove(initialpos, 1).Insert(initialpos, s);
//s += orig.Remove(0, 1);
return final;

public static bool tryload(Page p, int iattempt)
int itry = 1;

if (!reallymake)
Console.WriteLine("NOT loading " + p.title);
return true;

if (String.IsNullOrEmpty(p.title))
return false;

while (true)

try
p.Load();
return true;
catch (WebException e)
string message = e.Message;
Console.Error.WriteLine("tl we "+message);
itry++;
if (itry > iattempt)
return false;
else
Thread.Sleep(600000);//milliseconds
catch (NullReferenceException e)
string message = e.Message;
Console.Error.WriteLine("tl ne " + message);
itry++;
if (itry > iattempt)
return false;
else
Thread.Sleep(6000);//milliseconds

public static bool trysave(Page p, int iattempt)
int itry = 1;

if (!reallymake)
return true;

while (true)

try
//Bot.editComment = mp(60, null);
p.Save();
stats.Add(p.title, p.text, (makearticles || makefork));
DateTime newtime = DateTime.Now;
while (newtime < oldtime)
newtime = DateTime.Now;
oldtime = newtime.AddSeconds(pausetime);

if (pauseaftersave)
Console.WriteLine("");
Console.ReadKey();
return true;
catch (WebException e)
string message = e.Message;
Console.Error.WriteLine("ts we " + message);
itry++;
if (itry > iattempt)
return false;
else
Thread.Sleep(600000);//milliseconds
catch (WikiBotException e)
string message = e.Message;
Console.Error.WriteLine("ts wbe " + message);
if (message.Contains("Bad title"))
return false;
itry++;
if (itry > iattempt)
return false;
else
Thread.Sleep(600000);//milliseconds
catch (IOException e)
string message = e.Message;
Console.Error.WriteLine("ts ioe " + message);
itry++;
if (itry > iattempt)
return false;
else
Thread.Sleep(600000);//milliseconds

public static int tryconvert(string word)
int i = -1;

try
i = Convert.ToInt32(word);
catch (OverflowException)
Console.WriteLine("i Outside the range of the Int32 type: " + word);
catch (FormatException)
//if ( !String.IsNullOrEmpty(word))
// Console.WriteLine("i Not in a recognizable format: " + word);

return i;

public static long tryconvertlong(string word)
long i = -1;

try
i = Convert.ToInt64(word);
catch (OverflowException)
Console.WriteLine("i Outside the range of the Int64 type: " + word);
catch (FormatException)
Console.WriteLine("i Not in a recognizable long format: " + word);

return i;

public static double tryconvertdouble(string word)
double i = -1;

try
i = Convert.ToDouble(word);
catch (OverflowException)
Console.WriteLine("i Outside the range of the Double type: " + word);
catch (FormatException)
try
i = Convert.ToDouble(word.Replace(".",","));
catch (FormatException)
Console.WriteLine("i Not in a recognizable double format: " + word.Replace(".", ","));
//Console.WriteLine("i Not in a recognizable double format: " + word);

return i;

public static void fill_propdict()
propdict.Add("country",17);
propdict.Add("capital",36);
propdict.Add("commonscat",373);
propdict.Add("coat of arms",94);
propdict.Add("locatormap",242);
propdict.Add("flag",41);
propdict.Add("timezone",421);
propdict.Add("kids",150);
propdict.Add("parent",131);
propdict.Add("iso",300);
propdict.Add("borders",47);
propdict.Add("coordinates",625);
propdict.Add("inception",571);
propdict.Add("head of government",6);
propdict.Add("gnid",1566);
propdict.Add("follows",155);
propdict.Add("category dead",1465);
propdict.Add("category born",1464);
propdict.Add("category from",1792);
propdict.Add("image",18);
propdict.Add("banner",948);
//propdict.Add("sister city",190);
propdict.Add("postal code", 281);
propdict.Add("position", 625);
propdict.Add("population", 1082);
propdict.Add("instance", 31);
propdict.Add("subclass", 279);
propdict.Add("nexttowater", 206);

//propdict.Add("",);

public static void read_adm1()
int n = 0;

using (StreamReader sr = new StreamReader(geonamesfolder + "admin1CodesASCII.txt"))
while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

// public static Dictionary> adm1dict = new Dictionary>();

int geonameid = -1;

geonameid = tryconvert(words[3]);

string[] ww = words[0].Split('.');
if (adm1dict.ContainsKey(ww[0]))
adm1dict[ww[0]].Add(ww[1], geonameid);
else
Dictionary dd = new Dictionary();
dd.Add(ww[1], geonameid);
adm1dict.Add(ww[0], dd);

if (ww[0] == makecountry)
Console.WriteLine("adm1:" + words[0] + ":" + geonameid.ToString());

n++;
if ((n % 1000) == 0)
Console.WriteLine("n (adm1) = " + n.ToString());

Console.WriteLine("n (adm1)= " + n.ToString());
//Console.WriteLine("");
//Console.ReadLine();

public static void read_adm2()
int n = 0;

using (StreamReader sr = new StreamReader(geonamesfolder + "admin2Codes.txt"))
while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

// public static Dictionary> adm1dict = new Dictionary>();

countryclass country = new countryclass();

int geonameid = -1;

geonameid = tryconvert(words[3]);

string[] ww = words[0].Split('.');
if (!adm2dict.ContainsKey(ww[0]))
Dictionary> dd = new Dictionary>();
adm2dict.Add(ww[0], dd);

if ( adm2dict[ww[0]].ContainsKey(ww[1]))
adm2dict[ww[0]][ww[1]].Add(ww[2], geonameid);
else
Dictionary ddd = new Dictionary();
ddd.Add(ww[2], geonameid);
adm2dict[ww[0]].Add(ww[1], ddd);

n++;
if ((n % 10000) == 0)
Console.WriteLine("n (adm2) = " + n.ToString());

Console.WriteLine("n (adm2)= " + n.ToString());

public static void read_timezone()
int n = 0;

tzdict.Clear();
tznamedict.Clear();
tzsummerdict.Clear();
tzfulldict.Clear();
tzfullsummerdict.Clear();

string filename = "timezonenames.txt";

//first look if timezone name file exists for specific country...
filename = "timezonenames-" + makecountry + ".txt";
//Console.WriteLine(filename);

//... then for continent...
if ( !File.Exists(geonamesfolder+filename))
if ( ( !String.IsNullOrEmpty(makecountry)) && countryid.ContainsKey(makecountry))
filename = "timezonenames-"+countrydict[countryid[makecountry]].continent+".txt";

//...otherwise default names.
if ( !File.Exists(geonamesfolder+filename))
filename = "timezonenames.txt";

Console.WriteLine(filename);
//Console.ReadLine();

using (StreamReader sr = new StreamReader(geonamesfolder + filename))
//String line = sr.ReadLine();
//line = sr.ReadLine();
while (!sr.EndOfStream)
String line = sr.ReadLine();
//Console.WriteLine(line);
n++;
string[] words = line.Split(tabchar);

if (!String.IsNullOrEmpty(words[1]))
tznamedict.Add(words[0], words[1]);
if (!String.IsNullOrEmpty(words[2]))
tzsummerdict.Add(words[0], words[2]);
if (!String.IsNullOrEmpty(words[3]))
tzfulldict.Add(words[0], words[3]);
if ((words.Length > 4 ) && (!String.IsNullOrEmpty(words[4])))
tzfullsummerdict.Add(words[0], words[4]);

n = 0;
Console.WriteLine("tznamedict.Count = " + tznamedict.Count);
//Console.ReadLine();

if (savewikilinks)
Page pt = new Page(makesite, mp(13, null) + botname + "/timezonelinks");
pt.text = "Timezone links used by Lsjbot\n\n";
foreach (string tz in tznamedict.Keys)
pt.text += "* UTC" + tz + " [[" + tzfulldict[tz] + "|" + tznamedict[tz] + "]]\n";
foreach (string tz in tzsummerdict.Keys)
pt.text += "* UTC" + tz + " [[" + tzfullsummerdict[tz] + "|" + tzsummerdict[tz] + "]]\n";
trysave(pt, 1);

using (StreamReader sr = new StreamReader(geonamesfolder + "timeZones.txt"))
String line = sr.ReadLine();
line = sr.ReadLine();
while (!sr.EndOfStream)
line = sr.ReadLine();

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

// public static Dictionary> adm1dict = new Dictionary>();

tzclass tzz = new tzclass();

for (int ii = 2; ii < 5; ii++)
if (!words[ii].Contains("+") && !words[ii].Contains("-"))
words[ii] = "+" + words[ii];
words[ii] = words[ii].Replace(".0", "");
words[ii] = words[ii].Replace(".5", ":30");
words[ii] = words[ii].Replace(".75", ":45");

tzz.offset = words[2];
tzz.summeroffset = words[3];
tzz.rawoffset = words[4];

if (tznamedict.ContainsKey(tzz.offset))
tzz.tzname = tznamedict[tzz.offset];
else
Console.WriteLine("No tzname for |" + tzz.offset + "|");
if (tzfulldict.ContainsKey(tzz.offset))
tzz.tzfull = tzfulldict[tzz.offset];

if ((tzz.summeroffset != tzz.offset) && (tzsummerdict.ContainsKey(tzz.summeroffset)))
tzz.tzsummer = tzsummerdict[tzz.summeroffset];
tzz.tzfullsummer = tzfullsummerdict[tzz.summeroffset];

tzdict.Add(words[1], tzz);

n++;
if ((n % 1000) == 0)
Console.WriteLine("n (timezone) = " + n.ToString());

Console.WriteLine("n (timezone)= " + n.ToString());
//Console.ReadLine();

public static void addnamefork(int geonameid, string Name)
string nn = Name.Trim();
if (String.IsNullOrEmpty(nn))
return;

//Skip numbers and acronyms:
if (tryconvert(Name) > 0)
return;
if ((Name.Length <= 3 ) && (Name == Name.ToUpper()))
return;

if (namefork.ContainsKey(Name))
if (!namefork[Name].Contains(geonameid))
namefork[Name].Add(geonameid);
else
List ll = new List();
ll.Add(geonameid);
namefork.Add(Name, ll);

public static void read_existing_coord()
int n = 0;
using (StreamReader sr = new StreamReader(geonamesfolder + "coord-" + makelang + ".txt"))
while (!sr.EndOfStream)
String line = sr.ReadLine();
//Console.WriteLine(line);
n++;
string[] words = line.Split(tabchar);

if (words.Length < 3)
continue;

existingclass ex = new existingclass();

ex.articlename = words[0];
ex.latitude = tryconvertdouble(words[1]);
ex.longitude = tryconvertdouble(words[2]);
existingdict.Add(n, ex);
addexistinglatlong(ex.latitude, ex.longitude, n);

public static void read_existing_adm1()
//public static Dictionary> existing_adm1 = new Dictionary>();
PageList pl = new PageList(makesite);
pl.FillAllFromCategory("Kategori:Regionala politiska indelningar");

int n = 0;
foreach (Page p in pl)
string tit = remove_disambig(p.title.Replace("Kategori:", ""));

string country = "";
if (tit.Contains("s "))
country = tit.Substring(0, tit.IndexOf("s "));
else
continue;

PageList pl1 = new PageList(makesite);
pl1.FillAllFromCategory(p.title);
foreach (Page p1 in pl1)
string tit1 = p1.title.Replace("Kategori:", "");
if (!existing_adm1.ContainsKey(country))
List ll = new List();
existing_adm1.Add(country, ll);
Console.WriteLine("country = " + country);
existing_adm1[country].Add(tit1);
n++;
Console.WriteLine("n(adm1) = " + n.ToString());

public static void addexistinglatlong(double lat, double lon, int exid)
int ilat = Convert.ToInt32(Math.Truncate(lat));
int ilong = Convert.ToInt32(Math.Truncate(lon));

if (!exlatlong.ContainsKey(ilat))
Dictionary> dd = new Dictionary>();
exlatlong.Add(ilat, dd);
if (!exlatlong[ilat].ContainsKey(ilong))
List ll = new List();
exlatlong[ilat].Add(ilong, ll);
if (!exlatlong[ilat][ilong].Contains(exid))
exlatlong[ilat][ilong].Add(exid);

public static List getexisting(double lat, double lon, double radius)
List ll = new List();
double kmdeg = 40000 / 360; //km per degree at equator
double r2 = radius * radius / (kmdeg * kmdeg);
double scale = Math.Cos(lat * 3.1416 / 180); //latitude-dependent longitude scale

int ilat = Convert.ToInt32(Math.Truncate(lat));
int ilong = Convert.ToInt32(Math.Truncate(lon));

int cells = Convert.ToInt32(radius / kmdeg + 1);
for (int u = -cells; u < (cells + 1); u++)
for (int v = -cells; v < (cells + 1); v++)
if (exlatlong.ContainsKey(ilat + u))
if (exlatlong[ilat + u].ContainsKey(ilong + v))
foreach (int gnn in exlatlong[ilat + u][ilong + v])
if (!existingdict.ContainsKey(gnn))
continue;
//if ((existingdict[gnn].latitude == lat) && (existingdict[gnn].longitude == lon))
// continue;
double dlat = existingdict[gnn].latitude - lat;
double dlon = (existingdict[gnn].longitude - lon) * scale;
if ((dlat * dlat + dlon * dlon) < r2)
ll.Add(gnn);
return ll;

public static void addlatlong(double lat, double lon, int gnid)
int ilat = Convert.ToInt32(Math.Truncate(lat));
int ilong = Convert.ToInt32(Math.Truncate(lon));

if (!latlong.ContainsKey(ilat))
Dictionary> dd = new Dictionary>();
latlong.Add(ilat, dd);
if (!latlong[ilat].ContainsKey(ilong))
List ll = new List();
latlong[ilat].Add(ilong, ll);
if (!latlong[ilat][ilong].Contains(gnid))
latlong[ilat][ilong].Add(gnid);

public static bool getghostneighbors(double lat, double lon, double radius)
double kmdeg = 40000 / 360; //km per degree at equator
double r2 = radius * radius / (kmdeg * kmdeg);
double scale = Math.Cos(lat * 3.1416 / 180); //latitude-dependent longitude scale

foreach (int gnn in ghostdict.Keys)
double dlat = ghostdict[gnn].latitude - lat;
double dlon = (ghostdict[gnn].longitude - lon) * scale;
if ((dlat * dlat + dlon * dlon) < r2)
return true;

return false;

public static List getneighbors(double lat, double lon, double radius)
List ll = new List();
double kmdeg = 40000 / 360; //km per degree at equator
double r2 = radius * radius / (kmdeg * kmdeg);
double scale = Math.Cos(lat * 3.1416 / 180); //latitude-dependent longitude scale

int ilat = Convert.ToInt32(Math.Truncate(lat));
int ilong = Convert.ToInt32(Math.Truncate(lon));

int cells = Convert.ToInt32(radius / kmdeg + 1);
for (int u = -cells; u < (cells + 1); u++)
for (int v = -cells; v < (cells + 1); v++)
if (latlong.ContainsKey(ilat + u))
if (latlong[ilat + u].ContainsKey(ilong + v))
foreach (int gnn in latlong[ilat + u][ilong + v])
if (!gndict.ContainsKey(gnn))
continue;
if ((gndict[gnn].latitude == lat ) && (gndict[gnn].longitude == lon))
continue;
double dlat = gndict[gnn].latitude - lat;
double dlon = (gndict[gnn].longitude - lon) * scale;
if ((dlat * dlat + dlon * dlon) < r2)
ll.Add(gnn);
return ll;

public static List getneighbors(int gnid, double radius) //radius in km!
List ll = new List();

if (!gndict.ContainsKey(gnid))
return ll;
double lat = gndict[gnid].latitude;
double lon = gndict[gnid].longitude;
ll = getneighbors(lat, lon, radius);
return ll;

public static void read_geoboxes()
int n = 0;

foreach (string geotype in geoboxlist)
string filename = geonamesfolder + "geobox-"+geotype+"-"+makelang+".txt";

using (StreamReader sr = new StreamReader(filename))
geoboxtemplates.Add(geotype, sr.ReadToEnd());
n++;

Console.WriteLine("Read " + n.ToString() + " geoboxes.");

public static void read_categories()
int n = 0;

string filename = geonamesfolder + "categories.txt";

using (StreamReader sr = new StreamReader(filename))
String headline = "";
headline = sr.ReadLine();

int icol = 0;
string[] langs = headline.Split(tabchar);
for (icol = 0; icol < langs.Length; icol++)
if (langs[icol] == makelang)
break;

//public static Dictionary parentcategory = new Dictionary(); //from category to parent category
//public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang

while (!sr.EndOfStream)
String line = sr.ReadLine();

string[] words = line.Split(tabchar);

if (words.Length < icol+1)
continue;

parentcategory.Add(words[0], words[1]);
categoryml.Add(words[0], words[icol]);

n++;
if ((n % 100) == 0)
Console.WriteLine("n (categories) = " + n.ToString());
if (n >= 100000000)
break;

Console.WriteLine("n (categories)= " + n.ToString());

public static void read_catstat()
int n = 0;
double nctot = 0;

string filename = geonamesfolder + "catstat.txt";

using (StreamReader sr = new StreamReader(filename))

while (!sr.EndOfStream)
String line = sr.ReadLine();

string[] words = line.Split(':');

if (words.Length < 2)
continue;

int nc = tryconvert(words[1].Trim());
catstatdict.Add(words[0], nc);
nctot += nc;

n++;
if ((n % 100) == 0)
Console.WriteLine("n (categories) = " + n.ToString());
if (n >= 100000000)
break;

Console.WriteLine("n (categories)= " + n.ToString());

foreach (string s in catstatdict.Keys)
catnormdict.Add(s, catstatdict[s] / nctot);

public static void read_featurecodes()
int n = 0;
int nbad = 0;

string filename = geonamesfolder + "featureCodes.txt";
string lf = "";

using (StreamReader sr = new StreamReader(filename))
String headline = "";
headline = sr.ReadLine();

int icol = 0;
string[] langs = headline.Split(tabchar);
for (icol = 0; icol < langs.Length; icol++)
if (langs[icol] == makelang)
break;

string oldfc0 = "X";

while (!sr.EndOfStream)
String line = sr.ReadLine();

string[] words = line.Split(tabchar);

string[] fc = words[0].Split('.');

if (words[1] == "0")
nbad++;
continue;

//Console.WriteLine(fc[1]);
featuredict.Add(fc[1], words[icol]);

char fchar = fc[0].ToCharArray()[0];
featureclassdict.Add(fc[1], fchar);

string geotype = words[2];
if ( String.IsNullOrEmpty(geotype))
geotype = "alla";
geoboxdict.Add(fc[1], geotype);
if (!geoboxlist.Contains(geotype))
geoboxlist.Add(geotype);

string catname = words[3];
if (String.IsNullOrEmpty(catname))
catname = "landforms";
categorydict.Add(fc[1], catname);

featurepointdict.Add(fc[1], (words[4] == "1"));

if (savefeaturelink)
if ((!fc[1].Contains("ADM")) && (!fc[1].Contains("PPLA")))
if (fc[0] != oldfc0)
oldfc0 = fc[0];
lf += "\n\n== " + fc[0] + " ==\n\n";
lf += "* " + words[0] +": " + linkfeature(fc[1], -1);
if (words.Length > 8)
lf += " (" + words[8] + ")";
lf += "\n";

n++;
if ((n % 100) == 0)
Console.WriteLine("n (featurecodes) = " + n.ToString());
if (n >= 100000000)
break;

Console.WriteLine("n (featurecodes)= " + n.ToString());
Console.WriteLine("nbad (featurecodes)= " + nbad.ToString());

if ( savefeaturelink )
Console.WriteLine(lf);

Page plf = new Page(makesite,mp(13,null)+botname+"/linkfeatures");
plf.text = lf;
trysave(plf,1);
Console.ReadLine();

read_specialfeatures();

public static void read_specialfeatures()
int n = 0;

string filename = geonamesfolder + "specialfeatures-"+makelang+".txt";
if (!File.Exists(filename))
return;

using (StreamReader sr = new StreamReader(filename))

while (!sr.EndOfStream)
String line = sr.ReadLine();

string[] words = line.Split(tabchar);

if (words.Length < 2)
continue;

int gnid = tryconvert(words[0]);
if (gnid < 0)
continue;

if (!specialfeaturedict.ContainsKey(gnid))
specialfeaturedict.Add(gnid, words[1]);

if (words.Length >= 3)
if (!admtodet.ContainsKey(words[1]))
admtodet.Add(words[1],words[2]);

n++;
if ((n % 100) == 0)
Console.WriteLine("n (specialfeatures) = " + n.ToString());
if (n >= 100000000)
break;

Console.WriteLine("n (specialfeatures)= " + n.ToString());

public static void make_altitude_files()
elevdiffhist.SetBins(-1000.0, 1000.0, 200);

using (StreamWriter sw = new StreamWriter("altitude-" + makecountry + ".txt"))

int ngnid = gndict.Count;

foreach (int gnid in gndict.Keys)
Console.WriteLine("=====" + makecountry + "======== " + ngnid.ToString() + " remaining. ===========");
ngnid--;
if ((ngnid % 1000) == 0)
Console.WriteLine("Garbage collection:");
GC.Collect();

if ((resume_at > 0) && (resume_at != gnid))
stats.Addskip();
continue;
else
resume_at = -1;

int altitude = get_altitude(gnid);

Console.WriteLine(gndict[gnid].Name + ", " + gndict[gnid].featureclass + "." + gndict[gnid].featurecode + ": " + altitude.ToString());

if (gndict[gnid].elevation > 0)
elevdiffhist.Add(1.0 * (gndict[gnid].elevation - altitude));

if (altitude != 0)
sw.WriteLine(gnid.ToString() + tabstring + altitude.ToString());

elevdiffhist.PrintDHist();
//Console.ReadLine();

public static void read_chinese_pop()
Console.WriteLine("read_chinese_pop");
string filepath = geonamesfolder + @"\China population\";
string filekeyname = filepath + "filekey.txt";
Dictionary filekeys = new Dictionary();
int nkeys = 0;
using (StreamReader sr = new StreamReader(filekeyname))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
int fn = tryconvert(words[0]);
if (fn > 0)
int gnid = tryconvert(words[2]);
if (gnid > 0)
nkeys++;
filekeys.Add(fn, gnid);
Console.WriteLine("nkeys = " + nkeys);

//public class chinese_pop_class
//{
//public int adm1 = -1;
//public long pop = -1;
//public long malepop = -1;
//public long femalepop = -1;
//public long households = -1;
//public long pop014 = -1;
//public long pop1564 = -1;
//public long pop65 = -1;
//}

foreach (int fn in filekeys.Keys)
string filename = filepath + "China" + fn.ToString() + ".txt";
Console.WriteLine(filename);
if (!File.Exists(filename))
continue;

int npop = 0;
using (StreamReader sr = new StreamReader(filename))
bool started = false;
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
if (!started) //skip preamble in file
if (words[0] == "start")
started = true;
continue;
chinese_pop_class cc = new chinese_pop_class();
cc.adm1 = filekeys[fn];
if (words.Length <= 1)
continue;
cc.pop = tryconvertlong(words[1]);
if (cc.pop < 0)
continue;
if (words.Length > 9)
cc.malepop = tryconvertlong(words[2]);
cc.femalepop = tryconvertlong(words[3]);
cc.households = tryconvertlong(words[4]);
cc.pop014 = tryconvertlong(words[7]);
cc.pop1564 = tryconvertlong(words[8]);
cc.pop65 = tryconvertlong(words[9]);
if (!chinese_pop_dict.ContainsKey(words[0]))
List cl = new List();
chinese_pop_dict.Add(words[0], cl);
chinese_pop_dict[words[0]].Add(cc);
npop++;
Console.WriteLine("npop = " + npop);

public static void chinese_special()
read_chinese_pop();

int nfdouble = 0;
foreach (int gnid in gndict.Keys)
if (( gndict[gnid].featureclass == 'A' ) || (gndict[gnid].featureclass == 'P' ))
int nfcc = 0;
foreach (string an in gndict[gnid].altnames)
if (chinese_pop_dict.ContainsKey(an))
foreach (chinese_pop_class cc in chinese_pop_dict[an])
if (cc.adm1 == gndict[gnid].adm[1])
nfcc++;
if (nfcc == 1)
chinese_pop_dict2.Add(gnid, cc);
else if ( nfcc == 2)
Console.WriteLine("pop1 = " + cc.pop);
Console.WriteLine("pop2 = " + chinese_pop_dict2[gnid].pop);
chinese_pop_dict2.Remove(gnid);
nfdouble++;
if ( nfcc > 0 )
Console.WriteLine("nfcc = " + nfcc);

Console.WriteLine("chinese pop found: " + chinese_pop_dict2.Count);
Console.WriteLine("nfdouble = "+nfdouble);
Console.ReadLine();

public static void read_geonames(string countrycode)
int n = 0;
int nbad = 0;
Console.WriteLine("read_geonames "+countrycode);

string filename = geonamesfolder;
if ( countrycode == "" )
filename += "allCountries.txt";
else
filename += "Countries//"+countrycode+".txt";

gnfiledate = File.GetLastWriteTime(@filename);
dumpdate = gnfiledate.ToString("yyyy-MM-dd");

using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

bool badone = false;

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

int geonameid = -1;

geonameclass gn = new geonameclass();

words[1] = initialcap(words[1]);

gn.Name = words[1];
gn.Name_ml = words[1];
gn.articlename = "XXX";
geonameid = tryconvert(words[0]);
if (geonameid <= 0)
continue;
gn.asciiname = words[2];
foreach (string ll in words[3].Split(','))
gn.altnames.Add(initialcap(ll));
gn.latitude = tryconvertdouble(words[4]);
gn.longitude = tryconvertdouble(words[5]);
if ( words[6].Length > 0 )
gn.featureclass = words[6][0];
gn.featurecode = words[7];

if (!featuredict.ContainsKey(gn.featurecode))
badone = true;

for (int ii=0;ii<4;ii++)
gn.adm[ii] = -1;
if ( countryid.ContainsKey(words[8]))
gn.adm[0] = countryid[words[8]];
if ( !altnamesonly)
foreach (string ll in words[9].Split(','))
if ((ll != words[8]) && ( countryid.ContainsKey(ll)))
gn.altcountry.Add(countryid[ll]);
if (adm1dict.ContainsKey(words[8]))
if (adm1dict[words[8]].ContainsKey(words[10]))
gn.adm[1] = adm1dict[words[8]][words[10]];
else if (adm1dict[words[8]].ContainsKey("0"+words[10]))
gn.adm[1] = adm1dict[words[8]]["0"+words[10]];
if ( adm2dict.ContainsKey(words[8]))
if ( adm2dict[words[8]].ContainsKey(words[10]))
if ( adm2dict[words[8]][words[10]].ContainsKey(words[11]))
gn.adm[2] = adm2dict[words[8]][words[10]][words[11]];
gn.adm[3] = tryconvert(words[12]);
gn.adm[4] = tryconvert(words[13]);
for (int ii = 1; ii < 4; ii++)
if ( gn.adm[ii] == geonameid )
gn.adm[ii] = -1;

gn.population = tryconvertlong(words[14]);
gn.elevation = tryconvert(words[15]);
gn.dem = tryconvert(words[16]);
if ((gn.elevation <= 0) && (gn.dem > 0))
gn.elevation = gn.dem;
gn.tz = words[17];
gn.moddate = words[18];

//if ((gn.featureclass == 'P') && (gn.population <= 0) && (!checkwikidata))
// badone = true;

bool skipnoheight = false;
if (skipnoheight)
if ((gn.featureclass == 'T') && (gn.elevation <= 0) && (gn.dem <= 0))
switch (gn.featurecode)
case "HLL":
badone = true;
break;
case "HLLS":
badone = true;
break;
case "MT":
badone = true;
break;
case "MTS":
badone = true;
break;
case "MESA":
badone = true;
break;
case "MND":
badone = true;
break;
case "PK":
badone = true;
break;
case "PKS":
badone = true;
break;
case "RDGE":
badone = true;
break;
default:
break;

if (gn.featurecode == "PPLC") //Capital
if (countrydict.ContainsKey(gn.adm[0]))
countrydict[gn.adm[0]].capital_gnid = geonameid;

if (statisticsonly)
//Console.WriteLine(gn.featurecode);
fchist.Add(gn.featurecode);
if (badone)
fcbad.Add(gn.featurecode);
fclasshist.Add(gn.featureclass);
if ( !badone && categorydict.ContainsKey(gn.featurecode))
fcathist.Add(categorydict[gn.featurecode]);

// public static Dictionary> namefork = new Dictionary>(); //names with list of corresponding geonameid(s)

if (!badone)
//if (altnamesonly)
//{
// addnamefork(geonameid, gn.Name);
// addnamefork(geonameid, gn.asciiname);
// foreach (string nm in gn.altnames)
// addnamefork(geonameid, nm);
//}

if (!altnamesonly)
addlatlong(gn.latitude, gn.longitude, geonameid);

if ((geonameid > 0) && (!badone))// && (!statisticsonly))
gndict.Add(geonameid, gn);
else
nbad++;

n++;
if ((n % 10000) == 0)
Console.WriteLine("n (geonames) = " + n.ToString());
Console.WriteLine("nbad (geonames) = " + nbad.ToString());
if (n >= maxread)
break;

Console.WriteLine("n (geonames) = " + n.ToString());
Console.WriteLine("nbad (geonames) = " + nbad.ToString());

if ((!verifywikidata) && (!checkwikidata))
read_wd_files(countrycode); //files for individual countries, with pop and area

if ( checkwikidata)
read_good_wd_file(); //file for all countries, with id match only

if (makecountry == "CN")
chinese_special();

//clear away villages without population:
if ((!verifywikidata) && (!checkwikidata))
List ghosts = new List();
foreach (int gnid in gndict.Keys)
if ((gndict[gnid].featureclass == 'P') && (gndict[gnid].population <= minimum_population) && (gndict[gnid].population_wd <= minimum_population))
ghosts.Add(gnid);
else if ((gndict[gnid].featurecode == "PPLQ" ) && (gndict[gnid].population > 0 ))
ghosts.Add(gnid);
foreach (int gnid in ghosts)
existingclass gh = new existingclass();
gh.articlename = gndict[gnid].Name_ml;
gh.latitude = gndict[gnid].latitude;
gh.longitude = gndict[gnid].longitude;
ghostdict.Add(gnid, gh);

gndict.Remove(gnid);

read_islands(countrycode); //which place is on which island?

read_lakes(countrycode); //which place is in or near which lake?

read_ranges(countrycode); //which mountain is in which mountain range?

read_altitudes(countrycode);

//get proper country names
foreach (int gnid in countrydict.Keys)
if (gndict.ContainsKey(gnid))
gndict[gnid].Name_ml = countrydict[gnid].Name_ml;

Console.WriteLine("read_geonames done");

public static bool is_latin(string name)
return (get_alphabet(name) == "latin");

public static string get_alphabet(string name)
char[] letters = remove_disambig(name).ToCharArray();
int n = 0;
int sum = 0;
//int nlatin = 0;
Dictionary alphdir = new Dictionary();
foreach (char c in letters)
int uc = Convert.ToInt32(c);
sum += uc;
string alphabet = "none";
if (uc <= 0x0040) alphabet = "none";
//else if ((uc >= 0x0030) && (uc <= 0x0039)) alphabet = "number";
//else if ((uc >= 0x0020) && (uc <= 0x0040)) alphabet = "punctuation";
else if ((uc >= 0x0041) && (uc <= 0x007F)) alphabet = "latin";
else if ((uc >= 0x00A0) && (uc <= 0x00FF)) alphabet = "latin";
else if ((uc >= 0x0100) && (uc <= 0x017F)) alphabet = "latin";
else if ((uc >= 0x0180) && (uc <= 0x024F)) alphabet = "latin";
else if ((uc >= 0x0250) && (uc <= 0x02AF)) alphabet = "phonetic";
else if ((uc >= 0x02B0) && (uc <= 0x02FF)) alphabet = "spacing modifier letters";
else if ((uc >= 0x0300) && (uc <= 0x036F)) alphabet = "combining diacritical marks";
else if ((uc >= 0x0370) && (uc <= 0x03FF)) alphabet = "greek and coptic";
else if ((uc >= 0x0400) && (uc <= 0x04FF)) alphabet = "cyrillic";
else if ((uc >= 0x0500) && (uc <= 0x052F)) alphabet = "cyrillic";
else if ((uc >= 0x0530) && (uc <= 0x058F)) alphabet = "armenian";
else if ((uc >= 0x0590) && (uc <= 0x05FF)) alphabet = "hebrew";
else if ((uc >= 0x0600) && (uc <= 0x06FF)) alphabet = "arabic";
else if ((uc >= 0x0700) && (uc <= 0x074F)) alphabet = "syriac";
else if ((uc >= 0x0780) && (uc <= 0x07BF)) alphabet = "thaana";
else if ((uc >= 0x0900) && (uc <= 0x097F)) alphabet = "devanagari";
else if ((uc >= 0x0980) && (uc <= 0x09FF)) alphabet = "bengali";
else if ((uc >= 0x0A00) && (uc <= 0x0A7F)) alphabet = "gurmukhi";
else if ((uc >= 0x0A80) && (uc <= 0x0AFF)) alphabet = "gujarati";
else if ((uc >= 0x0B00) && (uc <= 0x0B7F)) alphabet = "oriya";
else if ((uc >= 0x0B80) && (uc <= 0x0BFF)) alphabet = "tamil";
else if ((uc >= 0x0C00) && (uc <= 0x0C7F)) alphabet = "telugu";
else if ((uc >= 0x0C80) && (uc <= 0x0CFF)) alphabet = "kannada";
else if ((uc >= 0x0D00) && (uc <= 0x0D7F)) alphabet = "malayalam";
else if ((uc >= 0x0D80) && (uc <= 0x0DFF)) alphabet = "sinhala";
else if ((uc >= 0x0E00) && (uc <= 0x0E7F)) alphabet = "thai";
else if ((uc >= 0x0E80) && (uc <= 0x0EFF)) alphabet = "lao";
else if ((uc >= 0x0F00) && (uc <= 0x0FFF)) alphabet = "tibetan";
else if ((uc >= 0x1000) && (uc <= 0x109F)) alphabet = "myanmar";
else if ((uc >= 0x10A0) && (uc <= 0x10FF)) alphabet = "georgian";
else if ((uc >= 0x1100) && (uc <= 0x11FF)) alphabet = "korean";
else if ((uc >= 0x1200) && (uc <= 0x137F)) alphabet = "ethiopic";
else if ((uc >= 0x13A0) && (uc <= 0x13FF)) alphabet = "cherokee";
else if ((uc >= 0x1400) && (uc <= 0x167F)) alphabet = "unified canadian aboriginal syllabics";
else if ((uc >= 0x1680) && (uc <= 0x169F)) alphabet = "ogham";
else if ((uc >= 0x16A0) && (uc <= 0x16FF)) alphabet = "runic";
else if ((uc >= 0x1700) && (uc <= 0x171F)) alphabet = "tagalog";
else if ((uc >= 0x1720) && (uc <= 0x173F)) alphabet = "hanunoo";
else if ((uc >= 0x1740) && (uc <= 0x175F)) alphabet = "buhid";
else if ((uc >= 0x1760) && (uc <= 0x177F)) alphabet = "tagbanwa";
else if ((uc >= 0x1780) && (uc <= 0x17FF)) alphabet = "khmer";
else if ((uc >= 0x1800) && (uc <= 0x18AF)) alphabet = "mongolian";
else if ((uc >= 0x1900) && (uc <= 0x194F)) alphabet = "limbu";
else if ((uc >= 0x1950) && (uc <= 0x197F)) alphabet = "tai le";
else if ((uc >= 0x19E0) && (uc <= 0x19FF)) alphabet = "khmer";
else if ((uc >= 0x1D00) && (uc <= 0x1D7F)) alphabet = "phonetic";
else if ((uc >= 0x1E00) && (uc <= 0x1EFF)) alphabet = "latin";
else if ((uc >= 0x1F00) && (uc <= 0x1FFF)) alphabet = "greek and coptic";
else if ((uc >= 0x2000) && (uc <= 0x206F)) alphabet = "none";
else if ((uc >= 0x2070) && (uc <= 0x209F)) alphabet = "none";
else if ((uc >= 0x20A0) && (uc <= 0x20CF)) alphabet = "none";
else if ((uc >= 0x20D0) && (uc <= 0x20FF)) alphabet = "combining diacritical marks for symbols";
else if ((uc >= 0x2100) && (uc <= 0x214F)) alphabet = "letterlike symbols";
else if ((uc >= 0x2150) && (uc <= 0x218F)) alphabet = "none";
else if ((uc >= 0x2190) && (uc <= 0x21FF)) alphabet = "none";
else if ((uc >= 0x2200) && (uc <= 0x22FF)) alphabet = "none";
else if ((uc >= 0x2300) && (uc <= 0x23FF)) alphabet = "none";
else if ((uc >= 0x2400) && (uc <= 0x243F)) alphabet = "none";
else if ((uc >= 0x2440) && (uc <= 0x245F)) alphabet = "optical character recognition";
else if ((uc >= 0x2460) && (uc <= 0x24FF)) alphabet = "enclosed alphanumerics";
else if ((uc >= 0x2500) && (uc <= 0x257F)) alphabet = "none";
else if ((uc >= 0x2580) && (uc <= 0x259F)) alphabet = "none";
else if ((uc >= 0x25A0) && (uc <= 0x25FF)) alphabet = "none";
else if ((uc >= 0x2600) && (uc <= 0x26FF)) alphabet = "none";
else if ((uc >= 0x2700) && (uc <= 0x27BF)) alphabet = "none";
else if ((uc >= 0x27C0) && (uc <= 0x27EF)) alphabet = "none";
else if ((uc >= 0x27F0) && (uc <= 0x27FF)) alphabet = "none";
else if ((uc >= 0x2800) && (uc <= 0x28FF)) alphabet = "braille";
else if ((uc >= 0x2900) && (uc <= 0x297F)) alphabet = "none";
else if ((uc >= 0x2980) && (uc <= 0x29FF)) alphabet = "none";
else if ((uc >= 0x2A00) && (uc <= 0x2AFF)) alphabet = "none";
else if ((uc >= 0x2B00) && (uc <= 0x2BFF)) alphabet = "none";
else if ((uc >= 0x2E80) && (uc <= 0x2EFF)) alphabet = "chinese/japanese";
else if ((uc >= 0x2F00) && (uc <= 0x2FDF)) alphabet = "chinese/japanese";
else if ((uc >= 0x2FF0) && (uc <= 0x2FFF)) alphabet = "none";
else if ((uc >= 0x3000) && (uc <= 0x303F)) alphabet = "chinese/japanese";
else if ((uc >= 0x3040) && (uc <= 0x309F)) alphabet = "chinese/japanese";
else if ((uc >= 0x30A0) && (uc <= 0x30FF)) alphabet = "chinese/japanese";
else if ((uc >= 0x3100) && (uc <= 0x312F)) alphabet = "bopomofo";
else if ((uc >= 0x3130) && (uc <= 0x318F)) alphabet = "korean";
else if ((uc >= 0x3190) && (uc <= 0x319F)) alphabet = "chinese/japanese";
else if ((uc >= 0x31A0) && (uc <= 0x31BF)) alphabet = "bopomofo";
else if ((uc >= 0x31F0) && (uc <= 0x31FF)) alphabet = "chinese/japanese";
else if ((uc >= 0x3200) && (uc <= 0x32FF)) alphabet = "chinese/japanese";
else if ((uc >= 0x3300) && (uc <= 0x33FF)) alphabet = "chinese/japanese";
else if ((uc >= 0x3400) && (uc <= 0x4DBF)) alphabet = "chinese/japanese";
else if ((uc >= 0x4DC0) && (uc <= 0x4DFF)) alphabet = "none";
else if ((uc >= 0x4E00) && (uc <= 0x9FFF)) alphabet = "chinese/japanese";
else if ((uc >= 0xA000) && (uc <= 0xA48F)) alphabet = "chinese/japanese";
else if ((uc >= 0xA490) && (uc <= 0xA4CF)) alphabet = "chinese/japanese";
else if ((uc >= 0xAC00) && (uc <= 0xD7AF)) alphabet = "korean";
else if ((uc >= 0xD800) && (uc <= 0xDB7F)) alphabet = "high surrogates";
else if ((uc >= 0xDB80) && (uc <= 0xDBFF)) alphabet = "high private use surrogates";
else if ((uc >= 0xDC00) && (uc <= 0xDFFF)) alphabet = "low surrogates";
else if ((uc >= 0xE000) && (uc <= 0xF8FF)) alphabet = "private use area";
else if ((uc >= 0xF900) && (uc <= 0xFAFF)) alphabet = "chinese/japanese";
else if ((uc >= 0xFB00) && (uc <= 0xFB4F)) alphabet = "alphabetic presentation forms";
else if ((uc >= 0xFB50) && (uc <= 0xFDFF)) alphabet = "arabic";
else if ((uc >= 0xFE00) && (uc <= 0xFE0F)) alphabet = "variation selectors";
else if ((uc >= 0xFE20) && (uc <= 0xFE2F)) alphabet = "combining half marks";
else if ((uc >= 0xFE30) && (uc <= 0xFE4F)) alphabet = "chinese/japanese";
else if ((uc >= 0xFE50) && (uc <= 0xFE6F)) alphabet = "small form variants";
else if ((uc >= 0xFE70) && (uc <= 0xFEFF)) alphabet = "arabic";
else if ((uc >= 0xFF00) && (uc <= 0xFFEF)) alphabet = "halfwidth and fullwidth forms";
else if ((uc >= 0xFFF0) && (uc <= 0xFFFF)) alphabet = "specials";
else if ((uc >= 0x10000) && (uc <= 0x1007F)) alphabet = "linear b";
else if ((uc >= 0x10080) && (uc <= 0x100FF)) alphabet = "linear b";
else if ((uc >= 0x10100) && (uc <= 0x1013F)) alphabet = "aegean numbers";
else if ((uc >= 0x10300) && (uc <= 0x1032F)) alphabet = "old italic";
else if ((uc >= 0x10330) && (uc <= 0x1034F)) alphabet = "gothic";
else if ((uc >= 0x10380) && (uc <= 0x1039F)) alphabet = "ugaritic";
else if ((uc >= 0x10400) && (uc <= 0x1044F)) alphabet = "deseret";
else if ((uc >= 0x10450) && (uc <= 0x1047F)) alphabet = "shavian";
else if ((uc >= 0x10480) && (uc <= 0x104AF)) alphabet = "osmanya";
else if ((uc >= 0x10800) && (uc <= 0x1083F)) alphabet = "cypriot syllabary";
else if ((uc >= 0x1D000) && (uc <= 0x1D0FF)) alphabet = "byzantine musical symbols";
else if ((uc >= 0x1D100) && (uc <= 0x1D1FF)) alphabet = "musical symbols";
else if ((uc >= 0x1D300) && (uc <= 0x1D35F)) alphabet = "tai xuan jing symbols";
else if ((uc >= 0x1D400) && (uc <= 0x1D7FF)) alphabet = "none";
else if ((uc >= 0x20000) && (uc <= 0x2A6DF)) alphabet = "chinese/japanese";
else if ((uc >= 0x2F800) && (uc <= 0x2FA1F)) alphabet = "chinese/japanese";
else if ((uc >= 0xE0000) && (uc <= 0xE007F)) alphabet = "none";

bool ucprint = false;
if (alphabet != "none")
n++;
if (!alphdir.ContainsKey(alphabet))
alphdir.Add(alphabet, 0);
alphdir[alphabet]++;
else if (uc != 0x0020)
//Console.Write("c=" + c.ToString() + ", uc=0x" + uc.ToString("x5") + "|");
//ucprint = true;
if ( ucprint )
Console.WriteLine();

int nmax = 0;
string alphmax = "none";
foreach (string alph in alphdir.Keys)
//Console.WriteLine("ga:" + alph + " " + alphdir[alph].ToString());
if (alphdir[alph] > nmax)
nmax = alphdir[alph];
alphmax = alph;

if ( letters.Length > 2*n) //mostly non-alphabetic
return "none";
else if ( nmax > n/2 ) //mostly same alphabet
return alphmax;
else
return "mixed"; //mixed alphabets

public static string get_alphabet_sv(string alph_en)
Console.WriteLine("gas:" + alph_en);
if (alphabet_sv.Count == 0)
alphabet_sv.Add("bopomofo", "zhuyin");
alphabet_sv.Add("halfwidth and fullwidth forms", "");
alphabet_sv.Add("syriac", "syriska alfabetet");
alphabet_sv.Add("thaana", "tāna");
alphabet_sv.Add("lao", "laotisk skrift");
alphabet_sv.Add("khmer", "khmerisk skrift");
alphabet_sv.Add("gurmukhi", "gurmukhi");
alphabet_sv.Add("myanmar", "burmesisk skrift");
alphabet_sv.Add("tibetan", "tibetansk skrift");
alphabet_sv.Add("sinhala", "singalesisk skrift");
alphabet_sv.Add("ethiopic", "etiopisk skrift");
alphabet_sv.Add("oriya", "oriya-skrift");
alphabet_sv.Add("kannada", "kannada");
alphabet_sv.Add("malayalam", "malayalam");
alphabet_sv.Add("telugu", "teluguskrift");
alphabet_sv.Add("tamil", "tamilska alfabetet");
alphabet_sv.Add("gujarati", "gujarati");
alphabet_sv.Add("bengali", "bengalisk skrift");
alphabet_sv.Add("armenian", "armeniska alfabetet");
alphabet_sv.Add("georgian", "georgiska alfabetet");
alphabet_sv.Add("devanagari", "devanāgarī");
alphabet_sv.Add("korean", "hangul");
alphabet_sv.Add("hebrew", "hebreiska alfabetet");
alphabet_sv.Add("greek and coptic", "grekiska alfabetet");
alphabet_sv.Add("chinese/japanese", "kinesiska tecken");
alphabet_sv.Add("thai", "thailändska alfabetet");
alphabet_sv.Add("cyrillic", "kyrilliska alfabetet");
alphabet_sv.Add("arabic", "arabiska alfabetet");
alphabet_sv.Add("latin", "latinska alfabetet");

if (alphabet_sv.ContainsKey(alph_en))
return alphabet_sv[alph_en];
else
return "okänd skrift";

public static void add_nameforks()
int ntot = gndict.Count;

Console.WriteLine("Add_nameforks: " + ntot.ToString() + " to do.");

foreach (int gnid in gndict.Keys)
addnamefork(gnid, gndict[gnid].Name);
addnamefork(gnid, gndict[gnid].Name_ml);
addnamefork(gnid, gndict[gnid].asciiname);

if (altdict.ContainsKey(gnid))
foreach (altnameclass ac in altdict[gnid])
addnamefork(gnid, ac.altname);
//else
//{
// foreach (string nm in gndict[gnid].altnames)
// addnamefork(gnid, nm);
//}

//if (gndict[gnid].wdid > 0)
//{
// XmlDocument cx = get_wd_xml(wdid);
// if (cx != null)
// {
// Dictionary rd = get_wd_sitelinks(cx);
// foreach (string sw in rd.Keys)
// addnamefork(gnid, rd[sw]);
// }
//}

ntot--;
if ( (ntot % 1000) == 0 )
Console.WriteLine("=== " + ntot.ToString() + " left ===");

public static void read_island_file(string wdcountry)
string filename = geonamesfolder+ @"islands\islands-" + wdcountry + ".txt";
islanddict.Clear();
try
int nislands = 0;
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.Write(gnid.ToString() + tabstring + area.ToString() + tabstring + kmew.ToString() + tabstring + kmns.ToString());
//foreach (int oi in onisland)
// sw.Write(tabstring + oi.ToString());
//sw.WriteLine();

if (words.Length < 4)
continue;

int gnid = tryconvert(words[0]);
if (!gndict.ContainsKey(gnid))
continue;

double area = tryconvertdouble(words[1]);
if ((area > 0) && (gndict[gnid].area <= 0 ))
gndict[gnid].area = area;

islandclass isl = new islandclass();
isl.area = area;

double scale = Math.Cos(gndict[gnid].latitude * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
double pixkmy = 40000.0 / (360.0 * 1200.0);

isl.kmew = tryconvertdouble(words[2])+pixkmx;
isl.kmns = tryconvertdouble(words[3])+pixkmy;
for (int i = 4; i < words.Length; i++)
int oi = tryconvert(words[i]);
if (gndict.ContainsKey(oi))
isl.onisland.Add(oi);
//if (gndict[oi].island <= 0)
//{
// gndict[oi].island = gnid;
//}
//else //on two islands - error
//{
// otherisland = gndict[oi].island;
// gndict[oi].island = 0;
//}
//if (islanddict.ContainsKey(otherisland))
// islanddict.Remove(otherisland);
//else
islanddict.Add(gnid,isl);
nislands++;

Console.WriteLine("# islands = " + nislands.ToString());

Dictionary oindex = new Dictionary();
//List badlist = new List();
Dictionary> badlist = new Dictionary>();

//identify stuff that's listed as on two islands:
foreach (int gnid in islanddict.Keys)
//first add island itself as "on" itself...
if (oindex.ContainsKey(gnid))
if (!badlist.ContainsKey(oindex[gnid]))
List bl = new List();
badlist.Add(oindex[gnid], bl);
if (!badlist[oindex[gnid]].Contains(gnid))
badlist[oindex[gnid]].Add(gnid);
else
oindex.Add(gnid, gnid);
//... then add everything else on the island.
foreach (int oi in islanddict[gnid].onisland)
if (oindex.ContainsKey(oi))
if (!badlist.ContainsKey(oindex[oi]))
List bl = new List();
badlist.Add(oindex[oi], bl);
if (!badlist[oindex[oi]].Contains(gnid))
badlist[oindex[oi]].Add(gnid);
else
oindex.Add(oi, gnid);

if (verifyislands) //Go through and find best island for stuff with double location,
{ //then make new island file.
int nbad = 0;

foreach (int badi in badlist.Keys)
long bestdist = seed_center_dist(badi);
long best2dist = 99999999;
int best = badi;
foreach (int badg in badlist[badi])
long scdist = seed_center_dist(badg);
if (scdist < bestdist)
best2dist = bestdist;
bestdist = scdist;
best = badg;

if (bestdist * 3 > best2dist) //require 3 times better to "promote" one of the islands
best = -1;

if (badi != best)
islanddict.Remove(badi);
nbad++;
foreach (int badg in badlist[badi])
if (badg != best)
islanddict.Remove(badg);
nbad++;

Console.WriteLine("# islands = " + nislands.ToString());
Console.WriteLine("# bad islands = " + nbad.ToString());
using (StreamWriter sw = new StreamWriter("islands-" + makecountry + ".txt"))

foreach (int gnid in islanddict.Keys)
sw.Write(gnid.ToString() + tabstring + islanddict[gnid].area.ToString() + tabstring + islanddict[gnid].kmew.ToString() + tabstring + islanddict[gnid].kmns.ToString());
foreach (int oi in islanddict[gnid].onisland)
sw.Write(tabstring + oi.ToString());
sw.WriteLine();
else //just remove the duplicate islands
foreach (int badi in badlist.Keys)
islanddict.Remove(badi);
foreach (int badg in badlist[badi])
islanddict.Remove(badg);

foreach (int gnid in islanddict.Keys)
foreach (int oi in islanddict[gnid].onisland)
gndict[oi].island = gnid;

catch (IOException e)
string message = e.Message;
Console.Error.WriteLine(message);

public static void read_range_file(string wdcountry)
string filename = geonamesfolder + @"ranges\ranges-" + wdcountry + ".txt";
rangedict.Clear();
try
int nranges = 0;
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.Write(gnid.ToString() + tabstring + area.ToString() + tabstring + kmew.ToString() + tabstring + kmns.ToString());
//foreach (int oi in onrange)
// sw.Write(tabstring + oi.ToString());
//sw.WriteLine();

if (words.Length < 6)
continue;

int gnid = tryconvert(words[0]);
if (!gndict.ContainsKey(gnid))
continue;

double length = tryconvertdouble(words[1]);
rangeclass isl = new rangeclass();
isl.length = length;

double scale = Math.Cos(gndict[gnid].latitude * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
//double pixkmy = 40000.0 / (360.0 * 1200.0);

//public class rangeclass //data for each MTS/HLLS
//{
// public double length = 0;
// public string orientation = "....";
// public double angle = 0; //polar angle of long axis (radians). 0 or pi = EW, pi/2 or 3pi/2 = NS etc.
// public double kmew = 0;
// public double kmns = 0;
// public int maxheight = 0; //highest point; gnid of peak if negative, height if positive
// public double hlat = 999; //latitude/longitude of highest point
// public double hlon = 999;
// public List inrange = new List(); //list of GeoNames id of mountains in the range.
//}

isl.kmew = tryconvertdouble(words[2]);
isl.kmns = tryconvertdouble(words[3]);
isl.angle = tryconvertdouble(words[4]);
isl.maxheight = tryconvert(words[5]);
isl.hlat = tryconvertdouble(words[6]);
isl.hlon = tryconvertdouble(words[7]);

for (int i = 8; i < words.Length; i++)
int oi = tryconvert(words[i]);
if (gndict.ContainsKey(oi))
isl.inrange.Add(oi);
//if (rangedict.ContainsKey(otherrange))
// rangedict.Remove(otherrange);
//else
rangedict.Add(gnid, isl);
nranges++;

Console.WriteLine("# ranges = " + nranges.ToString());

foreach (int gnid in rangedict.Keys)
foreach (int oi in rangedict[gnid].inrange)
gndict[oi].inrange = gnid;

catch (IOException e)
string message = e.Message;
Console.Error.WriteLine(message);

public static void read_islands(string countrycode)
if (countrycode == "")
foreach (int gnid in countrydict.Keys)
read_island_file(countrydict[gnid].iso);
else
read_island_file(countrycode);

public static void read_ranges(string countrycode)
if (countrycode == "")
foreach (int gnid in countrydict.Keys)
read_range_file(countrydict[gnid].iso);
else
read_range_file(countrycode);

public static void read_lake_file(string wdcountry)
string filename = geonamesfolder + @"lakes\lakes-" + wdcountry + ".txt";
lakedict.Clear();
try
int nlakes = 0;
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.Write(gnid.ToString() + tabstring + area.ToString() + tabstring + kmew.ToString() + tabstring + kmns.ToString());
//foreach (int oi in onisland)
// sw.Write(tabstring + oi.ToString());
//sw.WriteLine();

if (words.Length < 6)
continue;

int gnid = tryconvert(words[0]);
if (!gndict.ContainsKey(gnid))
continue;

double area = tryconvertdouble(words[1]);
if ((area > 0) && (gndict[gnid].area <= 0))
gndict[gnid].area = area;

lakeclass lake = new lakeclass();
lake.area = area;

double scale = Math.Cos(gndict[gnid].latitude * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
double pixkmy = 40000.0 / (360.0 * 1200.0);

lake.kmew = tryconvertdouble(words[2]);
lake.kmns = tryconvertdouble(words[3]);
if (verifylakes)
lake.kmew += pixkmx;
lake.kmns += pixkmy;

int iw = 5;
while ((iw < words.Length) && (words[iw] != "around"))
int ii = tryconvert(words[iw]);
if (gndict.ContainsKey(ii))
lake.inlake.Add(ii);
iw++;
while (iw < words.Length)
int ii = tryconvert(words[iw]);
if (gndict.ContainsKey(ii))
lake.atlake.Add(ii);
iw++;

lakedict.Add(gnid, lake);
nlakes++;

Console.WriteLine("# lakes = " + nlakes.ToString());

Dictionary oindex = new Dictionary();
//List badlist = new List();
Dictionary> badlist = new Dictionary>();

//identify stuff that's listed as on two lakes:
foreach (int gnid in lakedict.Keys)
//first add lake itself as "in" itself...
if (oindex.ContainsKey(gnid))
if (!badlist.ContainsKey(oindex[gnid]))
List bl = new List();
badlist.Add(oindex[gnid], bl);
if (!badlist[oindex[gnid]].Contains(gnid))
badlist[oindex[gnid]].Add(gnid);
else
oindex.Add(gnid, gnid);
//... then add everything else in the lake.
foreach (int oi in lakedict[gnid].inlake)
if (oindex.ContainsKey(oi))
if (!badlist.ContainsKey(oindex[oi]))
List bl = new List();
badlist.Add(oindex[oi], bl);
if (!badlist[oindex[oi]].Contains(gnid))
badlist[oindex[oi]].Add(gnid);
else
oindex.Add(oi, gnid);

if (verifylakes) //Go through and find best lake for stuff with double location,
{ //then make new lake file.
int nbad = 0;

foreach (int badi in badlist.Keys)
long bestdist = seed_center_dist(badi);
long best2dist = 99999999;
int best = badi;
foreach (int badg in badlist[badi])
long scdist = seed_center_dist(badg);
if (scdist < bestdist)
best2dist = bestdist;
bestdist = scdist;
best = badg;

if (bestdist * 3 > best2dist) //require 3 times better to "promote" one of the lakes
best = -1;

if (badi != best)
lakedict.Remove(badi);
nbad++;
foreach (int badg in badlist[badi])
if (badg != best)
lakedict.Remove(badg);
nbad++;

Console.WriteLine("# lakes = " + nlakes.ToString());
Console.WriteLine("# bad lakes = " + nbad.ToString());
using (StreamWriter sw = new StreamWriter("lakes-" + makecountry + ".txt"))

foreach (int gnid in lakedict.Keys)
Console.WriteLine(gndict[gnid].Name + "; " + lakedict[gnid].area.ToString() + "; " + lakedict[gnid].kmew.ToString() + "; " + lakedict[gnid].kmns.ToString() + "; " + lakedict[gnid].inlake.Count.ToString() + "; " + lakedict[gnid].atlake.Count.ToString());
sw.Write(gnid.ToString() + tabstring + lakedict[gnid].area.ToString() + tabstring + lakedict[gnid].kmew.ToString() + tabstring + lakedict[gnid].kmns.ToString() + tabstring + "in");
foreach (int il in lakedict[gnid].inlake)
sw.Write(tabstring + il.ToString());
Console.WriteLine(gndict[il].Name + " in lake");
sw.Write(tabstring + "around");
foreach (int al in lakedict[gnid].atlake)
sw.Write(tabstring + al.ToString());
Console.WriteLine(gndict[al].Name + " around lake");
sw.WriteLine();
else //just remove the duplicate lakes
foreach (int badi in badlist.Keys)
lakedict.Remove(badi);
foreach (int badg in badlist[badi])
lakedict.Remove(badg);

foreach (int gnid in lakedict.Keys)
foreach (int ii in lakedict[gnid].inlake)
gndict[ii].inlake = gnid;
foreach (int ai in lakedict[gnid].atlake)
gndict[ai].atlakes.Add(gnid);

catch (IOException e)
string message = e.Message;
Console.Error.WriteLine(message);

public static void read_lakes(string countrycode)
if (countrycode == "")
foreach (int gnid in countrydict.Keys)
read_lake_file(countrydict[gnid].iso);
else
read_lake_file(countrycode);

public static void read_altitude_file(string wdcountry)
string filename = geonamesfolder + @"altitudes\altitude-" + wdcountry + ".txt";
try
int nalt = 0;
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);

if (words.Length < 2)
continue;

int gnid = tryconvert(words[0]);
if (!gndict.ContainsKey(gnid))
continue;

int altitude = tryconvert(words[1]);

if (altitude < 9000)
gndict[gnid].elevation_vp = altitude;
if (gndict[gnid].elevation <= 0)
gndict[gnid].elevation = altitude;
nalt++;

Console.WriteLine("# altitudes = " + nalt.ToString());

catch (IOException e)
string message = e.Message;
Console.Error.WriteLine(message);

public static void read_altitudes(string countrycode)
if (countrycode == "")
foreach (int gnid in countrydict.Keys)
read_altitude_file(countrydict[gnid].iso);
else
read_altitude_file(countrycode);

public static void read_altnames()
int n = 0;
int nbad = 0;
Console.WriteLine("read_altnames");
string filename = geonamesfolder + "alternateNames.txt";

using (StreamReader sr = new StreamReader(filename))

//public class altnameclass
//{
// public int altid = 0;
// public string altname = "";
// public int ilang = 0;
// public string wikilink = "";
// public bool official = false;
// public bool shortform = false;
// public bool colloquial = false;
// public bool historic = false;
//}

while (!sr.EndOfStream)
String line = sr.ReadLine();

bool goodname = false;

string[] words = line.Split(tabchar);

altnameclass an = new altnameclass();

an.altid = tryconvert(words[0]);

int gnid = tryconvert(words[1]);

if (!checkdoubles && !gndict.ContainsKey(gnid))
continue;

int country = -1;
if (gndict.ContainsKey(gnid))
country = gndict[gnid].adm[0];

//if (gnid == 3039154)
// Console.WriteLine(line);

if (words[2] == "link")
an.wikilink = words[3];
goodname = true;
else if (words[2] == "iata")
if (!iatadict.ContainsKey(gnid))
iatadict.Add(gnid, words[3]);
else if (words[2] == "icao")
if (!icaodict.ContainsKey(gnid))
icaodict.Add(gnid, words[3]);
else
an.altname = initialcap(words[3]);

if (langtoint.ContainsKey(words[2]))
an.ilang = langtoint[words[2]];
else if (langtoint.ContainsKey(words[2].Split('-')[0]))
an.ilang = langtoint[words[2].Split('-')[0]];
else if (words[2].Length > 3)
an.ilang = -1;
continue;
//Console.WriteLine("lang:" + an.ilang.ToString() + ", " + words[2]);

if (words[2] == makelang)
if (gndict.ContainsKey(gnid))
if ( !gndict[gnid].articlename.Contains("*"))
gndict[gnid].Name_ml = words[3];

if (countrydict.ContainsKey(country))
if (countrydict[country].languages.Contains(an.ilang))
goodname = true;

string script = get_alphabet(an.altname);

if ((an.ilang < 0) && (script == "latin"))
goodname = true;

if (((makecountry == "CN") || (makecountry == "JP") || (makecountry == "TW")) && (script == "chinese/japanese"))
goodname = true;

if ((words.Length > 4) && (words[4] == "1"))
an.official = true;
if ((words.Length > 5) && (words[5] == "1"))
an.shortform = true;
if ((words.Length > 6) && (words[6] == "1"))
an.colloquial = true;
if ((words.Length > 7) && (words[7] == "1"))
an.historic = true;

//if (an.official || an.shortform || an.colloquial || an.historic)
// goodname = true;

//if (langdict.ContainsKey(an.ilang))
// Console.Write(langdict[an.ilang].iso2 + ":");
//else
// Console.Write("--:");
//Console.WriteLine(an.altname + ": is_latin = " + is_latin(an.altname).ToString()+", goodname = "+goodname.ToString());

if (goodname)
if (!altdict.ContainsKey(gnid))
List anl = new List();
altdict.Add(gnid, anl);
altdict[gnid].Add(an);
if ( !String.IsNullOrEmpty(an.altname))
n++;
else
nbad++;

if ((n % 1000000) == 0)
Console.WriteLine("n (altnames) = " + n.ToString());
if (n >= 1000000000)
break;

Console.WriteLine("n (altnames)= " + n.ToString());
Console.WriteLine("nbad (altnames)= " + nbad.ToString());

if (statisticsonly)
hbookclass althist = new hbookclass();
hbookclass altuniquehist = new hbookclass();

foreach (int gnid in altdict.Keys)
althist.Add(altdict[gnid].Count);
List al = new List();
foreach (altnameclass ac in altdict[gnid])
if (!al.Contains(ac.altname))
al.Add(ac.altname);
altuniquehist.Add(al.Count);
if (al.Count > 100)
Console.Write(al.Count.ToString() + ": ");
foreach (string s in al)
Console.Write(s + " | ");
Console.WriteLine();

althist.PrintIHist();
altuniquehist.PrintIHist();

Console.ReadLine();

if (makelang == "sv")
if (makecountry == "")
read_translit("BG");
read_translit("BY");
read_translit("KZ");
read_translit("MK");
read_translit("RS");
read_translit("RU");
read_translit("UA");
read_translit("MN");
read_translit("KG");
read_translit("TJ");
read_translit("RS");

else
read_translit(makecountry);

read_handtranslated();

public static void read_handtranslated()

string filename = geonamesfolder + @"handtranslated-" + makelang + ".txt";
int n = 0;
if (File.Exists(filename))
Console.WriteLine("read_handtranslated " + filename);
using (StreamReader sr = new StreamReader(filename))

//public class altnameclass
//{
// public int altid = 0;
// public string altname = "";
// public int ilang = 0;
// public string wikilink = "";
// public bool official = false;
// public bool shortform = false;
// public bool colloquial = false;
// public bool historic = false;
//}

while (!sr.EndOfStream)
String line = sr.ReadLine();

//bool goodname = false;

string[] words = line.Split(tabchar);

if (words.Length < 2)
continue;

altnameclass an = new altnameclass();

an.altid = -1;

int gnid = tryconvert(words[0]);

if (!checkdoubles && !gndict.ContainsKey(gnid))
continue;

an.altname = remove_disambig(words[1].Replace("*", ""));

an.ilang = langtoint[makelang];

if (gndict.ContainsKey(gnid))
gndict[gnid].Name_ml = an.altname;
if (words[1].Contains("*"))
gndict[gnid].articlename = words[1];

//else
if (!artnamedict.ContainsKey(gnid))
artnamedict.Add(gnid, words[1]);
else if (!artnamedict[gnid].Contains("*"))
artnamedict[gnid] = words[1];

if (!altdict.ContainsKey(gnid))
List anl = new List();
altdict.Add(gnid, anl);
altdict[gnid].Add(an);
if (!String.IsNullOrEmpty(an.altname))
n++;

Console.WriteLine("Names found in handtranslated: " + n.ToString());
else
Console.WriteLine("File not found! " + filename);

public static void read_translit(string tlcountry)

string filename = geonamesfolder + @"translit\translit-" + tlcountry + ".txt";
int n = 0;
if (File.Exists(filename))
Console.WriteLine("read_translit " + filename);
using (StreamReader sr = new StreamReader(filename))

//public class altnameclass
//{
// public int altid = 0;
// public string altname = "";
// public int ilang = 0;
// public string wikilink = "";
// public bool official = false;
// public bool shortform = false;
// public bool colloquial = false;
// public bool historic = false;
//}

while (!sr.EndOfStream)
String line = sr.ReadLine();

//bool goodname = false;

string[] words = line.Split(tabchar);

if (words.Length < 3)
continue;

altnameclass an = new altnameclass();

an.altid = -1;

int gnid = tryconvert(words[0]);

if (!checkdoubles && !gndict.ContainsKey(gnid))
continue;

an.altname = words[2];

an.ilang = langtoint["sv"];

if (makelang == "sv")
if (gndict.ContainsKey(gnid))
if (gndict[gnid].Name_ml == gndict[gnid].Name)
gndict[gnid].Name_ml = an.altname;

if (!altdict.ContainsKey(gnid))
List anl = new List();
altdict.Add(gnid, anl);
altdict[gnid].Add(an);
if (!String.IsNullOrEmpty(an.altname))
n++;

else
Console.WriteLine("File not found! " + filename);
Console.WriteLine("Names found in translit: " + n.ToString());

public static string getdatestring()
DateTime thismonth = DateTime.Now;
string monthstring = thismonth.Month.ToString();
while (monthstring.Length < 2)
monthstring = "0" + monthstring;
string daystring = thismonth.Day.ToString();
while (daystring.Length < 2)
daystring = "0" + daystring;
return thismonth.Year.ToString() + monthstring + daystring;

public static void list_nameforks()
int nfork = 0;
int nfork2 = 0;
int nfork10 = 0;
int nfork100 = 0;
int nfork500 = 0;
int nfork1000 = 0;
int maxfork = 0;
string maxforkname = "xxx";
//string datestring = getdatestring();

Console.WriteLine("list_nameforks");

using (StreamWriter sw = new StreamWriter("namefork-" + getdatestring() + ".csv"))

foreach (string s in namefork.Keys)
if (namefork[s].Count > 0)
nfork++;
if (namefork[s].Count > maxfork)
maxfork = namefork[s].Count;
maxforkname = s;
if (namefork[s].Count >= 2)
nfork2++;
if (namefork[s].Count >= 10)
nfork10++;
if (namefork[s].Count >= 100)
nfork100++;
if (namefork[s].Count >= 500)
nfork500++;
if (namefork[s].Count >= 1000)
nfork1000++;

sw.WriteLine(s);
foreach (int i in namefork[s])
if (!gndict.ContainsKey(i))
sw.WriteLine("Bad geonameid " + i.ToString());
continue;
sw.Write(i.ToString() + ";" + gndict[i].featurecode + ";");
if (countrydict.ContainsKey(gndict[i].adm[0]))
//Console.WriteLine(gndict[i].adm[0].ToString() + " " + countrydict[gndict[i].adm[0]].Name);
sw.Write(countrydict[gndict[i].adm[0]].Name);
else if (gndict.ContainsKey(gndict[i].adm[0]))
sw.Write(gndict[gndict[i].adm[0]].Name);
sw.Write(";");
if (gndict.ContainsKey(gndict[i].adm[1]))
sw.Write(gndict[gndict[i].adm[1]].Name_ml);
sw.Write(";");
if (gndict.ContainsKey(gndict[i].adm[2]))
sw.Write(gndict[gndict[i].adm[2]].Name_ml);
sw.Write(";");
sw.Write(gndict[i].latitude.ToString() + ";" + gndict[i].longitude.ToString() + ";");
if (gndict[i].Name_ml == s)
sw.Write("*");
else
sw.Write(gndict[i].Name_ml);
sw.Write(";" + gndict[i].wdid.ToString());
sw.WriteLine();
sw.WriteLine("#");
//sw.WriteLine();

Console.WriteLine("nfork = " + nfork.ToString());
Console.WriteLine("maxfork = " + maxfork.ToString());
Console.WriteLine("maxforkname = |" + maxforkname + "|");
Console.WriteLine("nfork2 = " + nfork2.ToString());
Console.WriteLine("nfork10 = " + nfork10.ToString());
Console.WriteLine("nfork100 = " + nfork100.ToString());
Console.WriteLine("nfork500 = " + nfork100.ToString());
Console.WriteLine("nfork1000 = " + nfork100.ToString());

public static PageList get_geotemplates()
PageList pl = new PageList(makesite);
pl.FillAllFromCategoryTree(mp(74, null));
//Skip all namespaces except templates (ns = 10):
Console.WriteLine("pl.Count = "+pl.Count().ToString());
//pl.RemoveNamespaces(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 100, 101 });
Console.WriteLine("pl.Count = " + pl.Count().ToString());

coordparams.Add("coord");
coordparams.Add("Coord");
coordparams.Add("lat_d");
coordparams.Add("lat_g");
coordparams.Add("latitude");
coordparams.Add("latitud");

return pl;

public static double coordlat(string coordstring)
//{{Coord|42|33|18|N|1|31|59|E|region:AD_type:city|display=title,inline}}

string[] cs = coordstring.Split('|');

if (cs.Length <= 2)
return 9999.9;
else
int ins = -1;
int iew = -1;
int iregion = -1;
for (int i = 1; i < cs.Length; i++)
if ((cs[i].ToUpper() == "N") || (cs[i].ToUpper() == "S"))
ins = i;
if ((cs[i].ToUpper() == "E") || (cs[i].ToUpper() == "W"))
iew = i;
if (cs[i].ToLower().Contains("region"))
iregion = i;
if (ins < 0)
return tryconvertdouble(cs[1]);
else
double lat = 0.0;
double scale = 1.0;
for (int i = 1; i < ins; i++)
double lx = tryconvertdouble(cs[i]);
if (lx < 90.0)
lat += lx / scale;
scale *= 60.0;
if (cs[ins].ToUpper() == "S")
lat = -lat;
return lat;
//else if (cs.Length < 9)
//{
// return tryconvertdouble(cs[1]);
//}
//else
//{
// double lat = tryconvertdouble(cs[1]) + tryconvertdouble(cs[2]) / 60 + tryconvertdouble(cs[3]) / 3600;
// if (cs[4].ToUpper() == "S")
// lat = -lat;
// return lat;
//}

public static double coordlong(string coordstring)
//{{Coord|42|33|18|N|1|31|59|E|region:AD_type:city|display=title,inline}}

string[] cs = coordstring.Split('|');
if (cs.Length <= 2)
return 9999.9;
else
int ins = -1;
int iew = -1;
int iregion = -1;
for (int i = 1; i < cs.Length; i++)
if ((cs[i].ToUpper() == "N") || (cs[i].ToUpper() == "S"))
ins = i;
if ((cs[i].ToUpper() == "E") || (cs[i].ToUpper() == "W"))
iew = i;
if (cs[i].ToLower().Contains("region"))
iregion = i;
if (iew < 0)
return tryconvertdouble(cs[2]);
else
double lon = 0.0;
double scale = 1.0;
for (int i = ins + 1; i < iew; i++)
double lx = tryconvertdouble(cs[i]);
if (lx < 180.0)
lon += lx / scale;
scale *= 60.0;
if (cs[iew].ToUpper() == "W")
lon = -lon;
return lon;
//else
//{
// double lon = tryconvertdouble(cs[5]) + tryconvertdouble(cs[6]) / 60 + tryconvertdouble(cs[7]) / 3600;
// if (cs[8].ToUpper() == "W")
// lon = -lon;
// return lon;
//}

public static string removearticle(string s)
string rs = s;
if (makelang == "sv")
if (s.IndexOf("en ") == 0)
rs = s.Remove(0,3);
else if (s.IndexOf("ett ") == 0)
rs = s.Remove(0,4);
else if (makelang == "ceb")
if (s.IndexOf("ang ") == 0)
rs = s.Remove(0,4);

return rs;

public static string getwikilink(string s)
int i1 = s.IndexOf("[[");
int i2 = s.IndexOf("]]");
if (i1 < 0)
return "";
if (i2 < i1 + 2)
return "";

return s.Substring(i1+2, i2 - i1 - 2);

public static void fill_featurearticle()
if (featurearticle.ContainsKey("vik"))
return;
if (makelang == "sv")
featurearticle.Add("vik", "havsvik");
featurearticle.Add("samhälle", "samhälle (geografi)");
featurearticle.Add("udde", "halvö");
featurearticle.Add("ö", "ö (landområde)");
featurearticle.Add("kulle", "kulle (landform)");
featurearticle.Add("del av lagun", "lagun");
//featurearticle.Add("periodisk sjö", "sjö");
//featurearticle.Add("periodisk saltsjö", "saltsjö");
//featurearticle.Add("periodisk korvsjö", "korvsjö");
featurearticle.Add("periodiska sjöar", "periodisk sjö");
featurearticle.Add("periodiska saltsjöar", "periodisk saltsjö");
featurearticle.Add("saltsjöar", "saltsjö");
featurearticle.Add("del av en sjö", "sjö");
featurearticle.Add("del av ett rev", "rev");
featurearticle.Add("trångt sund", "sund");
//featurearticle.Add("saltträsk", "våtmark");
featurearticle.Add("fors", "fors (vattendrag)");
featurearticle.Add("periodisk reservoar", "vattenmagasin");
featurearticle.Add("sabkha", "saltöken");
featurearticle.Add("grund", "grund (sjöfart)");
featurearticle.Add("källa", "vattenkälla");
featurearticle.Add("periodiskt vattendrag", "vattendrag");
featurearticle.Add("wadier", "wadi");
featurearticle.Add("periodisk våtmark", "våtmark");
featurearticle.Add("reservat", "indianreservat");
featurearticle.Add("övergiven gruva", "gruva");
featurearticle.Add("kitteldalar", "kitteldal");
featurearticle.Add("sänka", "bäcken (geografi)");
featurearticle.Add("klippöken", "öken");
featurearticle.Add("del av en ö", "ö (landområde)");
featurearticle.Add("karstområde", "karst");
featurearticle.Add("höjd", "kulle (landform)");
featurearticle.Add("undervattenshöjd", "kulle (landform)");
featurearticle.Add("morän", "morän (landform)");
featurearticle.Add("nunataker", "nunatak");
featurearticle.Add("sänkor", "bäcken (geografi)");
featurearticle.Add("del av halvö", "halvö");
featurearticle.Add("bergstoppar", "bergstopp");
featurearticle.Add("klippa", "klippa (geologi)");
featurearticle.Add("åmynning", "flodmynning");
featurearticle.Add("militärt övningsområde", "övningsfält");
featurearticle.Add("mangroveö", "mangrove");
featurearticle.Add("del av en halvö", "halvö");
featurearticle.Add("del av en platå", "platå");
featurearticle.Add("del av en slätt", "slätt");
featurearticle.Add("uddar", "halvö");
featurearticle.Add("stenöken", "öken");
featurearticle.Add("bankar", "sandbank");
featurearticle.Add("bank", "sandbank");
featurearticle.Add("sandbankar", "sandbank");
featurearticle.Add("dalar", "dal");
//featurearticle.Add("sadelpass", "bergspass");
featurearticle.Add("del av en lagun", "lagun");
featurearticle.Add("del av en ort", "stadsdel");
featurearticle.Add("delta", "floddelta");
featurearticle.Add("plattform", "massrörelse (geologi)");
featurearticle.Add("veckterräng", "veckning");
featurearticle.Add("bassäng", "bassäng (geologi)");
featurearticle.Add("kanjoner", "kanjon");
featurearticle.Add("fossil skog", "förstenat trä");
featurearticle.Add("åsar", "ås");
featurearticle.Add("undervattensåsar", "ås");
featurearticle.Add("undervattensås", "ås");
featurearticle.Add("orter", "ort");
featurearticle.Add("hög udde", "halvö");
featurearticle.Add("del av en dal", "dal");
featurearticle.Add("liten undervattenskulle", "kulle (landform)");
featurearticle.Add("små undervattenskullar", "kulle (landform)");
featurearticle.Add("undervattenskulle", "kulle (landform)");
featurearticle.Add("undervattenskullar", "kulle (landform)");
featurearticle.Add("tröskel", "tröskelfjord");
featurearticle.Add("kontinentalsluttning", "kontinentalbranten");
featurearticle.Add("undervattensdal", "dal");
featurearticle.Add("undervattensdalar", "dal");
featurearticle.Add("utlöpare", "utlöpare (landform)");
featurearticle.Add("guyoter", "guyot");
featurearticle.Add("terass", "terass (landform)");
featurearticle.Add("å", "å (vattendrag)");
featurearticle.Add("klint", "klint (landform)");
featurearticle.Add("ekonomisk region", "ekonomisk region (Finland)");
featurearticle.Add("", "");

public static string comment(string incomment)
return "";

public static List getcomments(string text)
List rl = new List();
Match m;
Regex HeaderPattern = new Regex("");

try
m = HeaderPattern.Match(text);
while (m.Success)
//Console.WriteLine("Found comment " + m.Groups[1] + " at " + m.Groups[1].Index);
rl.Add(m.Groups[1].Value);
m = m.NextMatch();
catch (Exception e)
Console.WriteLine(e.Message);
return rl;

public static string get_fcode(string text)
List comments = getcomments(text);
foreach (string c in comments)
string[] c2 = c.Split('.');
if (c2.Length == 2)
if (c2[0].Length == 1)
if ( c2[1].ToUpper() == c2[1] )
return c2[1];

return "";

public static string linkfeature(string fcode,int gnid)
fill_featurearticle();
string s = getfeaturelabelindet(makecountry,fcode,gnid);
Console.WriteLine("linkfeature " + fcode + " " + s);

//if (fcode.Contains("ADM"))
//{
// //Console.WriteLine("linkfeature ADM");
// int admlevel = -1;
// if (fcode.Length >= 4)
// admlevel = tryconvert(fcode.Substring(3, 1));
// if (admlevel > 0)
// s = getadmindet(makecountry, admlevel,gnid);
//}

string rs = s;

if (makelang == "sv")
if (s.IndexOf("en ") == 0)
rs = s.Insert(3, "[[");
//rs = s.Replace("en ", "en [[");
else if (s.IndexOf("ett ") == 0)
rs = s.Insert(4, "[[");
//rs = s.Replace("ett ", "ett [[");
else if (makelang == "ceb")
if (s.IndexOf("ang ") == 0)
rs = s.Insert(4, "[[");
if (s.IndexOf("mga ") == 0)
rs = s.Insert(4, "[[");

if (!rs.Contains("[["))
rs = "[[" + rs;

rs = rs + "]]";

Console.WriteLine("rs = " + rs);
string gw = getwikilink(rs);
Console.WriteLine("gw = " + gw);
if (featurearticle.ContainsKey(gw))
rs = rs.Replace(gw, featurearticle[gw] + "|" + gw);

rs = comment(featureclassdict[fcode].ToString() + "." + fcode) + rs;

return rs;

public static string fix_artname(string artname)
string rs = artname;
if (funny_quotes.Count == 0)
funny_quotes.Add("„", "“");//„...“ (makedonska m.m.)
funny_quotes.Add("“", "”");//“…” (engelska m.m.)
funny_quotes.Add("«", "»");//«…» (franska m.m.)

foreach (string q1 in funny_quotes.Keys)
if ( rs.Contains(q1) && rs.Contains(funny_quotes[q1]))
rs = rs.Replace(q1, "\"").Replace(funny_quotes[q1], "\"");

rs = rs.Replace("’", "'");
rs = rs.Replace("[", "").Replace("]", "");
rs = rs.Replace("{", "").Replace("}", "");

bool hascomma = rs.Contains(",");
rs = Regex.Replace(rs, @"[\u0000-\u001F]|[\u00AD]", string.Empty); //Ta bort nonprintable. \u00AD är mjukt bindestreck.
bool stillhascomma = rs.Contains(",");
if (hascomma != stillhascomma)
Console.WriteLine(rs);
Console.WriteLine("Comma removed ");
Console.ReadLine();
return rs;

public static void fix_names()
foreach (int gnid in gndict.Keys)
gndict[gnid].Name = fix_artname(gndict[gnid].Name);
gndict[gnid].Name_ml = fix_artname(gndict[gnid].Name_ml);

public static void read_artname2_file(string filename)
int nartname = 0;
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
string s = sr.ReadLine();
string[] words = s.Split(tabchar);
if (words.Length < 2)
continue;
nartname++;
int gnid = tryconvert(words[0]);
string aname = fix_artname(words[1]);

if ((gndict.Count > 0) && !checkdoubles)
if (gndict.ContainsKey(gnid))
gndict[gnid].artname2 = aname.Replace("*","");
if ((nartname % 1000000) == 0)
Console.WriteLine("nartname2 = " + nartname.ToString());

Console.WriteLine("nartname2 = " + nartname.ToString());

public static void read_oldartname_file(string filename)
int nartname = 0;
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
string s = sr.ReadLine();
string[] words = s.Split(tabchar);
if (words.Length < 2)
continue;
nartname++;
int gnid = tryconvert(words[0]);
string aname = fix_artname(words[1]);

if ((gndict.Count > 0) && !checkdoubles)
if (gndict.ContainsKey(gnid))
if ( gndict[gnid].articlename != aname )
gndict[gnid].oldarticlename = aname;
if (checkdoubles)
if (!oldartnamedict.ContainsKey(gnid))
oldartnamedict.Add(gnid, aname);

if ((nartname % 1000000) == 0)
Console.WriteLine("noldartname = " + nartname.ToString());

Console.WriteLine("noldartname = " + nartname.ToString());

public static void read_artname_file(string filename)
int nartname = 0;
int nparish = 0;
Console.WriteLine("artname: " + filename);
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
string s = sr.ReadLine();
string[] words = s.Split(tabchar);
if (words.Length < 2)
continue;
nartname++;
int gnid = tryconvert(words[0]);
string aname = fix_artname(words[1]);

//if ((makecountry == "AZ") && (filename.Contains("missing")) && (makelang == "sv")) //kludge to get around weird Azerbadjan bug
//{
// Page pz = new Page(makesite, aname.Replace("*", ""));
// tryload(pz, 1);
// if (is_fork(pz) && pz.text.Contains("Azerbajdzjan"))
// {
// aname += " (distrikt i Azerbajdzjan)";
// Console.WriteLine(aname);
// }
//}

if ((gndict.Count > 0) && !checkdoubles)
if (gndict.ContainsKey(gnid))
if (makecountry == "BE")
if (aname.Contains("(församling"))
aname = aname.Replace("(församling)", "(kommun i Belgien)");
aname = aname.Replace("(församling i", "(kommun i");
aname = aname.Replace("(församling,", "(kommun i Belgien,");
aname = aname.Replace("(församlingshuvudort", "(kommunhuvudort");
if (aname.Contains("(församling"))
nparish++;
Console.WriteLine(aname);

if (makecountry == "CW")
if (aname.Contains("Curacao"))
aname = aname.Replace("Curacao", "Curaçao");

if (makecountry == "FI")
if (aname.Contains("Åboland-Turunmaa"))
aname = aname.Replace("Åboland-Turunmaa", "Åboland");

if ((!gndict[gnid].articlename.Contains("*")) || (aname.Contains("*")) || filename.Contains("missing"))
gndict[gnid].articlename = aname;
if (words[1] != aname)
gndict[gnid].unfixedarticlename = words[1];
if ((aname.Contains("*")) || (gndict[gnid].Name == gndict[gnid].Name_ml) || filename.Contains("missing"))
gndict[gnid].Name_ml = remove_disambig(aname.Replace("*", ""));
if (aname.Contains("Östprovinsen"))
Console.WriteLine(gnid.ToString() + ": " + aname);
//else
if (!artnamedict.ContainsKey(gnid))
artnamedict.Add(gnid, aname);
else if (!artnamedict[gnid].Contains("*"))
artnamedict[gnid] = aname;

if ((nartname % 1000000) == 0)
Console.WriteLine("nartname = " + nartname.ToString());

Console.WriteLine("nartname = " + nartname.ToString());
Console.WriteLine("nparish = " + nparish.ToString());

public static void read_artname()
Console.WriteLine("read_artname");
string filename = "artname-" + makelang + ".txt"; //use current file
if ( checkdoubles )
filename = "artname-" + makelang + "-checkwiki.txt"; //use the file that really was checked against wiki
read_artname_file(filename);

if ( makelang == "sv" ) //fil med diverse handfixade namn på svenska
read_artname_file("missing-adm1-toartname.txt");

//Console.ReadLine();

if (makelang == "sv")
filename = "artname-ceb.txt";
else
filename = "artname-sv.txt";
read_artname2_file(filename);

//filename = "artname-"+makelang+"-old.txt";
//read_oldartname_file(filename);

public static string remove_disambig(string title)
string tit = title;
if (tit.IndexOf("(") > 0)
tit = tit.Remove(tit.IndexOf("(")).Trim();
else if (tit.IndexOf(",") > 0)
tit = tit.Remove(tit.IndexOf(",")).Trim();
//if (tit != title)
// Console.WriteLine(title + " |" + tit + "|");
return tit;

public static bool is_disambig(string title)
return (title != remove_disambig(title));

public static void fill_donecountries()
if (makelang == "sv")
donecountries.Add("BT");
donecountries.Add("AG");
donecountries.Add("BH");
donecountries.Add("MK");
donecountries.Add("MT");
donecountries.Add("SS");
donecountries.Add("NI");
donecountries.Add("LU");

public static void check_doubles()
int ndouble = 0;
int ndouble_ow = 0;
int ndouble_cw = 0;
int ndouble_coord = 0;
int ndouble_iw = 0;
int nfuzzy = 0;
int nadm1 = 0;
int nalldisambig = 0;
List fuzzylist = new List();
bool checkwiki = false;

hbookclass scripthist = new hbookclass();

Console.WriteLine("Check doubles");
//PageList geolist = get_geotemplates();
//foreach (Page p in geolist)
// Console.WriteLine("Template " + p.title);

Dictionary geotempdict = new Dictionary();

int np = 0;

using (StreamWriter swfuzz = new StreamWriter("manualmatch-ADM1-" + makelang + "-" + getdatestring() + ".txt"))
using (StreamWriter sw = new StreamWriter("artname-" + makelang + "-" + getdatestring() + ".txt"))
using (StreamReader sr = new StreamReader("namefork-"+makelang+".csv"))
while (!sr.EndOfStream)
string s = sr.ReadLine();
s = s.Trim(';');

//scripthist.Add(get_alphabet(s));
if (get_alphabet(s) == "none")
Console.WriteLine("none:"+s+"|");
//Console.ReadLine();
List fl = new List();

string countryname = "";
string adm1name = "";
string adm2name = "";
string fcode = "";

int nrealnames = 0;
int nnames = 0;
int imakelang = langtoint[makelang];

while (true)
string line = sr.ReadLine();
if (line[0] == '#')
break;
string[] words = line.Split(';');

//public class forkclass
//{
// public int geonameid = 0;
// public string featurecode = "";
// public string[] admname = new string[3];
// public double latitude = 0.0;
// public double longitude = 0.0;

//}

forkclass fc = new forkclass();
fc.geonameid = tryconvert(words[0]);
fc.featurecode = words[1];
fc.admname[0] = words[2];
if ( countryml.ContainsKey(words[2]))
fc.admname[0] = countryml[words[2]];
if ( countryiso.ContainsKey(words[2]))
fc.iso = countryiso[words[2]];
fc.admname[1] = words[3];
fc.admname[2] = words[4];
fc.latitude = tryconvertdouble(words[5]);
fc.longitude = tryconvertdouble(words[6]);
fc.realname = words[7];
fc.wdid = tryconvert(words[8]);
fc.featurename = getfeaturelabel(fc.iso, fc.featurecode, fc.geonameid);
if (altdict.ContainsKey(fc.geonameid))
foreach (altnameclass ac in altdict[fc.geonameid])
if (ac.ilang == imakelang)
if (ac.altname == s)
fc.realname = "*";
else
fc.realname = ac.altname;
nnames++;
if (fc.realname == "*")
nrealnames++;
fl.Add(fc);
countryname = words[2];
adm1name = words[3];
adm2name = words[4];
fcode = words[1];

bool allsamecountry = true;
//bool allsamefcode = true;
bool allsameadm1 = true;
bool allsameadm2 = true;
bool somesamecountry = false;
bool somesamefcode = false;
bool somesameadm1 = false;
bool somesameadm2 = false;

foreach (forkclass ff in fl)
if (ff.realname == "*")
if (ff.admname[0] != countryname)
allsamecountry = false;
if (ff.admname[1] != adm1name)
allsameadm1 = false;
if (ff.admname[2] != adm2name)
allsameadm2 = false;
//if (ff.featurecode != fcode)
// allsamefcode = false;

if (String.IsNullOrEmpty(ff.admname[1]))
somesameadm1 = true;
if (String.IsNullOrEmpty(ff.admname[2]))
somesameadm2 = true;

foreach (forkclass ff2 in fl)
if ((ff2.realname == "*") && (ff.geonameid != ff2.geonameid))
if (ff.admname[0] == ff2.admname[0])
somesamecountry = true;
if (ff.admname[1] == ff2.admname[1])
somesameadm1 = true;
if (ff.admname[2] == ff2.admname[2])
somesameadm2 = true;
if (ff.featurecode == ff2.featurecode)
somesamefcode = true;
if (ff.featurename == ff2.featurename)
somesamefcode = true;

if (nrealnames == 0)
continue;

//bool geotemplatefound = false;
bool coordfound = false;
bool pagefound = false;
string namefound = "";
double lat = 999.0;
double lon = 999.0;

//foreach (forkclass fc in fl)
// if (fc.realname == "*")
// {
// List enb = getexistingneighbors(fc.latitude, fc.longitude, 10.0);
// foreach (int nb in enb)
// {
// if (existingdict.ContainsKey(nb))
// {
// if ( remove_disambig(existingdict[nb].articlename) == s)
// {
// pagefound = true;
// coordfound = true;
// namefound = existingdict[nb].articlename;
// lat = fc.latitude;
// lon = fc.longitude;
// break;
// }
// }
// }
// }

if (checkwiki)
Page oldpage = new Page(makesite, s);
//Page forkpage = new Page(makesite, testprefix + s + " (" + mp(67, null) + ")");
if (tryload(oldpage, 1))
if (oldpage.Exists())
pagefound = true;
if (oldpage.IsRedirect())
oldpage.title = oldpage.RedirectsTo();
tryload(oldpage, 1);
pagefound = oldpage.Exists();

if (is_fork(oldpage))
pagefound = false;

if (pagefound)
//geotemplatefound = false;
coordfound = false;
double[] latlong = get_article_coord(oldpage);
if (latlong[0] + latlong[1] < 720.0)
coordfound = true;
Console.WriteLine(latlong[0].ToString() + "|" + latlong[1].ToString());
lat = latlong[0];
lon = latlong[1];
namefound = oldpage.title;
ndouble_cw++;

else //check against old artname-file
bool alldisambig = true;
foreach (forkclass fc in fl)
if (fc.realname == "*")
if (artnamedict.ContainsKey(fc.geonameid))
if (artnamedict[fc.geonameid].Contains("*"))
pagefound = true;
coordfound = true;
namefound = artnamedict[fc.geonameid].Replace("*", "");
lat = fc.latitude;
lon = fc.longitude;
alldisambig = false;
ndouble_ow++;
break;
else if (!artnamedict[fc.geonameid].Contains("("))
alldisambig = false;
else
Console.WriteLine("gnid missing in artnamedict! " + s);
//Console.ReadLine();
if ((alldisambig) && (nrealnames == 1 )) //page with that name exists but doesn't match any place
pagefound = true;
coordfound = false;
nalldisambig++;

Dictionary dadict = new Dictionary();
//public class disambigclass
//{
// bool existsalready = false;
// bool country = false;
// bool adm1 = false;
// bool adm2 = false;
// bool latlong = false;
// bool fcode = false;
//}

//Now we know if page exists:

if (pagefound)
//ndouble++;
if (nrealnames == 1)
foreach (forkclass fc in fl)
if (fc.realname == "*")
Disambigclass da = new Disambigclass();
da.fork = fc;

if (coordfound)
double dist = get_distance_latlong(lat, lon, fc.latitude, fc.longitude);
if (dist < 5.0) //Probably same object
da.existsalready = true;
//sw.WriteLine(fc.geonameid + tabstring + "*"+s);
else
da.fcode = true;
da.country = true;
//sw.WriteLine(fc.geonameid + tabstring + s + " (" + removearticle(featuredict[fc.featurecode]) + " " + mp(75, null) + " " + fc.admname[0] + ")");
else //no coordinates
da.fcode = true;
da.country = true;
//sw.WriteLine(fc.geonameid + tabstring + s + " (" + removearticle(featuredict[fc.featurecode]) + " " + mp(75, null) + " " + fc.admname[0] + ")");
dadict.Add(fc.geonameid, da);

else //several realnames, pagefound
if (coordfound)
foreach (forkclass fc in fl)
if (fc.realname == "*")
Disambigclass da = new Disambigclass();
da.fork = fc;

double dist = get_distance_latlong(lat, lon, fc.latitude, fc.longitude);
if (dist < 5.0) //Probably same object
da.existsalready = true;
//sw.WriteLine(fc.geonameid + tabstring + "X");
else
//sw.Write(fc.geonameid + tabstring + s + " (");
//sw.Write(removearticle(featuredict[fc.featurecode]));
da.fcode = true;
if (somesamefcode)
//sw.Write(" " + mp(75, null) + " " + fc.admname[0]);
da.country = !allsamecountry && !String.IsNullOrEmpty(da.fork.admname[0]);
if (somesamecountry)

//sw.Write(", ");
//if (!allsameadm1)
da.adm1 = !allsameadm1 && !String.IsNullOrEmpty(da.fork.admname[1]); // sw.Write(fc.admname[1]);
if (somesameadm1)
//if (!allsameadm1 && !String.IsNullOrEmpty(fc.admname[1]))
// sw.Write(", ");
//if (!allsameadm2)
// sw.Write(fc.admname[2]);
da.adm2 = !allsameadm2 && !String.IsNullOrEmpty(da.fork.admname[2]);
if (somesameadm2)
da.latlong = true;
//if (!allsameadm2 && !String.IsNullOrEmpty(fc.admname[2]))
// sw.Write(", ");
//sw.Write("lat " + fc.latitude.ToString("F2") + ", long " + fc.longitude.ToString("F2"));

//sw.WriteLine(")");
dadict.Add(fc.geonameid, da);

else //no coordinates, several realnames, page found
foreach (forkclass fc in fl)
if (fc.realname == "*")
Disambigclass da = new Disambigclass();
da.fork = fc;

//sw.Write(fc.geonameid + tabstring + s + " (");
//sw.Write(removearticle(featuredict[fc.featurecode]));
da.fcode = true;

if (somesamefcode)
//sw.Write(" " + mp(75, null) + " " + fc.admname[0]);
da.country = !allsamecountry && !String.IsNullOrEmpty(da.fork.admname[0]);
if (somesamecountry)

//sw.Write(", ");
//if (!allsameadm1)
da.adm1 = !allsameadm1 && !String.IsNullOrEmpty(da.fork.admname[1]); // sw.Write(fc.admname[1]);
if (somesameadm1)
//if (!allsameadm1 && !String.IsNullOrEmpty(fc.admname[1]))
// sw.Write(", ");
//if (!allsameadm2)
// sw.Write(fc.admname[2]);
da.adm2 = !allsameadm2 && !String.IsNullOrEmpty(da.fork.admname[2]);
if (somesameadm2)
da.latlong = true;
//if (!allsameadm2 && !String.IsNullOrEmpty(fc.admname[2]))
// sw.Write(", ");
//sw.Write("lat " + fc.latitude.ToString("F2") + ", long " + fc.longitude.ToString("F2"));

//sw.WriteLine(")");
dadict.Add(fc.geonameid, da);

else //page not found
if (nrealnames == 1)
foreach (forkclass fc in fl)
if (fc.realname == "*")
//sw.WriteLine(fc.geonameid + tabstring + s);
Disambigclass da = new Disambigclass();
da.fork = fc;
if (nnames > 1)
da.fcode = true;
da.country = true;
dadict.Add(fc.geonameid, da);
else //several realnames, page not found
foreach (forkclass fc in fl)
if (fc.realname == "*")
Disambigclass da = new Disambigclass();
da.fork = fc;

//sw.Write(fc.geonameid + tabstring + s + " (");
//sw.Write(removearticle(featuredict[fc.featurecode]));
da.fcode = true;

if (somesamefcode)
//sw.Write(" " + mp(75, null) + " " + fc.admname[0]);
da.country = !allsamecountry && !String.IsNullOrEmpty(da.fork.admname[0]);
if (somesamecountry)

//sw.Write(", ");
//if (!allsameadm1)
da.adm1 = !allsameadm1 && !String.IsNullOrEmpty(da.fork.admname[1]); // sw.Write(fc.admname[1]);
if (somesameadm1)
//if (!allsameadm1 && !String.IsNullOrEmpty(fc.admname[1]))
// sw.Write(", ");
//if (!allsameadm2)
// sw.Write(fc.admname[2]);
da.adm2 = !allsameadm2 && !String.IsNullOrEmpty(da.fork.admname[2]);
if (somesameadm2)
da.latlong = true;
//if (!allsameadm2 && !String.IsNullOrEmpty(fc.admname[2]))
// sw.Write(", ");
//sw.Write("lat " + fc.latitude.ToString("F2") + ", long " + fc.longitude.ToString("F2"));

//sw.WriteLine(")");
dadict.Add(fc.geonameid, da);

foreach (int gnid in dadict.Keys)
if (nrealnames > 1)
bool uniquecountry = !String.IsNullOrEmpty(dadict[gnid].fork.admname[0]);
bool uniqueadm1 = !String.IsNullOrEmpty(dadict[gnid].fork.admname[1]);
bool uniqueadm2 = !String.IsNullOrEmpty(dadict[gnid].fork.admname[2]);
bool uniquefcode = true;

foreach (forkclass ff2 in fl)
if ((ff2.realname == "*") && (ff2.geonameid != gnid))
if (dadict[gnid].fork.admname[0] == ff2.admname[0])
uniquecountry = false;
if (dadict[gnid].fork.admname[1] == ff2.admname[1])
uniqueadm1 = false;
if (dadict[gnid].fork.admname[2] == ff2.admname[2])
uniqueadm2 = false;
if (dadict[gnid].fork.featurecode == ff2.featurecode)
uniquefcode = false;

if (dadict[gnid].fcode && uniquefcode)
dadict[gnid].country = false;
dadict[gnid].adm1 = false;
dadict[gnid].adm2 = false;
dadict[gnid].latlong = false;
else if (dadict[gnid].country && uniquecountry)
dadict[gnid].adm1 = false;
dadict[gnid].adm2 = false;
dadict[gnid].latlong = false;
else if (dadict[gnid].adm1 && uniqueadm1)
dadict[gnid].adm2 = false;
dadict[gnid].latlong = false;
else if (dadict[gnid].adm2 && uniqueadm2)
dadict[gnid].latlong = false;

//if (!gndict.ContainsKey(gnid))
// continue;
string artname = "";

if (countrydict.ContainsKey(gnid))
artname = "*" + countrydict[gnid].Name_ml;

if (checkwiki && String.IsNullOrEmpty(artname)) //Look for interwiki matches
if (dadict[gnid].fork.wdid > 0)
XmlDocument cx = get_wd_xml(wdid);
if (cx != null)
Dictionary rd = get_wd_sitelinks(cx);
foreach (string wiki in rd.Keys)
string ssw = wiki.Replace("wiki", "");
if (ssw == makelang)
artname = "*" + rd[wiki];
ndouble_iw++;
break;

if ((String.IsNullOrEmpty(artname)) && (dadict[gnid].fork.featurecode == "ADM1")) //Look for ADM1 in category:
Console.WriteLine("Checking for ADM1 match " + s + ", " + dadict[gnid].fork.admname[0]);
if (existing_adm1.ContainsKey(dadict[gnid].fork.admname[0]))
Console.WriteLine("Country found; count = " + existing_adm1[dadict[gnid].fork.admname[0]].Count.ToString());
if (existing_adm1[dadict[gnid].fork.admname[0]].Contains(s))
artname = "*" + s;
else
foreach (string es in existing_adm1[dadict[gnid].fork.admname[0]])
if (remove_disambig(es) == "s")
artname = "*" + es;
nadm1++;
break;

if (String.IsNullOrEmpty(artname)) //Look for fuzzy matches:
int mindist = 999;
if (s.Length < 4)
mindist = 1;
else if (s.Length < 7)
mindist = 3;
else if (s.Length < 20)
mindist = 4;
else
mindist = 5;
int distmax = mindist;
mindist = 999;
string mindistname = "";

foreach (string es in existing_adm1[dadict[gnid].fork.admname[0]])
string tit = remove_disambig(es);
int dist = LevenshteinDistance(s, tit);
//Console.WriteLine(s+" | "+tit + ": "+dist.ToString());
if (dist < mindist)
mindist = dist;
mindistname = es;

if (mindist < distmax)
Console.WriteLine("Fuzzy match: " + s + " | " + mindistname + ": " + mindist.ToString());
//Console.ReadLine();
nadm1++;
fuzzylist.Add(gnid.ToString() + ": " + s + " | " + mindistname + ": " + mindist.ToString());
artname = "*" + mindistname;
else if (manualcheck && (!String.IsNullOrEmpty(mindistname)))
Console.WriteLine("Fuzzy match: " + s + " | " + mindistname + ": " + mindist.ToString());
Console.Write("OK? (y/n)");
char yn = Console.ReadKey().KeyChar;
if (yn == 'y')
nadm1++;
fuzzylist.Add(gnid.ToString() + ": " + s + " | " + mindistname + ": " + mindist.ToString());
artname = "*" + mindistname;
Console.WriteLine("Saving " + artname);
swfuzz.WriteLine(gnid.ToString() + tabstring + artname);

else
Console.WriteLine(yn.ToString());

//Console.ReadLine();

if (String.IsNullOrEmpty(artname)) //Look for fuzzy matches:
int mindist = 999;
if (s.Length < 4)
mindist = 0;
else if (s.Length < 7)
mindist = 2;
else if (s.Length < 20)
mindist = 3;
else
mindist = 4;
int distmax = mindist;
string mindistname = "";
List enb = getexisting(dadict[gnid].fork.latitude, dadict[gnid].fork.longitude, 10.0);
foreach (int nb in enb)
if (existingdict.ContainsKey(nb))
string cleanart = remove_disambig(existingdict[nb].articlename);
if (cleanart == s)
artname = "*" + existingdict[nb].articlename;
ndouble_coord++;
break;
else
int dist = LevenshteinDistance(s, cleanart);
//Console.WriteLine(s+" | "+cleanart + ": "+dist.ToString());
if (dist < mindist)
mindist = dist;
mindistname = existingdict[nb].articlename;

if (String.IsNullOrEmpty(artname))
if (mindist < distmax)
Console.WriteLine("Fuzzy match: " + s+" | "+mindistname + ": "+mindist.ToString());
//Console.ReadLine();
nfuzzy++;
fuzzylist.Add(gnid.ToString() + ": " + s + " | " + mindistname + ": " + mindist.ToString());
artname = "*" + mindistname;

if (String.IsNullOrEmpty(artname))

if (dadict[gnid].existsalready)
artname = "*" + s;
else
bool daneeded = dadict[gnid].fcode || dadict[gnid].country || dadict[gnid].adm1 || dadict[gnid].adm2 || dadict[gnid].latlong;
if (!daneeded)
artname = s;
else
if (String.IsNullOrEmpty(dadict[gnid].fork.admname[0]))
dadict[gnid].country = false;
artname = s + " (";
bool needscomma = false;
if (dadict[gnid].fcode)
artname += removearticle(getfeaturelabel(dadict[gnid].fork.iso, dadict[gnid].fork.featurecode, gnid));
//if (dadict[gnid].fork.featurecode.Contains("ADM"))
//{
// int admlevel = tryconvert(dadict[gnid].fork.featurecode.Replace("ADM",""));
// if ( admlevel > 0 )
// s += getadmlabel(dadict[gnid].fork.iso, admlevel);
// else
// s += removearticle(featuredict[dadict[gnid].fork.featurecode]);
//}
//else
//{
// s += removearticle(featuredict[dadict[gnid].fork.featurecode]);
//}
if (dadict[gnid].adm1 || dadict[gnid].adm2 || dadict[gnid].country)
artname += " " + mp(75, null) + " ";
else
needscomma = true;
if (dadict[gnid].country)
if (needscomma)
artname += ", ";
artname += dadict[gnid].fork.admname[0];
needscomma = true;
if (dadict[gnid].adm1)
if (needscomma)
artname += ", ";
artname += dadict[gnid].fork.admname[1];
needscomma = true;
if (dadict[gnid].adm2)
if (needscomma)
artname += ", ";
artname += dadict[gnid].fork.admname[2];
needscomma = true;
if (dadict[gnid].latlong)
if (needscomma)
artname += ", ";
artname += "lat " + dadict[gnid].fork.latitude.ToString("F2") + ", long " + dadict[gnid].fork.longitude.ToString("F2");
needscomma = true;

artname += ")";

if (donecountries.Contains(dadict[gnid].fork.iso))

if (oldartnamedict.ContainsKey(gnid))
string fname = removearticle(getfeaturelabel(dadict[gnid].fork.iso, dadict[gnid].fork.featurecode, gnid));
if ( !oldartnamedict[gnid].Contains("(") || oldartnamedict[gnid].Contains(fname))
artname = oldartnamedict[gnid];

sw.WriteLine(gnid.ToString() + tabstring + artname);
np++;
if (artname.Contains("*"))
ndouble++;
if ((np % 1000) == 0)
Console.WriteLine("np = " + np.ToString()+", "+countryname);

if ((ndouble % 100) == 0)
Console.WriteLine("n (doubles) = " + ndouble.ToString());

//while (s[0] != '#')
// s = sr.ReadLine();
//continue;

foreach (string ss in fuzzylist)
Console.WriteLine(ss);
Console.WriteLine("n (doubles) = " + ndouble.ToString());
Console.WriteLine("n (checkwiki) = " + ndouble_cw.ToString());
Console.WriteLine("n (oldwiki) = " + ndouble_ow.ToString());
Console.WriteLine("n (coord) = " + ndouble_coord.ToString());
Console.WriteLine("n (wikidata) = " + ndouble_iw.ToString());
Console.WriteLine("n (fuzzy match) = " + nfuzzy.ToString());
Console.WriteLine("n (ADM1-match) = " + nadm1.ToString());
Console.WriteLine("n (alldisambig) = " + nalldisambig.ToString());

//scripthist.PrintSHist();
//foreach (string gt in geotempdict.Keys)
// Console.WriteLine(gt + ":" + geotempdict[gt].ToString());

public static string make_coord_template(string countrycode, string fcode, double lat, double lon,string artname)
string rs = "{{Coord|";
rs += lat.ToString(culture_en) + "|";
rs += lon.ToString(culture_en) + "|";
//rs += "display=inline|";

string typecode = "landmark";
//Console.WriteLine("fcode = " + fcode);
string cat = categorydict[fcode];

if (fcode == "ADM1")
typecode = "adm1st";
else if (fcode == "ADM2")
typecode = "adm2nd";
else if (fcode.Contains("ADM"))
typecode = "adm3rd";
else
switch (cat)
case "populated places":
typecode = "city";
break;
case "areas":
case "plains":
case "deserts":
typecode = "adm1st";
break;
case "navigation":
case "wetlands":
case "seabed":
case "lakes":
case "coasts":
case "straits":
case "bays":
typecode = "waterbody";
break;
case "mountains":
case "hills":
case "volcanoes":
case "rock formations":
case "valleys":
typecode = "mountain";
break;
case "islands":
case "peninsulas":
typecode = "isle";
break;
case "forests":
typecode = "forest";
break;
case "streams":
typecode = "river";
break;
case "ice":
typecode = "glacier";
break;
default:
typecode = "landmark";
break;

rs += "region:" + countrycode + "_type:" + typecode;
rs += "|name="+artname+"}}";
return rs;

public static bool is_fork(Page p)
if (!p.Exists())
return false;

if (makelang == "ceb")
if (p.text.ToLower().Contains("{{giklaro"))
return true;
else if (makelang == "war")
if (p.text.ToLower().Contains("{{pansayod"))
return true;
else if (makelang == "sv")
if (forktemplates.Count == 0)
PageList pl = new PageList(makesite);
pl.FillFromCategory("Förgreningsmallar");
foreach (Page pp in pl)
forktemplates.Add(pp.title.Replace("Mall:","").ToLower());
foreach (string ft in forktemplates)
if (p.text.ToLower().Contains("{{" + ft))
return true;

return false;

public static string saveconflict(string thisname,string othername)
if (thisname == othername)
return "";

string rs = "";
string[] p9 = new string[2] { othername, getmonthstring() };
rs = mp(9, p9) + "\n";
if (pconflict == null)
pconflict = new Page(makesite, mp(13, null) + botname + "/Namnkonflikter-PRIVAT");
tryload(pconflict, 1);

string ptt = "*[[" + thisname + "]] [[" + othername + "]]\n";
if (!pconflict.text.Contains(ptt))
if (!conflictheadline)
pconflict.text += "\n\n== " + countryml[makecountryname] + " ==\n";
conflictheadline = true;

pconflict.text += "\n" + ptt;

trysave(pconflict, 1);
return rs;

public static string saveanomaly(string thisname, string reason)
string rs = "";
string[] p196 = new string[1] { reason };
rs = mp(196, p196) + "\n";
if (panomaly == null)
panomaly = new Page(makesite, mp(13, null) + botname + "/Anomalier-PRIVAT");
tryload(panomaly, 1);

string ptt = "*[[" + thisname + "]] " + reason;
if (!panomaly.text.Contains(ptt))
if (!anomalyheadline)
panomaly.text += "\n\n== " + countryml[makecountryname] + " ==\n";
anomalyheadline = true;
panomaly.text += "\n" + ptt;
trysave(panomaly, 1);
return rs;

public static void find_duplicate_forks()
Dictionary> forkdict = new Dictionary>();

using (StreamReader sw = new StreamReader("namefork-" + makelang + ".csv"))
while (!sw.EndOfStream)
string s = sw.ReadLine();
s = s.Trim(';');

List fl = new List();

string countryname = "";

string gnidstring = "";
int nreal = 0;

while (true)
string line = sw.ReadLine();
if (line[0] == '#')
break;
string[] words = line.Split(';');

//public class forkclass
//{
// public int geonameid = 0;
// public string featurecode = "";
// public string[] admname = new string[3];
// public double latitude = 0.0;
// public double longitude = 0.0;

//}

forkclass fc = new forkclass();
fc.geonameid = tryconvert(words[0]);
gnidstring += " " + fc.geonameid;
fc.featurecode = words[1];
fc.admname[0] = words[2];
fc.admname[1] = words[3];
fc.admname[2] = words[4];
fc.latitude = tryconvertdouble(words[5]);
fc.longitude = tryconvertdouble(words[6]);
fc.realname = words[7];

if (fc.realname == "*")
fc.realname = s;
nreal++;
//if (artnamedict.ContainsKey(fc.geonameid))
// fc.realname = artnamedict[fc.geonameid];
fl.Add(fc);
countryname = words[2];

if (fl.Count < 2)
continue;

gnidstring = gnidstring.Trim();

if (forkdict.ContainsKey(gnidstring))
Console.WriteLine(s + ";" + nreal.ToString());
forkdict[gnidstring].Add(s + ";" + nreal.ToString());
else
List ll = new List();
ll.Add(s + ";" + nreal.ToString());
forkdict.Add(gnidstring, ll);

using (StreamWriter sw = new StreamWriter("namefork-duplicates-" + makelang + getdatestring() + ".txt"))
foreach (string gnidstring in forkdict.Keys)
if (forkdict[gnidstring].Count > 1)
int nrmax = -1;
string srmax = "";
foreach (string ss in forkdict[gnidstring])
string[] sss = ss.Split(';');
int nreal = tryconvert(sss[1]);
if (nreal > nrmax)
nrmax = nreal;
srmax = sss[0];
if (nrmax > 0)
sw.Write(srmax);
foreach (string ss in forkdict[gnidstring])
string[] sss = ss.Split(';');
if (sss[0] != srmax)
sw.Write(";" + sss[0]);
sw.WriteLine();

public static void makeforkpages()
int nfork = 0;
List forkdoubles = new List();
int nu2019 = 0;

makesite.defaultEditComment = mp(69, null);
if ( !String.IsNullOrEmpty(makecountry))
makesite.defaultEditComment += " " + countryml[makecountryname];

if (pstats == null)
pstats = new Page(makesite, mp(13, null) + botname + "/Statistik");
pstats.Load();
pstats.text += "\n\n== [[" + countryml[makecountryname] + "]] grensidor ==\n\n";
trysave(pstats, 1);

Dictionary forkduplicatedict = new Dictionary();
using (StreamReader sw = new StreamReader("namefork-duplicates-" + makelang + ".txt"))
while (!sw.EndOfStream)
string s = sw.ReadLine();
string[] ss = s.Split(';');
if (ss.Length < 2)
continue;
for (int i = 1; i < ss.Length; i++)
if (!forkduplicatedict.ContainsKey(ss[i]))
forkduplicatedict.Add(ss[i], ss[0]); //dictionary from duplicate name to proper name

using (StreamReader sw = new StreamReader("namefork-" + makelang + ".csv"))
while (!sw.EndOfStream)
string s = sw.ReadLine();
s = s.Trim(';');

int nbranches = 0;

List fl = new List();

string countryname = "";

int nsartname = 0;
while (true)
string line = sw.ReadLine();

if (line[0] == '#')
break; //normal exit from the loop

string[] words = line.Split(';');

//public class forkclass
//{
// public int geonameid = 0;
// public string featurecode = "";
// public string[] admname = new string[3];
// public double latitude = 0.0;
// public double longitude = 0.0;

//}

forkclass fc = new forkclass();
fc.geonameid = tryconvert(words[0]);
fc.featurecode = words[1];
fc.admname[0] = words[2];
fc.admname[1] = words[3];
fc.admname[2] = words[4];
fc.latitude = tryconvertdouble(words[5]);
fc.longitude = tryconvertdouble(words[6]);
fc.realname = words[7];
if (fc.realname == "*")
fc.realname = s;
//if (artnamedict.ContainsKey(fc.geonameid))
// fc.realname = artnamedict[fc.geonameid];
fl.Add(fc);
countryname = words[2];
if (artnamedict.ContainsKey(fc.geonameid))
if (s == artnamedict[fc.geonameid].Replace("*", ""))
nsartname++;

//bool allsamecountry = true;
bool hasmakecountry = false;
bool hasanomaly = false;
string anomalytext = "";

//Console.WriteLine("# names in fork page = " + fl.Count.ToString());

if (fl.Count < 2)
continue;

foreach (forkclass ff in fl)
//if (ff.admname[0] != countryname)
// allsamecountry = false;
if (ff.admname[0] == makecountryname)
hasmakecountry = true;

if (!hasmakecountry)
Console.WriteLine("No place in makecountry");
continue;

if (!String.IsNullOrEmpty(resume_at_fork))
if (s != resume_at_fork)
stats.Addskip();
continue;
else
resume_at_fork = "";

Console.WriteLine("nsartname = " + nsartname.ToString());
if (nsartname >= 2)
Console.WriteLine(s+" Too many places link to same!");
//Console.ReadLine();

bool alreadyfork = false;
string alreadyforktitle = "";

string forkpagename = testprefix + s;
Page forkpage = new Page(makesite, forkpagename);
if (tryload(forkpage, 1))
if (forkpage.Exists() || (nsartname > 0 ))
if (!forkpage.Exists())
forkpage.text = "";

if (forkpage.text.Contains(mp(69, null))) //botmade fork; don't make again unless overwrite is set
if (!overwrite || human_touched(forkpage, makesite))
continue;
else
alreadyfork = false;
else
if (is_fork(forkpage))
alreadyfork = true;
alreadyforktitle = forkpage.title;

forkpage.title += " (" + mp(67, null) + ")";
if (tryload(forkpage, 1))
if (forkpage.Exists())
alreadyfork = true;
alreadyforktitle = forkpage.title;

if (forkpage.text.Contains(mp(69, null))) //botmade fork; don't make again
if (!overwrite || human_touched(forkpage, makesite))
continue;
else
alreadyfork = false;
else
forkpage.title = forkpage.title.Replace(")", " 2)");
if (tryload(forkpage, 1))
if (forkpage.Exists())
continue;

if (forkduplicatedict.ContainsKey(s))
if ( forkpage.title == remove_disambig(forkpage.title))
make_redirect_override(forkpage, forkduplicatedict[s],"",-1);
Console.WriteLine("duplicate fork " + forkpage.title + " - " + forkduplicatedict[s]);
//Console.ReadLine();
continue;

string origtext = forkpage.text;
forkpage.text = "";
if (alreadyfork)
forkpage.text += saveconflict(forkpage.title,alreadyforktitle);

forkpage.text += mp(120, null) + "\n\n";

string[] p68 = new string[1] { s };
forkpage.text += mp(68, p68)+":";
if (makelang == "sv")
forkpage.text = mp(142, null) + "\n" + forkpage.text;
forkpage.text += "\n" + comment("NOTERA: Om platser läggs till, tas bort eller ordningen på platserna ändras, bör också mallen Kartposition under samma rubrik korrigeras för att kartan ska förbli rättvisande.") + "\n";

string[] p71 = new string[1] { countryname };

//if (allsamecountry)
//{
// if (fl.Count > 2)
// forkpage.text += mp(71, p71);
// else
// forkpage.text += mp(78, p71);
//}

forkpage.text += "\n\n";

//if (allsamecountry)
//{
// string countrynameml = countryname;
// if (countryml.ContainsKey(countryname))
// countrynameml = countryml[countryname];
// string[] p73 = new string[2] { countrynameml, s };
// if (locatordict.ContainsKey(countryname))
// {
// forkpage.text += mp(72, null) + "+|" + locatordict[countryname] + "\n |caption = " + mp(73, p73) + "\n |float = right\n |width=300\n | places =";
// int inum = 0;
// foreach (forkclass ff in fl)
// {
// inum++;
// forkpage.text += mp(72, null) + "~|" + locatordict[countryname] + "| label = " + inum.ToString() + "| mark =Blue_pog.svg|position=right|background=white|lat=" + ff.latitude.ToString(culture_en) + "|long=" + ff.longitude.ToString(culture_en) + "|caption=|float=}}\n";
// }
// forkpage.text += "}}\n";
// }
// foreach (forkclass ff in fl)
// {
// string artname = s;
// if (artnamedict.ContainsKey(ff.geonameid))
// {
// if (artnamedict[ff.geonameid] != "X")
// artname = artnamedict[ff.geonameid];
// }
// string ss = "# [[" + artname + "]], ";
// if (!artname.Contains("(" + featuredict[ff.featurecode]))
// ss += featuredict[ff.featurecode];
// if (!String.IsNullOrEmpty(ff.admname[1]) && !artname.Contains(ff.admname[1]))
// ss += ", " + ff.admname[1];
// if (!String.IsNullOrEmpty(ff.admname[2]) && !artname.Contains(ff.admname[2]))
// ss += ", " + ff.admname[2];
// if (!artname.Contains(" lat "))
// {
// ss += ", lat. " + ff.latitude.ToString("F1", culture);
// ss += ", long. " + ff.longitude.ToString("F1", culture);
// }
// forkpage.text += ss + "\n";
// nbranches++;
// }
//}
//else
//{
Dictionary> fd = new Dictionary>();
SortedDictionary scountry = new SortedDictionary();

foreach (forkclass ff in fl)
string sortcountry = ff.admname[0];
if (countryml.ContainsKey(ff.admname[0]))
sortcountry = countryml[ff.admname[0]];
string locatorkey = ff.admname[0];
if ((String.IsNullOrEmpty(sortcountry)) || (countrydict.ContainsKey(ff.geonameid)))
sortcountry = mp(166, null);
locatorkey = "";
if ( !scountry.ContainsKey(sortcountry))
scountry.Add(sortcountry, locatorkey);
if (!fd.ContainsKey(locatorkey))
List ffl = new List();
fd.Add(locatorkey, ffl);
fd[locatorkey].Add(ff);

int ncountries = 0;
int maxpercountry = 0;
int nplaces = 0;
foreach (string cs in fd.Keys)
ncountries++;
nplaces += fd[cs].Count;
if (fd[cs].Count > maxpercountry)
maxpercountry = fd[cs].Count;

bool worldmaponly = ((ncountries > 4) && (maxpercountry < 4));

if (worldmaponly)
int mapsize = 450;

string caption = "";
if (makelang == "sv")
string ifcollapsed = "";//" mw-collapsed";
string collapseintro = "{| class=\"mw-collapsible" + ifcollapsed + "\" data-expandtext=\"Visa karta\" data-collapsetext=\"Dölj karta\" style=\"float:right; clear:right;\"\n|-\n!\n|-\n|\n";
forkpage.text += collapseintro;
forkpage.text += mp(72, null) + "+|" + locatordict[""].locatorname + "\n |caption = " + caption + "\n |float = right\n |width=" + mapsize.ToString() + "\n | places =";
int inum = 0;
foreach (string csl in scountry.Keys)
string cs = scountry[csl];
foreach (forkclass ff in fd[cs])
inum++;
forkpage.text += mp(72, null) + "~|" + locatordict[""].locatorname + "| label = " + inum.ToString() + "| mark =Blue_pog.svg|position=right|background=white|lat=" + ff.latitude.ToString(culture_en) + "|long=" + ff.longitude.ToString(culture_en) + "}}\n";
forkpage.text += "}}\n";
if (makelang == "sv")
forkpage.text += "|}\n"; //collapse-end

foreach (string csl in scountry.Keys)
forkpage.text += "== " + csl + " ==\n";
string cs = scountry[csl];

string ciso = "";
string locname = csl;
if (countryiso.ContainsKey(cs))
ciso = countryiso[cs];
locname = linkcountry(ciso);

if (locatordict.ContainsKey(cs) && !worldmaponly)
int mapsize = 300;
if (fd[cs].Count > 40)
mapsize = 600;
else if (fd[cs].Count > 8)
mapsize = 450;
else if (fd[cs].Count == 1)
mapsize = 150;

string[] p73 = new string[2] { locname, s };
string caption = mp(73, p73);
if (csl == mp(166, null))
caption = csl;
if (makelang == "sv")
string ifcollapsed = "";//" mw-collapsed";
string collapseintro = "{| class=\"mw-collapsible"+ifcollapsed+"\" data-expandtext=\"Visa karta\" data-collapsetext=\"Dölj karta\" style=\"float:right; clear:right;\"\n|-\n!\n|-\n|\n";
forkpage.text += collapseintro;
forkpage.text += mp(72, null) + "+|" + locatordict[cs].locatorname + "\n |caption = " + caption + "\n |float = right\n |width="+mapsize.ToString()+"\n | places =";
int inum = 0;
foreach (forkclass ff in fd[cs])
inum++;
forkpage.text += mp(72, null) + "~|" + locatordict[cs].locatorname + "| label = " + inum.ToString() + "| mark =Blue_pog.svg|position=right|background=white|lat=" + ff.latitude.ToString(culture_en) + "|long=" + ff.longitude.ToString(culture_en) + "}}\n";
forkpage.text += "}}\n";
if (makelang == "sv")
forkpage.text += "|}\n"; //collapse-end

List artnames = new List();
foreach (forkclass ff in fd[cs])
string artname = s;
if (artnamedict.ContainsKey(ff.geonameid))
//if (artnamedict[ff.geonameid] != "X")
// artname = artnamedict[ff.geonameid];
artname = artnamedict[ff.geonameid].Replace("*", "");
if (artnames.Contains(artname))
string existing = "";
if (artnamedict[ff.geonameid].Contains("*"))
existing = "*";
if (!forkdoubles.Contains(existing + s))
forkdoubles.Add(existing + s);
hasanomaly = true;
//forkpage.text = saveanomaly(forkpage.title, mp(201, null)) + forkpage.text;
if (!String.IsNullOrEmpty(anomalytext))
anomalytext += " ";
anomalytext += mp(201, null) + " [["+artname+"]]";

else
artnames.Add(artname);
foreach (forkclass ff2 in fd[cs])
if (ff2 != ff)
if (get_distance_latlong(ff.latitude, ff.longitude, ff2.latitude, ff2.longitude) < 1.0)
//forkpage.text = saveanomaly(forkpage.title, mp(202, null)) + forkpage.text;
hasanomaly = true;
string artname2 = "";
if (artnamedict.ContainsKey(ff.geonameid))
artname2 = artnamedict[ff2.geonameid].Replace("*", "");

if (!String.IsNullOrEmpty(anomalytext))
anomalytext += " ";
anomalytext += mp(202, null) + " [[" + artname + "]]" + " [[" + artname2 + "]]";

break;
else
break;
string ss = "# [[" + artname.Replace("*", "") + "]], ";
if (!artname.Contains("(" + getfeaturelabel(ciso, ff.featurecode, ff.geonameid)))
ss += getfeaturelabel(ciso, ff.featurecode, ff.geonameid) + ", ";
if (!String.IsNullOrEmpty(ff.admname[1]) && !artname.Contains(ff.admname[1]))
ss += ff.admname[1] + ", ";
if (!String.IsNullOrEmpty(ff.admname[2]) && !artname.Contains(ff.admname[2]))
ss += ff.admname[2] + ", ";
//if (!artname.Contains(" lat "))
//{
// ss += ", lat. " + ff.latitude.ToString("F1", culture);
// ss += ", long. " + ff.longitude.ToString("F1", culture);
//}

//Console.WriteLine(make_coord_template(ciso, ff.featurecode, ff.latitude, ff.longitude));
//Console.ReadLine();

ss += make_coord_template(ciso, ff.featurecode, ff.latitude, ff.longitude, artname.Replace("*", ""));

ss += comment("Geonames ID " + ff.geonameid.ToString());

forkpage.text += ss + "\n";
nbranches++;
//}

forkpage.text += "\n{{" + mp(69, null) + "}}\n";
//forkpage.text += "[[" + mp(70, null) + "]]\n";

if ((makelang == "sv") && (!is_latin(forkpage.title)))
string alph_sv = get_alphabet_sv(get_alphabet(remove_disambig(forkpage.title)));
if (!alph_sv.Contains("okänd"))
forkpage.text += "{{Sidnamn annan skrift|" + alph_sv + "}}\n";
else
Console.WriteLine(forkpage.title);
Console.WriteLine(remove_disambig(forkpage.title));
Console.WriteLine(alph_sv);
//Console.ReadLine();

string[] p215 = new string[] { "",getmonthstring() };
forkpage.AddToCategory(mp(215, p215).Trim());
p215[1] = "";
foreach (string csl in scountry.Keys)
p215[0] = csl;
forkpage.AddToCategory(mp(215, p215).Trim());

if (nbranches > 1)
if ( hasanomaly )
forkpage.text = saveanomaly(forkpage.title, anomalytext) + forkpage.text;

if ( forkpage.text != origtext )
trysave(forkpage, 2);

nfork++;
//if ( s == "Andorra" )
// Console.ReadLine();
Console.WriteLine("nfork = " + nfork.ToString());
romanian_redirect(forkpage.title);

Console.WriteLine(stats.GetStat());
if (pstats == null)
pstats = new Page(makesite, mp(13, null) + botname + "/Statistik");
pstats.Load();
//pstats.text += "\n\n== [[" + countryml[makecountryname] + "]] grensidor ==\n\n";
pstats.text += stats.GetStat();
trysave(pstats, 1);
stats.ClearStat();

Console.WriteLine("nfork = " + nfork.ToString());
foreach (string fd in forkdoubles)
Console.WriteLine(fd);
using (StreamWriter sw = new StreamWriter("forkdoubles.txt"))
foreach (string ul in forkdoubles)
sw.WriteLine(ul);

Console.WriteLine("forkdoubles = " + forkdoubles.Count.ToString());

Console.WriteLine("nu2019 = " + nu2019.ToString());

public static void print_geonameid(int id)
if (gndict.ContainsKey(id))
Console.WriteLine("Name = "+gndict[id].Name);
Console.WriteLine("Country = " + gndict[gndict[id].adm[0]].Name);
Console.WriteLine("Province = " + gndict[gndict[id].adm[1]].Name);

public static void read_locatorlist()
int n = 0;

using (StreamReader sr = new StreamReader(geonamesfolder + "locatorlist.txt"))
int makelangcol = -1;
int altcol = -1;
while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

if (words[0] == "en") //headline
for (int i = 1; i < words.Length; i++)
if (words[i] == makelang)
makelangcol = i;
if (words[i] == makelang + "-alt")
altcol = i;
Console.WriteLine("makelangcol = " + makelangcol.ToString());
Console.WriteLine("altcol = " + altcol.ToString());
continue;
//Console.WriteLine("wl = " + words.Length.ToString());

locatorclass lc = new locatorclass();
lc.locatorname = words[makelangcol];
if ((words.Length > altcol) && (!String.IsNullOrEmpty(words[altcol])))
lc.altlocator = words[altcol];

if ((words.Length > makelangcol) && (!String.IsNullOrEmpty(words[makelangcol])))
locatordict.Add(words[0], lc);

n++;
if ((n % 10) == 0)
Console.WriteLine("n (locatorlist) = " + n.ToString());

Console.WriteLine("n (locatorlist)= " + n.ToString());

public static void fill_admcap()
if (admcap.Count != 0)
return;

if (makelang == "ceb")
for (int i = 1; i < 5; i++)
foreach (string mc in admdict.Keys)
if ((!String.IsNullOrEmpty(admdict[mc].label[i - 1])) && (!admcap.ContainsKey(admdict[mc].label[i - 1])))
admcap.Add(admdict[mc].label[i - 1], "kapital sa " + admdict[mc].label[i - 1]);
else if (makelang == "sv")
admcap.Add("administrativ atoll", "atollhuvudort");
admcap.Add("administrativ by", "byhuvudort");
admcap.Add("administrativ enhet", "enhetshuvudort");
admcap.Add("administrativ ö", "öhuvudort");
admcap.Add("administrativt område", "områdeshuvudort");
admcap.Add("arrondissement", "arrondissementhuvudort");
admcap.Add("barrio", "barriohuvudort");
admcap.Add("byutvecklingskommitté", "byutvecklingskommittéhuvudort");
admcap.Add("community", "communityhuvudort");
admcap.Add("constituency", "constituencyhuvudort");
admcap.Add("corregimiento", "corregimientohuvudort");
admcap.Add("county", "countyhuvudort");
admcap.Add("delegation", "delegationshuvudort");
admcap.Add("delstat", "delstatshuvudstad");
admcap.Add("departement", "departementshuvudort");
admcap.Add("distrikt", "distriktshuvudort");
admcap.Add("division", "divisionshuvudort");
admcap.Add("emirat", "emirathuvudstad");
admcap.Add("entitet", "entitetshuvudort");
admcap.Add("fylke", "fylkeshuvudort");
admcap.Add("förbundsland", "förbundslandshuvudstad");
admcap.Add("församling", "församlingshuvudort");
admcap.Add("gemenskap", "gemenskapshuvudort");
admcap.Add("gewog", "gewoghuvudort");
admcap.Add("grannskap", "grannskapshuvudort");
admcap.Add("grevskap", "grevskapshuvudort");
admcap.Add("guvernement", "guvernementshuvudort");
admcap.Add("härad", "häradshuvudort");
admcap.Add("hövdingadöme", "hövdingadömeshuvudort");
admcap.Add("hövdingaråd", "hövdingarådshuvudort");
admcap.Add("kabupaten", "kabupatenhuvudort");
admcap.Add("kanton", "kantonhuvudstad");
admcap.Add("klan", "klanhuvudort");
admcap.Add("kommun", "kommunhuvudort");
admcap.Add("krets", "kretshuvudort");
admcap.Add("kungadöme", "kungadömeshuvudstad");
admcap.Add("kvarter", "kvartershuvudort");
admcap.Add("lokalstyresområde", "lokalstyresområdeshuvudort");
admcap.Add("län", "länshuvudort");
admcap.Add("mahaliyya", "mahaliyyahuvudort");
admcap.Add("mukim", "mukimhuvudort");
admcap.Add("oblast", "oblasthuvudort");
admcap.Add("oblyst", "oblysthuvudort");
admcap.Add("område", "områdeshuvudort");
admcap.Add("opština", "opštinahuvudort");
admcap.Add("parroqui", "parroquihuvudort");
admcap.Add("powiat", "powiathuvudort");
admcap.Add("prefektur", "prefekturhuvudort");
admcap.Add("provins", "provinshuvudstad");
admcap.Add("rajon", "rajonhuvudort");
admcap.Add("region", "regionhuvudort");
admcap.Add("autonom region", "regionhuvudort");
admcap.Add("regeringsdistrikt", "regeringsdistriktshuvudort");
admcap.Add("riksdel", "riksdelshuvudstad");
admcap.Add("rote", "rotehuvudort");
admcap.Add("rådsområde", "rådsområdeshuvudort");
admcap.Add("samhällsutvecklingsråd", "samhällsutvecklingsrådshuvudort");
admcap.Add("sektor", "sektorshuvudort");
admcap.Add("shehia", "shehiahuvudort");
admcap.Add("socken", "sockenhuvudort");
admcap.Add("stad", "stadshuvudort");
admcap.Add("stadsdel", "stadsdelshuvudort");
admcap.Add("subbarrio", "subbarriohuvudort");
admcap.Add("subdistrikt", "subdistriktshuvudort");
admcap.Add("subprefektur", "subprefekturhuvudort");
admcap.Add("sýsla", "sýslahuvudort");
admcap.Add("tehsil", "tehsilhuvudort");
admcap.Add("territorium", "territoriehuvudort");
admcap.Add("tidigare kommun", "huvudort för tidigare kommun");
admcap.Add("underdistrikt", "underdistriktshuvudort");
admcap.Add("utvecklingsregion", "utvecklingsregionshuvudort");
admcap.Add("ward", "wardhuvudort");
admcap.Add("voblast", "voblasthuvudort");
admcap.Add("vojvodskap", "vojvodskapshuvudort");
admcap.Add("zon", "zonhuvudort");
admcap.Add("åldermannaskap", "åldermannaskapshuvudort");
admcap.Add("ö och specialkommun", "öhuvudort");
admcap.Add("ögrupp", "ögruppshuvudort");
admcap.Add("öområde", "öområdeshuvudort");
admcap.Add("öråd", "örådshuvudort");
admcap.Add("parish", "parishhuvudort");
admcap.Add("parroquia", "parroquiahuvudort");
admcap.Add("freguesia", "freguesiahuvudort");
admcap.Add("kraj", "krajhuvudort");
admcap.Add("delrepublik", "delrepublikhuvudstad");
admcap.Add("autonomt distrikt", "distriktshuvudort");

public static void read_adm()
int n = 0;

List uniquelabels = new List();

string lf = "";

if (firstround)
using (StreamReader sr = new StreamReader(geonamesfolder + "adm-" + makelang + ".txt"))
while (!sr.EndOfStream)
String line = sr.ReadLine();

//if (n > 250)
//Console.WriteLine(line);

string[] words = line.Split(tabchar);
while (words.Length < 11)
line += tabstring;
words = line.Split(tabchar);

//Console.WriteLine("wl = " + words.Length.ToString());

admclass ad = new admclass();

int maxlabel = 0;

for (int i = 0; i < 5; i++)
ad.label[i] = words[i + 1];
if (!String.IsNullOrEmpty(words[i + 1]))
maxlabel = i + 1;
if (!uniquelabels.Contains(words[i + 1]))
uniquelabels.Add(words[i + 1]);
ad.det[i] = words[i + 6];

if (!admtodet.ContainsKey(ad.label[i]))
admtodet.Add(ad.label[i], ad.det[i]);

ad.maxadm = maxlabel;
if ( words[0] == makecountry )
Console.WriteLine(words[0] + ": maxadm = " + maxlabel.ToString());

admdict.Add(words[0], ad);

if ( saveadmlinks)
lf += "* " + linkcountry(words[0]) + "\n";
for (int i = 0; i < 5; i++)
if ( !String.IsNullOrEmpty(ad.label[i]))
lf += ":* ADM"+(i+1).ToString()+": [["+ad.label[i]+"]]\n";

n++;
if ((n % 10) == 0)
Console.WriteLine("n (adm) = " + n.ToString());

if (makelang == "sv")
if (!admtodet.ContainsKey("kraj"))
admtodet.Add("kraj", "krajen");
if (!admtodet.ContainsKey("autonomt distrikt"))
admtodet.Add("autonomt distrikt", "det autonoma distriktet");
if (!admtodet.ContainsKey("delrepublik"))
admtodet.Add("delrepublik", "delrepubliken");

Console.WriteLine("n (adm)= " + n.ToString());
if ( saveadmlinks)
Page pf = new Page(makesite,mp(13,null)+botname+"/linkadmin");
pf.text = lf;
trysave(pf,1);

//using (StreamWriter sw = new StreamWriter("uniquelabels.txt"))
//{
// foreach (string ul in uniquelabels)
// sw.WriteLine(ul);
//}
//Console.WriteLine("unique labels written");
//Console.ReadLine();

fill_admcap();

if (makecountry != "")
if (admdict.ContainsKey(makecountry))
string[] p167 = new string[1] { countryml[countrydict[countryid[makecountry]].Name] };
string admlink = mp(167,p167);
for (int i = 1; i < 5; i++)
if (!String.IsNullOrEmpty(admdict[makecountry].label[i - 1]))
if (!featurearticle.ContainsKey(admdict[makecountry].label[i - 1]))
featurearticle.Add(admdict[makecountry].label[i - 1], admlink);
else
featurearticle[admdict[makecountry].label[i - 1]] = admlink;
string fc = "PPLA";
if (i > 1)
fc += i.ToString();
if (admcap.ContainsKey(admdict[makecountry].label[i - 1]))
featuredict[fc] = admcap[admdict[makecountry].label[i - 1]];
if (!featurearticle.ContainsKey(admcap[admdict[makecountry].label[i - 1]]))
featurearticle.Add(admcap[admdict[makecountry].label[i - 1]], admlink);

//Console.ReadLine();

public static string getfeaturelabel(string countrycode, string fcode, int gnid)
return removearticle(getfeaturelabelindet(countrycode,fcode,gnid));

public static string getfeaturelabelindet(string countrycode, string fcode, int gnid)
if (!featuredict.ContainsKey(fcode))
return "unknown feature";

if (specialfeaturedict.ContainsKey(gnid))
return specialfeaturedict[gnid];

if (fcode.Contains("PPLA"))
int level = 1;
if (fcode != "PPLA")
level = tryconvert(fcode.Replace("PPLA", ""));
if ((level > 0) && (level <= 5))
string admlabel = getadmlabel(countrycode, level, gnid);
if (admcap.ContainsKey(admlabel))
if (makelang == "sv")
return "en " + admcap[admlabel];
else
return admcap[admlabel];
else
return featuredict[fcode];
else
return featuredict[fcode];
else if ( fcode.Contains("ADM"))
int level = tryconvert(fcode.Replace("ADM", ""));
if ((level > 0) && (level <= 5))
return getadmindet(countrycode, level, gnid);

return featuredict[fcode];

public static bool is_zhen(int gnid)
bool zhenfound = false;
if (gndict[gnid].Name.ToLower().Contains(" zhen"))
zhenfound = true;
else
if (altdict.ContainsKey(gnid))
foreach (altnameclass ac in altdict[gnid])
if (ac.altname.ToLower().Contains(" zhen"))
zhenfound = true;
break;
return zhenfound;

public static string getadmlabel(string countrycode, int level, int gnid)
string rs = "";
if (specialfeaturedict.ContainsKey(gnid))
rs = specialfeaturedict[gnid];
else if (admdict.ContainsKey(countrycode))
if (level <= admdict[countrycode].maxadm)
rs = admdict[countrycode].label[level - 1];
else
switch (countrycode)
case "MY":
if ((gndict.ContainsKey(gnid)) && (gndict[gnid].longitude > 106.0))
rs = getadmlabel("MY2", level, gnid);
else
rs = getadmlabel("MY1", level, gnid);
break;
case "GB": //different for different kingdoms in United Kingdom
int kingdom = 6269131;
if (gndict.ContainsKey(gnid))
kingdom = gndict[gnid].adm[1];
switch (kingdom)
case 6269131: //England
rs = getadmlabel("GB1", level, gnid);
break;
case 2641364: //Northern Ireland
rs = getadmlabel("GB2", level, gnid);
break;
case 2638360: //Scotland
rs = getadmlabel("GB3", level, gnid);
break;
case 2634895: //Wales
rs = getadmlabel("GB4", level, gnid);
break;
default:
rs = getadmlabel("GB1", level, gnid);
break;

break;
case "RU":
if (level == 1)
if (gndict.ContainsKey(gnid))
if (gndict[gnid].Name.Contains(" Oblast"))
rs = "oblast";
else if ((gndict[gnid].Name.Contains(" Krai")) || (gndict[gnid].Name.Contains(" Kray")))
rs = "kraj";
else if (gndict[gnid].Name.Contains(" Okrug"))
rs = "autonomt distrikt";
else
rs = "delrepublik";
else
rs = "oblast";
else
rs = (admdict["RU1"].label[level - 1]);
break;
case "CN":
if (level == 4)
if (gndict.ContainsKey(gnid))
bool zhenfound = is_zhen(gnid);
if (zhenfound)
rs = mp(297,null);
else
rs = (admdict["CN1"].label[level - 1]);
else
rs = (admdict["CN1"].label[level - 1]);
else
rs = (admdict["CN1"].label[level - 1]);
break;

default:
rs = (admdict["default"].label[level - 1]);
break;

return rs;

public static string getadmindet(string countrycode, int level, int gnid)
string rs = getadmlabel(countrycode, level, gnid);

if (makelang == "sv")
if (getadmdet(countrycode, level, gnid).EndsWith("t"))
rs = "ett " + rs;
else
rs = "en " + rs;

return rs;

public static string getadmdet(string countrycode, int level, int gnid)
string rs = getadmlabel(countrycode, level, gnid);
if (admtodet.ContainsKey(rs))
rs = admtodet[rs];

if (makelang == "ceb")
rs += " sa";

return rs;

public static double get_distance(int gnidfrom, int gnidto)
double gnidlat = gndict[gnidto].latitude;
double gnidlong = gndict[gnidto].longitude;
double countrylat = gndict[gnidfrom].latitude;
double countrylong = gndict[gnidfrom].longitude;

return get_distance_latlong(countrylat,countrylong,gnidlat,gnidlong);

public static double get_distance_latlong(double fromlat, double fromlong, double tolat, double tolong) //returns distance in km
double kmdeg = 40000 / 360; //km per degree at equator
double scale = Math.Cos(fromlat * 3.1416 / 180); //latitude-dependent longitude scale
double dlat = (tolat - fromlat) * kmdeg;
double dlong = (tolong - fromlong) * kmdeg * scale;

double dist = Math.Sqrt(dlat * dlat + dlong * dlong);

if (dist > 1000.0) //use great circle distance (Haversine formula)
double f1 = fromlat * Math.PI / 180.0; //convert to radians
double f2 = tolat * Math.PI / 180.0;
double l1 = fromlong * Math.PI / 180.0;
double l2 = tolong * Math.PI / 180.0;
double r = 6371.0; //Earth radius

double underroot = Math.Pow(Math.Sin((f2 - f1) / 2), 2) + Math.Cos(f1) * Math.Cos(f2) * Math.Pow(Math.Sin((l2 - l1) / 2), 2);
double root = Math.Sqrt(underroot);
if (root > 1)
root = 1;
dist = 2 * r * Math.Asin(root);

return dist;

public static double[] get_article_coord(Page p)
double lat = 9999.9;
double lon = 9999.9;
double[] latlong = { lat, lon };
int ncoord = 0;

if (coordparams.Count == 0)
coordparams.Add("Coord");
coordparams.Add("coord");
coordparams.Add("lat_d");
coordparams.Add("lat_g");
coordparams.Add("latitude");
coordparams.Add("latitud");
coordparams.Add("nordliggrad");
coordparams.Add("sydliggrad");
coordparams.Add("breddgrad");

Dictionary geotempdict = new Dictionary();

//string template = mp(63, null);
foreach (string tt in p.GetTemplates(true, true))
if (tt.Length < 5)
continue;
string cleantt = tt.Replace("\n", "").Trim().Substring(0, 5).ToLower();
Console.WriteLine("cleantt = |" + cleantt + "|");
//if (true)//(geolist.Contains(template + cleantt))
//{
//geotemplatefound = true;
//Console.WriteLine("Possible double");

if (!geotempdict.ContainsKey(cleantt))
geotempdict.Add(cleantt, 1);
else
geotempdict[cleantt]++;
bool foundwithparams = false;
//foreach (string ttt in p.GetTemplates(true, true))
// if (ttt.IndexOf(tt) == 0)
//{
foundwithparams = true;
//Console.WriteLine("foundwithparams");
if (cleantt == "coord")
Console.WriteLine("found {{coord}}");
string coordstring = tt;
if (coordstring.Length > 10)
double newlat = coordlat(coordstring);
double newlon = coordlong(coordstring);
if (newlat + newlon < 720.0)
if (ncoord == 0)
lat = newlat;
lon = newlon;
else if ((Math.Abs(newlat - lat) + Math.Abs(newlon - lon) > 0.01)) //two different coordinates in article; skip!
lat = 9999;
lon = 9999;
ncoord = 9999;
break;
else
lat = newlat;
lon = newlon;
if (lat + lon < 720.0)
ncoord++;
if (ncoord > 3)
break;

else
Dictionary pdict = Page.ParseTemplate(tt);
foreach (string cp in coordparams)
Console.WriteLine("cp = " + cp);
double oldlat = lat;
double oldlon = lon;
if (pdict.ContainsKey(cp))
//coordfound = true;
Console.WriteLine("found coordparams");
switch (cp)
case "latitude":
case "latitud":
lat = tryconvertdouble(pdict[cp]);
if (pdict.ContainsKey("longitude"))
lon = tryconvertdouble(pdict["longitude"]);
else if (pdict.ContainsKey("longitud"))
lon = tryconvertdouble(pdict["longitud"]);
else
Console.WriteLine("latitude but no longitude");
break;
case "nordliggrad":
case "sydliggrad":
lat = tryconvertdouble(pdict[cp]);
if (pdict.ContainsKey("östliggrad"))
lon = tryconvertdouble(pdict["östliggrad"]);
else if (pdict.ContainsKey("västliggrad"))
lon = tryconvertdouble(pdict["västliggrad"]);
else
Console.WriteLine("latitude but no longitude");
break;
case "breddgrad":
lat = tryconvertdouble(pdict[cp]);
if (pdict.ContainsKey("längdgrad"))
lon = tryconvertdouble(pdict["längdgrad"]);
else
Console.WriteLine("latitude but no longitude");
break;
case "lat_d":
case "latd":
case "lat_g":
double llat = 0.0;
llat = tryconvertdouble(pdict[cp]);
if (llat > 0)
lat = llat;
if (pdict.ContainsKey("long_d"))
lon = tryconvertdouble(pdict["long_d"]);
else if (pdict.ContainsKey("longd"))
lon = tryconvertdouble(pdict["longd"]);
else if (pdict.ContainsKey("long_g"))
lon = tryconvertdouble(pdict["long_g"]);
if (pdict.ContainsKey("lat_m"))
lat += tryconvertdouble(pdict["lat_m"]) / 60;
if (pdict.ContainsKey("long_m"))
lon += tryconvertdouble(pdict["long_m"]) / 60;
if (pdict.ContainsKey("lat_s"))
lat += tryconvertdouble(pdict["lat_s"]) / 3600;
if (pdict.ContainsKey("long_s"))
lon += tryconvertdouble(pdict["long_s"]) / 3600;
if (pdict.ContainsKey("lat_NS"))
if (pdict["lat_NS"].ToUpper() == "S")
lat = -lat;
if (pdict.ContainsKey("long_EW"))
if (pdict["long_EW"].ToUpper() == "W")
lon = -lon;
break;
case "Coord":
case "coord": //{{Coord|42|33|18|N|1|31|59|E|region:AD_type:city|display=title,inline}}
string coordstring = pdict[cp];
if (coordstring.Length > 10)
lat = coordlat(coordstring);
lon = coordlong(coordstring);
break;
default:
Console.WriteLine("coord-default:" + tt);
break;

if (lat + lon < 720.0)
if ((Math.Abs(oldlat - lat) + Math.Abs(oldlon - lon) > 0.01)) //two different coordinates in article; skip!
lat = 9999;
lon = 9999;
ncoord = 9999;
break;
else
lat = oldlat;
lon = oldlon;

if (lat + lon < 720.0)
ncoord++;
if (ncoord > 3)
break;

//}
if (!foundwithparams)
Console.WriteLine("Params not found");
Console.WriteLine("lat = " + lat.ToString());
Console.WriteLine("lon = " + lon.ToString());
//}

if (ncoord > 4) //several coordinate sets, probably a list or something; return failure
return latlong;

latlong[0] = lat;
latlong[1] = lon;
return latlong;

//public static double[] get_article_coord_old(Page p)
//{
// double lat = 9999.9;
// double lon = 9999.9;
// double[] latlong = { lat, lon };

// if (coordparams.Count == 0)
// {
// coordparams.Add("coord");
// coordparams.Add("lat_d");
// coordparams.Add("lat_g");
// coordparams.Add("latitude");
// coordparams.Add("latitud");
// }

// Dictionary geotempdict = new Dictionary();

// string template = mp(63, null);
// foreach (string tt in p.GetTemplates(false, true))
// {
// string cleantt = initialcap(tt.Replace("\n", "").Trim());
// Console.WriteLine("tt = |" + cleantt + "|");
// if (true)//(geolist.Contains(template + cleantt))
// {
// //geotemplatefound = true;
// Console.WriteLine("Possible double");

// if (!geotempdict.ContainsKey(cleantt))
// geotempdict.Add(cleantt, 1);
// else
// geotempdict[cleantt]++;
// bool foundwithparams = false;
// foreach (string ttt in p.GetTemplates(true, true))
// if (ttt.IndexOf(tt) == 0)
// {
// foundwithparams = true;
// Console.WriteLine("foundwithparams");
// if (cleantt == "Coord")
// {
// Console.WriteLine("found {{coord}}");
// string coordstring = ttt;
// if (coordstring.Length > 10)
// {
// lat = coordlat(coordstring);
// lon = coordlong(coordstring);
// }

// }
// else
// {
// Dictionary pdict = makesite.ParseTemplate(ttt);
// foreach (string cp in coordparams)
// {
// Console.WriteLine("cp = " + cp);
// if (pdict.ContainsKey(cp))
// {
// //coordfound = true;
// Console.WriteLine("found coordparams");
// switch (cp)
// {
// case "latitude":
// case "latitud":
// lat = tryconvertdouble(pdict[cp]);
// if (pdict.ContainsKey("longitude"))
// lon = tryconvertdouble(pdict["longitude"]);
// else if (pdict.ContainsKey("longitud"))
// lon = tryconvertdouble(pdict["longitud"]);
// else
// Console.WriteLine("latitude but no longitude");
// break;
// case "lat_d":
// case "latd":
// case "lat_g":
// double llat = 0.0;
// llat = tryconvertdouble(pdict[cp]);
// if (llat > 0)
// {
// lat = llat;
// if (pdict.ContainsKey("long_d"))
// lon = tryconvertdouble(pdict["long_d"]);
// else if (pdict.ContainsKey("longd"))
// lon = tryconvertdouble(pdict["longd"]);
// else if (pdict.ContainsKey("long_g"))
// lon = tryconvertdouble(pdict["long_g"]);
// if (pdict.ContainsKey("lat_m"))
// lat += tryconvertdouble(pdict["lat_m"]) / 60;
// if (pdict.ContainsKey("long_m"))
// lon += tryconvertdouble(pdict["long_m"]) / 60;
// if (pdict.ContainsKey("lat_s"))
// lat += tryconvertdouble(pdict["lat_s"]) / 3600;
// if (pdict.ContainsKey("long_s"))
// lon += tryconvertdouble(pdict["long_s"]) / 3600;
// if (pdict.ContainsKey("lat_NS"))
// {
// if (pdict["lat_NS"].ToUpper() == "S")
// lat = -lat;
// }
// if (pdict.ContainsKey("long_EW"))
// {
// if (pdict["long_EW"].ToUpper() == "W")
// lon = -lon;
// }
// }
// break;
// case "coord": //{{Coord|42|33|18|N|1|31|59|E|region:AD_type:city|display=title,inline}}
// string coordstring = pdict["coord"];
// if (coordstring.Length > 10)
// {
// lat = coordlat(coordstring);
// lon = coordlong(coordstring);
// }
// break;
// default:
// Console.WriteLine("coord-default:" + ttt);
// break;

// }
// }
// }
// }
// }
// if (!foundwithparams)
// Console.WriteLine("Params not found");
// Console.WriteLine("lat = " + lat.ToString());
// Console.WriteLine("lon = " + lon.ToString());
// }
// }
// latlong[0] = lat;
// latlong[1] = lon;
// return latlong;
//}

public static int get_direction_latlong(double fromlat, double fromlong, double tolat, double tolong)
double kmdeg = 40000.0 / 360.0; //km per degree at equator
double scale = Math.Cos(fromlat * 3.1416 / 180); //latitude-dependent longitude scale
double dlat = (tolat - fromlat) * kmdeg;
double dlong = (tolong - fromlong) * kmdeg * scale;

//Console.WriteLine("dlat,dlong = " + dlat.ToString() + " " + dlong.ToString());
if (dlat * dlat > 4.0 * dlong * dlong)
if (dlat > 0) // north
return 1;
else //south
return 2;
else if (dlong * dlong > 4.0 * dlat * dlat)
if (dlong > 0) // east
return 4;
else //west
return 3;
else if (dlong > 0)
if (dlat > 0) //northeast
return 5;
else //southeast
return 6;
else
if (dlat > 0) //northwest
return 7;
else //southwest
return 8;

// 1
// 7 5
// 3 4
// 8 6
// 2

public static int get_direction(int gnidfrom, int gnidto)
double tolat = gndict[gnidto].latitude;
double tolong = gndict[gnidto].longitude;
double fromlat = gndict[gnidfrom].latitude;
double fromlong = gndict[gnidfrom].longitude;
return get_direction_latlong(fromlat, fromlong, tolat,tolong);

public static int get_pix_direction(int x, int y, int x0, int y0, double scale)
double dlat = y0 - y; //reverse sign; +y=south!
double dlong = (x-x0) * scale;

//Console.WriteLine("dlat,dlong = " + dlat.ToString() + " " + dlong.ToString());
if (dlat * dlat > 4.0 * dlong * dlong)
if (dlat > 0) // north
return 1;
else //south
return 2;
else if (dlong * dlong > 4.0 * dlat * dlat)
if (dlong > 0) // east
return 4;
else //west
return 3;
else if (dlong > 0)
if (dlat > 0) //northeast
return 5;
else //southeast
return 6;
else
if (dlat > 0) //northwest
return 7;
else //southwest
return 8;

// 1
// 7 5
// 3 4
// 8 6
// 2

public static int[] getdircoord(int dir) //translate from direction codes of get_pix_direction() into +/-1 x +/-1 y
int[] rc = new int[2] {0,0};
switch (dir)
case 1:
rc[1] = 1;
break;
case 2:
rc[1] = -1;
break;
case 3:
rc[0] = -1;
break;
case 4:
rc[0] = 1;
break;
case 5:
rc[0] = 1;
rc[1] = 1;
break;
case 6:
rc[0] = 1;
rc[1] = -1;
break;
case 7:
rc[0] = -1;
rc[1] = 1;
break;
case 8:
rc[0] = -1;
rc[1] = -1;
break;
return rc;

public static int getcountrypart(int gnid)
double gnidlat = gndict[gnid].latitude;
double gnidlong = gndict[gnid].longitude;
double countrylat = gndict[gndict[gnid].adm[0]].latitude;
double countrylong = gndict[gndict[gnid].adm[0]].longitude;
double area = countrydict[gndict[gnid].adm[0]].area;
double kmdeg = 40000 / 360; //km per degree at equator
double scale = Math.Cos(0.5*(countrylat+gnidlat)*3.1416/180); //latitude-dependent longitude scale
double dlat = (gnidlat - countrylat)*kmdeg;
double dlong = (gnidlong - countrylong)*kmdeg * scale;

if (countrylat < -80) //Antarctica
if (gnidlat < -86) //central part
return 82;

if ((gnidlat > -61) && ((gnidlong > -47) && (gnidlong < -44))) //South Orkney Islands
return 84;

if (gnidlong > -45) //East Antarctica
return 86;
else //West Antarctica
if ((gnidlong > -64) && (gnidlong < -52))
if (((gnidlong < -60) && (gnidlat > -64)) || ((gnidlong >= -60) && (gnidlat > -62.8))) //South Shetland Islands
return 83;
else
return 85;
else
return 85;

if ((dlat * dlat + dlong * dlong) < (area / 9)) //central part
return 82;

if (dlat * dlat > 4 * dlong * dlong)
if (dlat > 0) // northern part
return 83;
else //southern part
return 84;
else if (dlong * dlong > 4 * dlat * dlat)
if (dlong > 0) // eastern part
return 86;
else //western part
return 85;
else if (dlong > 0)
if (dlat > 0) //northeastern
return 87;
else //southeastern
return 88;
else
if (dlat > 0) //northwestern
return 89;
else //southwestern
return 90;

public static void fill_motherdict() //mother countries of colonies
if (motherdict.Count > 0)
return;
motherdict.Add("AN", "NL");

motherdict.Add("AS", "US");
motherdict.Add("AW", "NL");
motherdict.Add("AX", "FI");
motherdict.Add("BL", "FR");
motherdict.Add("BV", "NO");
motherdict.Add("CK", "NZ");
motherdict.Add("CX", "AU");
motherdict.Add("FK", "GB");
motherdict.Add("FM", "US");
motherdict.Add("FO", "DK");
motherdict.Add("GF", "FR");
motherdict.Add("GG", "GB");
motherdict.Add("GI", "GB");
motherdict.Add("GL", "DK");
motherdict.Add("GP", "FR");
motherdict.Add("GU", "US");
motherdict.Add("HK", "CN");
motherdict.Add("HM", "AU");
motherdict.Add("IM", "GB");
motherdict.Add("IO", "GB");
motherdict.Add("JE", "GB");
motherdict.Add("MF", "FR");
motherdict.Add("MH", "US");
motherdict.Add("MO", "CN");
motherdict.Add("MP", "US");
motherdict.Add("MQ", "FR");
motherdict.Add("MS", "GB");
motherdict.Add("NU", "NZ");
motherdict.Add("PF", "FR");
motherdict.Add("PM", "FR");
motherdict.Add("PN", "GB");
motherdict.Add("PR", "US");
motherdict.Add("RE", "FR");
motherdict.Add("SH", "GB");
motherdict.Add("SJ", "NO");
motherdict.Add("TC", "GB");
motherdict.Add("TF", "FR");
motherdict.Add("TK", "NZ");
motherdict.Add("UM", "US");
motherdict.Add("VG", "GB");
motherdict.Add("VI", "US");
motherdict.Add("WF", "FR");
motherdict.Add("YT", "FR");
motherdict.Add("SX", "NL");
motherdict.Add("CC", "AU");
motherdict.Add("BM", "GB");
motherdict.Add("CW", "NL");
motherdict.Add("GS", "GB");
motherdict.Add("KY", "GB");
motherdict.Add("NC", "FR");
motherdict.Add("NF", "AU");
Console.WriteLine("Motherdict: " + motherdict.Count.ToString());

public static void read_country_info()
int n = 0;

using (StreamReader sr = new StreamReader(geonamesfolder + "countryInfo.txt"))
int makelangcol = -1;
while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

if (words[0] == "ISO") //headline
for (int i = 1;iif (words[i] == makelang)
makelangcol = i;
continue;

int geonameid = -1;

countryclass country = new countryclass();

country.Name = words[4];
geonameid = tryconvert(words[16]);
country.iso = words[0];
country.iso3 = words[1];
country.isonumber = tryconvert(words[2]);
country.fips = words[3];
country.capital = words[5];
country.area = tryconvertdouble(words[6]);
country.population = tryconvertlong(words[7]);
country.continent = words[8];
country.tld = words[9];
country.currencycode = words[10];
country.currencyname = words[11];
country.phone = words[12];
country.postalcode = words[13];
foreach (string ll in words[15].Split(','))
//Console.WriteLine("ll.Split('-')[0] = " + ll.Split('-')[0]);
string lcode = ll.Split('-')[0];
if (String.IsNullOrEmpty(country.nativewiki))
country.nativewiki = lcode;
if ( langtoint.ContainsKey(lcode))
country.languages.Add(langtoint[lcode]);
foreach (string ll in words[17].Split(','))
country.bordering.Add(ll);

if (makelangcol > 0)
country.Name_ml = words[makelangcol];
else
country.Name_ml = country.Name;
countryml.Add(country.Name, country.Name_ml);
countryiso.Add(country.Name, country.iso);

if (geonameid > 0)
countryid.Add(country.iso, geonameid);

countrydict.Add(geonameid, country);
//Console.WriteLine(country.iso+":"+geonameid.ToString());

n++;
if ((n % 10) == 0)
Console.WriteLine("n (country_info) = " + n.ToString());

Console.WriteLine("n (country_info)= " + n.ToString());

if (savewikilinks)
Page pt = new Page(makesite, mp(13, null) + botname + "/countrylinks");
pt.text = "Country links used by Lsjbot\n\n";
foreach (string cn in countryml.Keys)
pt.text += "* [[" + countryml[cn] + "]]\n";
trysave(pt, 1);

fill_motherdict();

public static string countrytitle(int gnid)
string rs = "";
string iso = "";
if (countrydict.ContainsKey(gnid))
iso = countrydict[gnid].iso;

if (makelang != "sv")
return rs;

switch (iso)
case "MT":
case "IE":
case "IS":
rs = "republiken ";
break;
case "FK":
rs = "territoriet ";
break;
default:
rs = "";
break;

return rs;

public static string linkcountry(int gnid)
if (countrydict.ContainsKey(gnid))
return linkcountry(countrydict[gnid].iso);
else
return "";

public static string linkcountry(string ciso)
if (!countryid.ContainsKey(ciso))
return ciso;
int gnid = countryid[ciso];
string rt = "[[" + countryml[countrydict[gnid].Name] + "]]";
if (motherdict.ContainsKey(ciso))
//int mothergnid = countryid[motherdict[ciso]];
string mama = linkcountry(motherdict[ciso]);
if (((motherdict[ciso] == "DK")||(motherdict[ciso] == "NL")) && (makelang == "sv"))
mama = mama.Replace("[[", "[[Kungariket ");
rt += " (" + mama + ")";

return rt;

public static void get_country_iw(string langcode)
//Console.WriteLine("get country iw " + langcode);
//using (StreamWriter sw = new StreamWriter("countrynames-" + langcode + ".csv"))
//{

// foreach (int gnid in countrydict.Keys)
// {
// string langname = countrydict[gnid].Name;
// List iwlist = Interwiki(wdsite, countrydict[gnid].Name);
// foreach (string iws in iwlist)
// {
// string[] ss = iws.Split(':');
// string iwcode = ss[0];
// string iwtitle = ss[1];
// //Console.WriteLine("iw - " + iwcode + ":" + iwtitle);
// if (iwcode == langcode)
// langname = iwtitle;
// }
// sw.WriteLine(countrydict[gnid].Name + ";" + langname);
// Console.WriteLine(countrydict[gnid].Name + ";" + langname);

// }
//}

public static List Interwiki(Site site, string title)

//Borrowed from http://sv.wikipedia.org/wiki/Wikipedia:Projekt_DotNetWikiBot_Framework/Innocent_bot/Addbotkopia
List r = new List();
XmlDocument doc = new XmlDocument();

string url = "action=wbgetentities&sites=enwiki&titles=" + HttpUtility.UrlEncode(title) + "&languages=en&format=xml";
//string tmpStr = site.PostDataAndGetResultHTM(site.site+"/w/api.php", url);
try
//string tmpStr = site.PostDataAndGetResultHTM(site.site + "/w/api.php", url);
string tmpStr = site.PostDataAndGetResult(site.address + "/w/api.php", url);
doc.LoadXml(tmpStr);
for (int i = 0; i < doc.GetElementsByTagName("sitelink").Count; i++)
string s = doc.GetElementsByTagName("sitelink")[i].Attributes.GetNamedItem("site").Value;
string t = doc.GetElementsByTagName("sitelink")[i].Attributes.GetNamedItem("title").Value;
s = s.Replace("_", "-");
string t2 = s.Substring(0, s.Length - 4) + ":" + t;
//Console.WriteLine(t2);
r.Add(t2);
catch (WebException e)
string message = e.Message;
Console.Error.WriteLine(message);

return r;

public static string fnum(double x)
string rt = "{{formatnum:";
if (x < 1.0)
rt += x.ToString("F2",culture_en);
else if ( x < 30.0 )
rt += x.ToString("F1", culture_en);
else
rt += x.ToString("F0", culture_en);
rt += "}}";
return rt;

public static string fnum(long i)
return "{{formatnum:" + i.ToString() + "}}";

public static string fnum(int i)
return "{{formatnum:" + i.ToString() + "}}";

public static void fill_kids_features()
foreach (int gnid in gndict.Keys)
int parent = 0;
for (int i = 0; i < 5; i++)
if ((gndict[gnid].adm[i] > 0) && (gndict[gnid].adm[i] != gnid))
parent = gndict[gnid].adm[i];
if ((gndict[gnid].featureclass == 'A') && (gndict[gnid].featurecode.Contains("ADM")) && (!gndict[gnid].featurecode.Contains("ADMD")) && (!gndict[gnid].featurecode.Contains("H")))
if (gndict.ContainsKey(parent))
gndict[parent].children.Add(gnid);
else
if (gndict.ContainsKey(parent))
gndict[parent].features.Add(gnid);

public static void makeworldmap()
Bitmap map = new Bitmap(3600, 1800);

int n = 0;

string filename = geonamesfolder;

filename += "allCountries.txt";

using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);
double lat = tryconvertdouble(words[4]);
double lon = tryconvertdouble(words[5]);

double scale = 0.5 + 0.5 * Math.Cos(lat * 3.1416 / 180); //latitude-dependent longitude scale

int x = Convert.ToInt32((lon * scale + 180) * 10);
int y = 1800 - Convert.ToInt32((lat + 90) * 10);

if ((x >= 0) && (x < 3600) && (y >= 0) && (y < 1800))

Color oldcol = map.GetPixel(x, y);
Color newcol = Color.White;
if (oldcol.GetBrightness() < 1.0)
int nargb = oldcol.ToArgb() +0x00030303;
newcol = Color.FromArgb(nargb);
map.SetPixel(x, y, newcol);
else
Console.WriteLine("lat,lon,x,y = " + lat.ToString() + " " + lon.ToString() + " " + x.ToString() + " " + y.ToString());
n++;
if ((n % 10000) == 0)
Console.WriteLine("n (world map) = " + n.ToString());
//if (n >= 500000)
// break;

map.Save(geonamesfolder + "worldmap.jpg", ImageFormat.Jpeg);
map.Dispose();

public static void read_languageiso()
//public class langclass
//{
// public string iso3 = "";
// public string iso2 = "";
// public Dictionary name = new Dictionary(); //name of language in different language. Iso -> name.
//}

//public static Dictionary langdict = new Dictionary(); //main language table
//public static Dictionary langtoint = new Dictionary(); //from iso to integer code. Both iso2 and iso3 used as keys to the same int
int n = 0;

using (StreamReader sr = new StreamReader(geonamesfolder + "language-iso.txt"))
//int makelangcol = -1;

String headline = sr.ReadLine();
string[] heads = headline.Split(tabchar);
Dictionary ld = new Dictionary();
for (int i = 0; i < heads.Length; i++)
if ((heads[i].Length == 2) || (heads[i].Length == 3))
ld.Add(i, heads[i]);

while (!sr.EndOfStream)
String line = sr.ReadLine();

if (line[0] == '#')
continue;

//if (n > 250)
//Console.WriteLine(line);

string[] words = line.Split(tabchar);

if (words.Length < 3)
continue;

n++;

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

langclass lc = new langclass();
lc.iso3 = words[0].Trim();
lc.iso2 = words[1].Trim();

for (int i = 2; i < words.Length; i++)
if ( !String.IsNullOrEmpty(words[i].Trim()))
if ( ld.ContainsKey(i))
if (!lc.name.ContainsKey(ld[i]))
lc.name.Add(ld[i],words[i].Trim());

if (!String.IsNullOrEmpty(lc.iso3))
if ( !langtoint.ContainsKey(lc.iso3))
langtoint.Add(lc.iso3, n);
if (!String.IsNullOrEmpty(lc.iso2))
if (!langtoint.ContainsKey(lc.iso2))
langtoint.Add(lc.iso2, n);
langdict.Add(n, lc);

if ((n % 100) == 0)
Console.WriteLine("n (language-iso) = " + n.ToString());

Console.WriteLine("n (language-iso)= " + n.ToString());

using (StreamReader sr = new StreamReader(geonamesfolder + "langnames-" + makelang + ".txt"))
n = 0;
while (!sr.EndOfStream)
String line = sr.ReadLine();

string[] words = line.Split(tabchar);

if (words.Length < 2)
continue;

string iso = words[0];
string langname = words[1];
if (makelang == "sv")
langname = langname.ToLower();
if (langtoint.ContainsKey(iso))
int langcode = langtoint[iso];
if ((langdict.ContainsKey(langcode)) && (!langdict[langcode].name.ContainsKey(makelang)))
langdict[langcode].name.Add(makelang, langname);

n++;
if ((n % 100) == 0)
Console.WriteLine("n (langname-makelang) = " + n.ToString());

Console.WriteLine("n (langname-makelang)= " + n.ToString());

if (savewikilinks)
Page pt = new Page(makesite, mp(13, null) + botname + "/languagelinks");
pt.text = "Language links used by Lsjbot\n\n";
foreach (int ilang in langdict.Keys)
if ( langdict[ilang].name.ContainsKey(makelang))
pt.text += "* " + langdict[ilang].iso3 + " [[" + langdict[ilang].name[makelang] + "]]\n";
else
pt.text += "* " + langdict[ilang].iso3+"\n";
trysave(pt, 1);

public static string addref(string rn, string rstring)
if (String.IsNullOrEmpty(rn) || String.IsNullOrEmpty(rstring))
return "";

string refname = "\"" + rn + "\"";
if (!refnamelist.Contains(refname))
refnamelist.Add(refname);

string refref = "" + rstring + "";
reflist += "\n" + refref;
string shortref = "";
return shortref;

public static string addnote(string notestring)
//if (makelang != "sv")
// return "";
hasnotes = true;
Console.WriteLine("addnote:" + notestring);
return mp(174,null) + notestring + "}}";

public static void get_lang_iw(string langcode)
Console.WriteLine("get lang iw " + langcode);

using (StreamWriter sw = new StreamWriter("langnames-" + langcode + ".csv"))

foreach (int gnid in langdict.Keys)

string langname = langdict[gnid].name["en"];
string[] names = langname.Split(';');
foreach (string ln in names)
Page ep = new Page(ensite, ln);
tryload(ep, 2);
if (!ep.Exists())
continue;
if ( ep.IsRedirect())
ep.title = ep.RedirectsTo();
tryload(ep, 2);
if (!ep.Exists())
continue;
langname = ln;

List iwlist = ep.GetInterLanguageLinks();
foreach (string iws in iwlist)
string[] ss = iws.Split(':');
string iwcode = ss[0];
string iwtitle = ss[1];
//Console.WriteLine("iw - " + iwcode + ":" + iwtitle);
if (iwcode == langcode)
langname = iwtitle;
sw.WriteLine(langdict[gnid].iso3 + ";" + langname);
Console.WriteLine(ln + ";" + langname);

public static string cleanup_text(string text)
Dictionary cleanstring = new Dictionary();
cleanstring.Add(". ", ". ");
cleanstring.Add(", ", ", ");
cleanstring.Add("\n ", "\n");

string rs = text;
foreach (string cs in cleanstring.Keys)
while (rs.Contains(cs))
rs = rs.Replace(cs, cleanstring[cs]);
return rs;

public static string fill_geobox(int gnid)
string fc = gndict[gnid].featurecode;
int icountry = gndict[gnid].adm[0];
List allnames = new List();

string boxtype = "alla";
if (geoboxdict.ContainsKey(fc))
boxtype = geoboxdict[fc];
if (!geoboxtemplates.ContainsKey(boxtype))
Console.WriteLine("XXXXXXXXX Bad box type: " + boxtype);
return "";

//creates dummy page, in order to use DNWB tools for template handling
Page dummy = new Page(makesite, "dummy");
dummy.text = geoboxtemplates[boxtype];

//Console.WriteLine("Före:"+dummy.text.Substring(0,30));

dummy.SetTemplateParameter("geobox","name",gndict[gnid].Name_ml,true);
//Console.WriteLine("1:" + dummy.text.Substring(0, 30));
allnames.Add(gndict[gnid].Name_ml);

int othernames = 0;
if ( gndict[gnid].Name != gndict[gnid].Name_ml)
dummy.SetTemplateParameter("geobox","other_name",gndict[gnid].Name,true);
allnames.Add(gndict[gnid].Name);
othernames++;

if ( altdict.ContainsKey(gnid))
int nativenames = 0;
foreach (altnameclass ac in altdict[gnid])
if (!allnames.Contains(ac.altname))
if (countrydict[icountry].languages.Contains(ac.ilang))
nativenames++;
if (ac.official)
dummy.SetTemplateParameter("geobox", "official_name", ac.altname, true);
allnames.Add(ac.altname);
if (ac.colloquial)
dummy.SetTemplateParameter("geobox", "nickname", ac.altname, true);
allnames.Add(ac.altname);

if ( nativenames > 0)
//bool nativeset = false;
foreach (altnameclass ac in altdict[gnid])
if (!allnames.Contains(ac.altname))

if (countrydict[icountry].languages.Contains(ac.ilang))
dummy.SetTemplateParameter("geobox", "native_name", ac.altname, true);
allnames.Add(ac.altname);
break; //set only once
foreach (altnameclass ac in altdict[gnid])

if (!allnames.Contains(ac.altname))

//if ( !countrydict[icountry].languages.Contains(ac.ilang))
string order = "";
if (othernames > 0)
order = othernames.ToString();
if ((!String.IsNullOrEmpty(ac.altname)) && (!allnames.Contains(ac.altname)))
dummy.SetTemplateParameter("geobox", "other_name" + order, ac.altname, true);
allnames.Add(ac.altname);
othernames++;
else
foreach (altnameclass ac in altdict[gnid])
if (!allnames.Contains(ac.altname))

string order = "";
if (othernames > 0)
order = othernames.ToString();
if (!allnames.Contains(ac.altname))
dummy.SetTemplateParameter("geobox", "other_name" + order, ac.altname, true);
allnames.Add(ac.altname);
othernames++;

//Console.WriteLine("2:" + dummy.text.Substring(0, 30));

string latstring = gndict[gnid].latitude.ToString(culture_en);
if ( !latstring.Contains("."))
latstring += ".0";
string lonstring = gndict[gnid].longitude.ToString(culture_en);
if ( !lonstring.Contains("."))
lonstring += ".0";
dummy.SetTemplateParameter("geobox","lat_d",latstring,true);
dummy.SetTemplateParameter("geobox","long_d",lonstring,true);

string cat = initialcap(getfeaturelabel(countrydict[icountry].iso, gndict[gnid].featurecode, gnid));
dummy.SetTemplateParameter("geobox", "category", cat, true);

string countrynameml = countrydict[icountry].Name;
if (countryml.ContainsKey(countrynameml))
countrynameml = countryml[countrynameml];

if (makelang == "sv")

dummy.SetTemplateParameter("geobox","country",countrynameml,true);
dummy.SetTemplateParameter("geobox","country_flag","true",true);
else
dummy.SetTemplateParameter("geobox","country","{{flag|"+countrynameml+"}}",true);

int nc = 0;

int mamagnid = -1;
if ( motherdict.ContainsKey(makecountry))
string mama = motherdict[makecountry];
mamagnid = countryid[mama];
nc++;
string acml = countrydict[mamagnid].Name;
if (countryml.ContainsKey(acml))
acml = countryml[acml];
if (makelang == "sv")
dummy.SetTemplateParameter("geobox", "country"+nc.ToString(), acml, true);
dummy.SetTemplateParameter("geobox", "country_flag" + nc.ToString(), "true", true);

else
dummy.SetTemplateParameter("geobox", "country" + nc.ToString(), "{{flag|" + acml + "}}", true);
dummy.SetTemplateParameter("geobox", "country_type", mp(294,null), true);

foreach (int ic in gndict[gnid].altcountry)
nc++;
if ( ic == mamagnid )
continue;
string acml = countrydict[ic].Name;
if (countryml.ContainsKey(acml))
acml = countryml[acml];
if (makelang == "sv")
dummy.SetTemplateParameter("geobox", "country"+nc.ToString(), acml, true);
dummy.SetTemplateParameter("geobox", "country_flag" + nc.ToString(), "true", true);
else
dummy.SetTemplateParameter("geobox", "country" + nc.ToString(), "{{flag|" + acml + "}}", true);

if (!string.IsNullOrEmpty(getgnidname(gndict[gnid].adm[1])))
dummy.SetTemplateParameter("geobox", "state", makegnidlink(gndict[gnid].adm[1]), true);
dummy.SetTemplateParameter("geobox", "state_type", initialcap(removearticle(getadmlabel(makecountry, 1, gndict[gnid].adm[1]))), true);
if (!string.IsNullOrEmpty(getgnidname(gndict[gnid].adm[2])))
dummy.SetTemplateParameter("geobox", "region", makegnidlink(gndict[gnid].adm[2]), true);
dummy.SetTemplateParameter("geobox", "region_type", initialcap(getadmlabel(makecountry, 2,gndict[gnid].adm[2])), true);
if (!string.IsNullOrEmpty(getgnidname(gndict[gnid].adm[3])))
dummy.SetTemplateParameter("geobox", "district", makegnidlink(gndict[gnid].adm[3]), true);
dummy.SetTemplateParameter("geobox", "district_type", initialcap(getadmlabel(makecountry, 3, gndict[gnid].adm[3])), true);
if (!string.IsNullOrEmpty(getgnidname(gndict[gnid].adm[4])))
dummy.SetTemplateParameter("geobox", "municipality", makegnidlink(gndict[gnid].adm[4]), true);
dummy.SetTemplateParameter("geobox", "municipality_type", initialcap(getadmlabel(makecountry, 4, gndict[gnid].adm[4])), true);

int elev = gndict[gnid].elevation;
if ( elev < 0 )
elev = gndict[gnid].dem;
string category = "";
if (categorydict.ContainsKey(fc))
category = categorydict[fc];
if ((category == "oceans") || (category == "seabed") || (category == "reefs") || (category == "navigation"))
elev = -9999;

if (elev > 0)
dummy.SetTemplateParameter("geobox", "elevation", elev.ToString("N0", nfi_en), true);
if (is_height(gndict[gnid].featurecode))
double width = 0;
int prom = get_prominence(gnid, out width);

if ( prom <= 0 )
//int nearhigh = -1;
int altitude = -1;
double nbradius = 3.0;
List farlist = getneighbors(gnid, nbradius);
bool otherpeak = false;
Console.WriteLine("farlist = " + farlist.Count.ToString());
foreach (int nbgnid in farlist)
if (nbgnid != gnid)
if (is_height(gndict[nbgnid].featurecode) && (gndict[nbgnid].elevation > gndict[gnid].elevation))
otherpeak = true;
Console.WriteLine(gndict[nbgnid].Name);

if (!otherpeak)
Console.WriteLine("No other peak");
nbradius = 2.0;
double slat = 9999.9;
double slon = 9999.9;
altitude = get_summit(gnid, out slat, out slon);

Console.WriteLine("get summit " + slat.ToString() +" " + slon.ToString() + ": " + altitude.ToString());

double nhdist = get_distance_latlong(gndict[gnid].latitude, gndict[gnid].longitude, slat, slon);
Console.WriteLine("nhdist = " + nhdist.ToString());
if ((nhdist < nbradius) && ( nhdist > 0.1 ) && (altitude > elev))
gndict[gnid].latitude = slat;
gndict[gnid].longitude = slon;
gndict[gnid].elevation = altitude;
gndict[gnid].elevation_vp = altitude;
elev = altitude;

latstring = gndict[gnid].latitude.ToString("F4",culture_en);
if (!latstring.Contains("."))
latstring += ".0";
lonstring = gndict[gnid].longitude.ToString("F4",culture_en);
if (!lonstring.Contains("."))
lonstring += ".0";
dummy.SetTemplateParameter("geobox", "elevation", elev.ToString("N0", nfi_en), true);
dummy.SetTemplateParameter("geobox", "lat_d", latstring, true);
dummy.SetTemplateParameter("geobox", "long_d", lonstring, true);
dummy.SetTemplateParameter("geobox", "coordinates_note", addnote(mp(213, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null)), true);

prom = get_prominence(gnid, out width);

if (prom > minimum_prominence)
dummy.SetTemplateParameter("geobox", "height", prom.ToString("N0", nfi_en), true);
dummy.SetTemplateParameter("geobox", "width", fnum(width), true);
dummy.SetTemplateParameter("geobox", "width_unit", "km", true);
dummy.SetTemplateParameter("geobox", "highest_elevation", elev.ToString("N0", nfi_en), true);
dummy.SetTemplateParameter("geobox", "elevation", (elev - prom).ToString("N0", nfi_en), true);
gndict[gnid].prominence = prom;
gndict[gnid].width = width;
if (gndict.ContainsKey(gndict[gnid].inrange))
dummy.SetTemplateParameter("geobox", "range", makegnidlink(gndict[gnid].inrange), true);

bool haspop = false;

if (prefergeonamespop)
if (gndict[gnid].population > minimum_population)
dummy.SetTemplateParameter("geobox", "population", gndict[gnid].population.ToString(), true);
haspop = true;
if (gndict[gnid].population == gndict[gnid].population_wd)
dummy.SetTemplateParameter("geobox", "population_note", "" + mp(131, null) + " " + gndict[gnid].population_wd_iw + "wiki", true);
else
dummy.SetTemplateParameter("geobox", "population_note", geonameref(gnid), true);
dummy.SetTemplateParameter("geobox", "population_date", gndict[gnid].moddate, true);

else if ((wdid > 0) && (!String.IsNullOrEmpty(get_wd_prop(propdict["population"], currentxml))))
dummy.SetTemplateParameter("geobox", "population", wdlink("population"), true);
haspop = true;
dummy.SetTemplateParameter("geobox", "population_note", "" + mp(131, null) + " Wikidata", true);
long wdpop = tryconvert(get_wd_prop(propdict["population"], currentxml));
if (wdpop > 0)
gndict[gnid].population_wd = wdpop;
if ((gndict[gnid].population < minimum_population) || (!prefergeonamespop))
gndict[gnid].population = wdpop;
gndict[gnid].population_wd_iw = "Wikidata";
else if (gndict[gnid].population_wd > minimum_population)
dummy.SetTemplateParameter("geobox", "population", gndict[gnid].population_wd.ToString(), true);
haspop = true;
//dummy.SetTemplateParameter("geobox", "population_date", gndict[gnid].moddate, true);
dummy.SetTemplateParameter("geobox", "population_note", "" + mp(131, null) + " " + gndict[gnid].population_wd_iw + "wiki", true);
else
if ((wdid > 0) && (!String.IsNullOrEmpty(get_wd_prop(propdict["population"], currentxml))))
dummy.SetTemplateParameter("geobox", "population", wdlink("population"), true);
haspop = true;
dummy.SetTemplateParameter("geobox", "population_note", "" + mp(131, null) + " Wikidata", true);
long wdpop = tryconvert(get_wd_prop(propdict["population"], currentxml));
if (wdpop > 0)
gndict[gnid].population_wd = wdpop;
if ((gndict[gnid].population < minimum_population) || (!prefergeonamespop))
gndict[gnid].population = wdpop;
gndict[gnid].population_wd_iw = "Wikidata";
else if (gndict[gnid].population_wd > minimum_population)
dummy.SetTemplateParameter("geobox", "population", gndict[gnid].population_wd.ToString(), true);
haspop = true;
//dummy.SetTemplateParameter("geobox", "population_date", gndict[gnid].moddate, true);
dummy.SetTemplateParameter("geobox", "population_note", "" + mp(131, null) + " " + gndict[gnid].population_wd_iw + "wiki", true);
else if (gndict[gnid].population > minimum_population)
dummy.SetTemplateParameter("geobox", "population", gndict[gnid].population.ToString(), true);
haspop = true;
dummy.SetTemplateParameter("geobox", "population_date", gndict[gnid].moddate, true);
dummy.SetTemplateParameter("geobox", "population_note", geonameref(gnid), true);

if (gndict[gnid].area > minimum_area)
dummy.SetTemplateParameter("geobox", "area", gndict[gnid].area.ToString("N2", nfi_en), true);
if ( haspop )
dummy.SetTemplateParameter("geobox", "population_density", "auto", true);

if ( tzdict.ContainsKey(gndict[gnid].tz))
dummy.SetTemplateParameter("geobox", "timezone_label", gndict[gnid].tz, true);
dummy.SetTemplateParameter("geobox", "utc_offset", tzdict[gndict[gnid].tz].offset, true);
if ( tzdict[gndict[gnid].tz].summeroffset != tzdict[gndict[gnid].tz].offset )
dummy.SetTemplateParameter("geobox", "utc_offset_DST", tzdict[gndict[gnid].tz].summeroffset, true);
if (!String.IsNullOrEmpty(tzdict[gndict[gnid].tz].tzname))
dummy.SetTemplateParameter("geobox", "timezone", "[[" + tzdict[gndict[gnid].tz].tzfull+"|"+tzdict[gndict[gnid].tz].tzname+"]]", true);
if (tzdict[gndict[gnid].tz].summeroffset != tzdict[gndict[gnid].tz].offset)
dummy.SetTemplateParameter("geobox", "timezone_DST", "[[" + tzdict[gndict[gnid].tz].tzfullsummer + "|" + tzdict[gndict[gnid].tz].tzsummer + "]]", true);

if (wdid > 0) //get various stuff from Wikidata:
Console.WriteLine("Filling geobox from wikidata");
if (!String.IsNullOrEmpty(get_wd_prop(propdict["coat of arms"], currentxml)))
string imagename = get_wd_prop(propdict["coat of arms"], currentxml);
if ( exists_at_commons(imagename))
dummy.SetTemplateParameter("geobox", "symbol", imagename, true);
if (!String.IsNullOrEmpty(get_wd_prop(propdict["flag"], currentxml)))
dummy.SetTemplateParameter("geobox", "flag", get_wd_prop(propdict["flag"], currentxml), true);
//if (!String.IsNullOrEmpty(get_wd_prop(propdict("capital", currentxml))))
// dummy.SetTemplateParameter("geobox", "capital", wdlink("capital"));
if (!String.IsNullOrEmpty(get_wd_prop(propdict["locatormap"], currentxml)))
dummy.SetTemplateParameter("geobox", "map2", get_wd_prop(propdict["locatormap"], currentxml), true);
if (!String.IsNullOrEmpty(get_wd_prop(propdict["iso"], currentxml)))
dummy.SetTemplateParameter("geobox", "iso_code", wdlink("iso"), true);
//if (!String.IsNullOrEmpty(get_wd_prop(propdict("head of government", currentxml))))
// dummy.SetTemplateParameter("geobox", "leader", wdlink("head of government"));
if (!String.IsNullOrEmpty(get_wd_prop(propdict["postal code"], currentxml)))
dummy.SetTemplateParameter("geobox", "postal_code", wdlink("postal code"), true);
if (!String.IsNullOrEmpty(get_wd_prop(propdict["image"], currentxml)))
string imagename = get_wd_prop(propdict["image"], currentxml);
if (exists_at_commons(imagename))
dummy.SetTemplateParameter("geobox", "image", imagename, true);
else if (!String.IsNullOrEmpty(get_wd_prop(propdict["banner"], currentxml)))
string imagename = get_wd_prop(propdict["banner"], currentxml);
if (exists_at_commons(imagename))
dummy.SetTemplateParameter("geobox", "image", imagename, true);
foreach (int ic in get_wd_prop_idlist(propdict["capital"], currentxml))
dummy.SetTemplateParameter("geobox", "capital", get_wd_name(ic), true);
foreach (int ic in get_wd_prop_idlist(propdict["head of government"], currentxml))
dummy.SetTemplateParameter("geobox", "leader", get_wd_name(ic), true);

if (String.IsNullOrEmpty(get_wd_prop(propdict["gnid"], currentxml))) //if gnid NOT in wikidata, set it manually
dummy.SetTemplateParameter("geobox", "geonames", gnid.ToString(), true);

else //wdid missing, set geonames ID in template
dummy.SetTemplateParameter("geobox", "geonames", gnid.ToString(), true);

if (gndict[gnid].inrange > 0)
dummy.SetTemplateParameter("geobox", "range", makegnidlink(gndict[gnid].inrange), true);
else if (rangedict.ContainsKey(gnid))
//public class rangeclass //data for each MTS/HLLS
//{
// public double length = 0;
// public string orientation = "....";
// public double angle = 0; //polar angle of long axis (radians). 0 or pi = EW, pi/2 or 3pi/2 = NS etc.
// public double kmew = 0;
// public double kmns = 0;
// public int maxheight = 0; //highest point; gnid of peak if negative, height if positive
// public double hlat = 999; //latitude/longitude of highest point
// public double hlon = 999;
// public List inrange = new List(); //list of GeoNames id of mountains in the range.
//}

dummy.SetTemplateParameter("geobox", "length", rangedict[gnid].length.ToString("N0", nfi_en), true);
dummy.SetTemplateParameter("geobox", "length_orientation", get_nsew(rangedict[gnid].angle), true);
int hmax = rangedict[gnid].maxheight;
int hgnid = -1;
if (hmax < 0)
if (gndict.ContainsKey(-hmax))
hgnid = -hmax;
hmax = gndict[hgnid].elevation;
if (hmax > 0)
string hlatstring = rangedict[gnid].hlat.ToString(culture_en);
if (!hlatstring.Contains("."))
hlatstring += ".0";
string hlonstring = rangedict[gnid].hlon.ToString(culture_en);
if (!hlonstring.Contains("."))
hlonstring += ".0";
if ( hgnid > 0 )
dummy.SetTemplateParameter("geobox", "highest_location", makegnidlink(hgnid), true);
dummy.SetTemplateParameter("geobox", "highest_elevation", hmax.ToString("N0", nfi_en), true);
dummy.SetTemplateParameter("geobox", "highest_lat_d", hlatstring, true);
dummy.SetTemplateParameter("geobox", "highest_long_d", hlonstring, true);

//Airport codes:
if (makelang == "sv")
if (iatadict.ContainsKey(gnid))
dummy.SetTemplateParameter("geobox", "IATA-kod", iatadict[gnid], true);
if (icaodict.ContainsKey(gnid))
dummy.SetTemplateParameter("geobox", "ICAO-kod", icaodict[gnid], true);
else
if (iatadict.ContainsKey(gnid))
dummy.SetTemplateParameter("geobox", "free", iatadict[gnid], true);
dummy.SetTemplateParameter("geobox", "free_type", "[[IATA]]", true);
if (icaodict.ContainsKey(gnid))
dummy.SetTemplateParameter("geobox", "free1", icaodict[gnid], true);
dummy.SetTemplateParameter("geobox", "free1_type", "[[ICAO]]", true);

//Console.WriteLine("3:" + dummy.text.Substring(0, 30));

if (locatoringeobox) //only works in Swedish!
string countryname = countrydict[icountry].Name;

//if (String.IsNullOrEmpty(locatordict[countryname].locatorimage))
//{
//string templatename = mp(63,null)+mp(72, null).Replace("{{", "") + " " + locatordict[countryname];
//Console.WriteLine(templatename);
//string imagename = "";
//Page ltp = new Page(makesite, templatename);
//tryload(ltp, 2);
//if (ltp.Exists())
//{
// imagename = get_pictureparam(ltp);
//}

//if (!String.IsNullOrEmpty(imagename))
//{
// locatordict[countryname].locatorimage = imagename;
//}
//}

if (locatordict.ContainsKey(countryname))
string templatestart = mp(72, null) + " " + locatordict[countryname].get_locator(gndict[gnid].latitude, gndict[gnid].longitude);
string imagename = "{{xxx|reliefkarta_om_den_finns}}".Replace("{{xxx", templatestart).Replace("bild", mp(171, null));
//string imagename = "{{#if:xxx|bild1}}|xxx|bild1}}|xxx|bild}}}}".Replace("xxx", templatestart).Replace("bild", mp(171, null));
string[] p143 = new string[1] { countrynameml };
dummy.SetTemplateParameter("geobox", "map", imagename, true);
dummy.SetTemplateParameter("geobox", "map_locator", locatordict[countryname].get_locator(gndict[gnid].latitude, gndict[gnid].longitude), true);
dummy.SetTemplateParameter("geobox", "map_caption", mp(143,p143), true);

//Console.WriteLine("Efter:" + dummy.text.Substring(0, 30));

return dummy.text;

public static string get_pictureparam(Page ltp)
string imagename = "";
string[] param = ltp.text.Split('|');
foreach (string par in param)
if (((par.Trim().ToLower().IndexOf("bild") == 0) || (par.Trim().ToLower().IndexOf("image") == 0)) && (par.Contains("=")))
imagename = par.Split('=')[1].Trim();
if (imagename.Contains("}}"))
imagename = imagename.Remove(imagename.IndexOf("}}")).Trim();
//Console.WriteLine("imagename = " + imagename);
break;
return imagename;

public static double get_edgeparam(Page ltp,string edgepar)
string imagename = "";
string[] param = ltp.text.Split('|');
foreach (string par in param)
if ((par.Trim().ToLower().IndexOf(edgepar) == 0) && (par.Contains("=")))
imagename = par.Split('=')[1].Trim();
if (imagename.Contains("}}"))
imagename = imagename.Remove(imagename.IndexOf("}}")).Trim();
//Console.WriteLine("imagename = " + imagename);
break;
return tryconvertdouble(imagename);

public static bool exists_at_commons(string imagename)
string res = cmsite.indexPath + "?title=" +
HttpUtility.UrlEncode("File:" + imagename);
//Console.WriteLine("commonsres = " + res);
string src = "";
try
src = cmsite.GetWebPage(res); // cmsite.GetPageHTM(res);
Console.WriteLine("Found at Commons: " + imagename);
return true;
catch (WebException e)
//newpix[newpic] = "/// NOT FOUND ON COMMONS";
string message = e.Message;
if (message.Contains(": (404) "))
{ // Not Found
Console.Error.WriteLine(Bot.Msg("Page \"{0}\" doesn't exist."), imagename);
Console.WriteLine("Image not found: " + imagename);
//continue;
else
Console.Error.WriteLine(message);
//continue;
return false;

public static void fix_positionmaps()
//https://tools.wmflabs.org/magnus-toolserver/commonsapi.php?image=Bhutan_location_map.svg

Dictionary replacedict1 = new Dictionary();
Dictionary replacedict2 = new Dictionary();
replacedict1.Add("|topp", "| topp");
replacedict1.Add(" topp=", " topp =");
replacedict1.Add("|botten", "| botten");
replacedict1.Add(" botten=", " botten =");
replacedict1.Add("|vänster", "| vänster");
replacedict1.Add(" vänster=", " vänster =");
replacedict1.Add("|höger", "| höger");
replacedict1.Add(" höger=", " höger =");
replacedict1.Add("|bild", "| bild");
replacedict1.Add(" bild=", " bild =");

replacedict2.Add("| topp ", "| topp|top ");
replacedict2.Add("| botten ", "| botten|bottom ");
replacedict2.Add("| vänster ", "| vänster|left ");
replacedict2.Add("| höger ", "| höger|right ");

foreach (string countryname in locatordict.Keys)
Console.WriteLine("countryname = " + countryname);
string templatename = mp(63, null) + mp(72, null).Replace("{{", "") + " " + locatordict[countryname].locatorname;
Console.WriteLine(templatename);
string imagename = "";
Page ltp = new Page(makesite, templatename);
tryload(ltp, 2);
if (ltp.Exists())
if (ltp.text.Contains("topp|top"))
continue;
imagename = get_pictureparam(ltp);
if (!String.IsNullOrEmpty(imagename))

foreach (KeyValuePair replacepair in replacedict1)
ltp.text = ltp.text.Replace(replacepair.Key, replacepair.Value);
foreach (KeyValuePair replacepair in replacedict2)
ltp.text = ltp.text.Replace(replacepair.Key, replacepair.Value);

XmlDocument xd = new XmlDocument();
string cmurl = "https://tools.wmflabs.org/magnus-toolserver/commonsapi.php?image=" + imagename;
string s = get_webpage(cmurl);
if (!String.IsNullOrEmpty(s))
//Console.WriteLine(s);
xd.LoadXml(s);
XmlNodeList elemlist1 = xd.GetElementsByTagName("width");
double width = -1;
foreach (XmlNode ee in elemlist1)
width = tryconvertdouble(ee.InnerXml);
XmlNodeList elemlist2 = xd.GetElementsByTagName("height");
double height = -1;
foreach (XmlNode ee in elemlist2)
height = tryconvertdouble(ee.InnerXml);
Console.WriteLine("w,h = " + width.ToString() + ", " + height.ToString());
double ratio = height / width;
if ( !ltp.text.Contains("ratio"))
ltp.text = ltp.text.Replace("| bild ", "| ratio = " + ratio.ToString(culture_en) + "\n| bild ");

trysave(ltp, 2);
string redirectname = mp(63, null) + "Geobox locator " + locatordict[countryname].locatorname;
make_redirect(redirectname, templatename, "Geobox locator|" + locatordict[countryname].locatorname,-1);

//Mall:Geobox locator Andorra
//#OMDIRIGERING[[Mall:Kartposition Andorra]]
//[[Kategori:Geobox locator|Andorra]]

//Console.WriteLine("");
//Console.ReadLine();

Console.WriteLine("Done!");
Console.ReadLine();

public static void get_page_area_pop_height(Page p, out double areaout, out long popout, out int heightout)
areaout = -1;
popout = -1;
heightout = -1;

List popparams = new List();
List urbanparams = new List();
List areaparams = new List();
List heightparams = new List();

popparams.Add("population");
popparams.Add("population_total");
popparams.Add("population_urban");
popparams.Add("population_metro");
popparams.Add("befolkning");

urbanparams.Add("population_urban");
urbanparams.Add("population_metro");
urbanparams.Add("población_urb");

areaparams.Add("area_total_km2");
areaparams.Add("area");
areaparams.Add("fläche");
areaparams.Add("superficie");
areaparams.Add("yta");

heightparams.Add("highest_elevation");
heightparams.Add("elevation");

bool foundpop = false;
bool foundurban = false;
bool foundarea = false;
//bool foundheight = false;
bool foundhighest = false;
bool preferurban = true;

long popwdurban = -1;

foreach (string ttt in p.GetTemplates(true, true))
//Console.WriteLine(ttt);
Dictionary pdict = Page.ParseTemplate(ttt);
foreach (string param in pdict.Keys)
if (popparams.Contains(param) && !foundpop)
long popwd = tryconvertlong(pdict[param]);
if (popwd <= 0)
popwd = tryconvertlong(pdict[param].Split()[0]);
if (popwd > 0)
foundpop = true;
popout = popwd;
if (urbanparams.Contains(param) && !foundurban)
long popwd = tryconvertlong(pdict[param]);
if (popwd > 0)
foundurban = true;
popwdurban = popwd;
if (areaparams.Contains(param) && !foundarea)
double areawd = tryconvertdouble(pdict[param]);
if (areawd <= 0)
areawd = tryconvertdouble(pdict[param].Split()[0]);
Console.WriteLine("areaparam = " + pdict[param]);
Console.WriteLine("areawd = " + areawd.ToString());
if (areawd > 0)
foundarea = true;
areaout = areawd;

if (heightparams.Contains(param) && !foundhighest)
int heightwd = tryconvert(pdict[param]);
if (heightwd <= 0)
heightwd = tryconvert(pdict[param].Split()[0]);
Console.WriteLine("heightparam = " + param);
Console.WriteLine("heightwd = " + heightwd.ToString());
if (heightwd > 0)
//foundheight = true;
heightout = heightwd;
if (param.Contains("highest"))
foundhighest = true;

if (preferurban && foundurban)
popout = popwdurban;

public static void get_wd_area_pop(int wdid, XmlDocument cx, out double areawdout, out long popwdout, out string iwsout, bool preferurban)
areawdout = -1.0;
popwdout = -1;
iwsout = "";
long popwdurban = -1;
string iwsurban = "";

List popparams = new List();
List urbanparams = new List();
List areaparams = new List();
List paramlangs = new List();

popparams.Add("population");
popparams.Add("population_total");
popparams.Add("population_urban");
popparams.Add("population_metro");
popparams.Add("poblacion");
popparams.Add("población");
popparams.Add("población_urb");
popparams.Add("einwohner");

urbanparams.Add("population_urban");
urbanparams.Add("population_metro");
urbanparams.Add("población_urb");

areaparams.Add("area_total_km2");
areaparams.Add("area");
areaparams.Add("fläche");
areaparams.Add("superficie");

paramlangs.Add("en");
paramlangs.Add("de");
paramlangs.Add("fr");
paramlangs.Add("es");
if (!paramlangs.Contains(countrydict[countryid[makecountry]].nativewiki) && !String.IsNullOrEmpty(countrydict[countryid[makecountry]].nativewiki))
paramlangs.Add(countrydict[countryid[makecountry]].nativewiki);

string badlang = "";
foreach (string iwl in paramlangs)
if ((!iwsites.ContainsKey(iwl)) && (iwl != makelang))
Console.WriteLine(iwl);
try
Site ssite = new Site("https://" + iwl + ".wikipedia.org", botname, password);
iwsites.Add(iwl, ssite);
catch (WebException e)
string message = e.Message;
Console.Error.WriteLine(message);
badlang = iwl;
catch (WikiBotException e)
string message = e.Message;
Console.Error.WriteLine(message);
badlang = iwl;
if (!String.IsNullOrEmpty(badlang))
paramlangs.Remove(badlang);

if (cx == null)
cx = get_wd_xml(wdid);
if (cx == null)
Console.WriteLine("cx = null");
return;
Dictionary iwdict = get_wd_sitelinks(cx);

bool foundpop = false;
bool foundurban = false;
bool foundarea = false;

foreach (string iws in iwdict.Keys)
string iwss = iws.Replace("wiki", "");
Console.WriteLine(iwss + ":" + iwdict[iws]);
if ((paramlangs.Contains(iwss))&&(iwsites.ContainsKey(iwss)))
Console.WriteLine(iws + ":" + iwdict[iws] + " Paramlang!");
Page iwpage = new Page(iwsites[iwss], iwdict[iws]);
if (tryload(iwpage, 1))
if (iwpage.Exists())
foreach (string ttt in iwpage.GetTemplates(true, true))
//Console.WriteLine(ttt);
Dictionary pdict = Page.ParseTemplate(ttt);// iwsites[iwss].ParseTemplate(ttt);
foreach (string param in pdict.Keys)
if (popparams.Contains(param) && !foundpop)
long popwd = tryconvertlong(pdict[param]);
if (popwd > 0)
foundpop = true;
popwdout = popwd;
iwsout = iwss;
if (urbanparams.Contains(param) && !foundurban)
long popwd = tryconvertlong(pdict[param]);
if (popwd > 0)
foundurban = true;
popwdurban = popwd;
iwsurban = iwss;
if (areaparams.Contains(param) && !foundarea)
double areawd = tryconvertdouble(pdict[param]);
if (areawd <= 0)
areawd = tryconvertdouble(pdict[param].Split()[0]);
Console.WriteLine("areaparam = " + pdict[param]);
Console.WriteLine("areawd = " + areawd.ToString());
if (areawd > 0)
foundarea = true;
areawdout = areawd;

if (foundarea && foundpop && (foundurban || !preferurban))
break;

if (preferurban && foundurban)
popwdout = popwdurban;
iwsout = iwsurban;

public static void check_wikidata()
int nwd = 0;
int npop = 0;
int narea = 0;

using (StreamWriter sw = new StreamWriter("wikidata-" + makecountry + ".txt"))

int ngnid = gndict.Count;

foreach (int gnid in gndict.Keys)
Console.WriteLine("====="+makecountry+"======== "+ngnid.ToString()+" remaining. ===========");
ngnid--;
if ((ngnid % 1000) == 0)
Console.WriteLine("Garbage collection:");
GC.Collect();

//wdid = get_wd_item(gnid);
if (gndict[gnid].wdid <= 0)
continue;
else
wdid = gndict[gnid].wdid;

Console.WriteLine(gndict[gnid].Name + ": " + wdid.ToString());
if (wdid > 0)
nwd++;

double areawd = -1.0;
long popwd = -1;
string iwss = "";

bool preferurban = (gndict[gnid].featureclass == 'P');
get_wd_area_pop(wdid, null, out areawd, out popwd, out iwss, preferurban);

if (popwd > 0)
Console.WriteLine("popwd = " + popwd.ToString());
gndict[gnid].population_wd = popwd;
gndict[gnid].population_wd_iw = iwss;
npop++;

if (areawd > 0)
gndict[gnid].area = areawd;
narea++;

//Console.WriteLine("");
//Console.ReadLine();
sw.WriteLine(gnid.ToString() + tabstring + wdid.ToString() + tabstring + gndict[gnid].area.ToString() + tabstring + gndict[gnid].population_wd.ToString() + tabstring + gndict[gnid].population_wd_iw);
Console.WriteLine("nwd = " + nwd.ToString());
Console.WriteLine("npop = " + npop.ToString());
Console.WriteLine("narea = " + narea.ToString());
Console.WriteLine("gndict = " + gndict.Count.ToString());
nwdhist.PrintSHist();

public static void read_good_wd_file()
Console.WriteLine("read_good_wd_file");
int nwdtot = 0;
if (!File.Exists("wikidata-good.nt"))
return;

using (StreamReader sr = new StreamReader("wikidata-good.nt"))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);

if (words.Length < 2)
continue;

nwdtot++;

int gnid = tryconvert(words[0]);
if (!gndict.ContainsKey(gnid))
continue;

int wdid = tryconvert(words[1]);
if (wdid <= 0)
continue;

gndict[gnid].wdid = wdid;

if (!wdgniddict.ContainsKey(wdid))
wdgniddict.Add(wdid, gnid);
else if (wdgniddict[wdid] != gnid)
{// negative numbers count how many duplicates
if (wdgniddict[wdid] > 0)
wdgniddict[wdid] = -2;
else
wdgniddict[wdid]--;
if (( nwdtot % 10000 ) == 0 )
Console.WriteLine("nwdtot = " + nwdtot.ToString());

Console.WriteLine("nwdtot = " + nwdtot.ToString());

public static Dictionary read_wd_dict(string wdcountry)
Dictionary rdict = new Dictionary();
string filename = geonamesfolder + "wikidata\\wikidata-" + wdcountry + ".txt";
string filename_override = geonamesfolder + "wikidata\\wikidata-" + wdcountry + "-override.txt";

if (!File.Exists(filename))
Console.WriteLine("No file " + filename);
return rdict;

Dictionary overridedict = new Dictionary();
if (File.Exists(filename_override)) //use to override wd assignments in case of systematic errors in main wd run
int nover = 0;
using (StreamReader sr = new StreamReader(filename_override))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
if (words.Length < 2)
continue;
int gnid0 = tryconvert(words[0]);
int gnid1 = tryconvert(words[1]);
if ((gnid0 <= 0) || (gnid1 <= 0))
continue;

if (overridedict.ContainsKey(gnid0))
continue;

overridedict.Add(gnid0, gnid1);
nover++;
Console.WriteLine("noverride = " + nover.ToString());

try
List withwd = new List();

//first pass, in order to doublecheck overridedict
if (overridedict.Count > 0)
Console.WriteLine("First pass...");
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.WriteLine(gnid.ToString() + tabstring + wdid.ToString() + tabstring + gndict[gnid].area.ToString() + tabstring + gndict[gnid].population_wd.ToString() + tabstring + gndict[gnid].population_wd_iw);

if (words.Length < 4)
continue;

int gnid = tryconvert(words[0]);
withwd.Add(gnid);
foreach (int gnid in withwd) //remove from overridedict those where both have wd match
if (overridedict.ContainsKey(gnid))
if (withwd.Contains(overridedict[gnid]))
overridedict.Remove(gnid);
Console.WriteLine("Remaining in overridedict: " + overridedict.Count.ToString());

using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.WriteLine(gnid.ToString() + tabstring + wdid.ToString() + tabstring + gndict[gnid].area.ToString() + tabstring + gndict[gnid].population_wd.ToString() + tabstring + gndict[gnid].population_wd_iw);

if (words.Length < 4)
continue;

int gnid = tryconvert(words[0]);

if (overridedict.ContainsKey(gnid))
Console.WriteLine("Overriding " + gnid.ToString() + " with " + overridedict[gnid].ToString());
gnid = overridedict[gnid];

int wdid = tryconvert(words[1]);
if (wdid <= 0)
continue;

nwdtot++;
rdict.Add(gnid, wdid);
catch (IOException e)
string message = e.Message;
Console.Error.WriteLine(message);

return rdict;

public static void read_wd_file(string wdcountry)
string filename = geonamesfolder + "wikidata\\wikidata-" + wdcountry + ".txt";
string filename_override = geonamesfolder + "wikidata\\wikidata-" + wdcountry + "-override.txt";

if (!File.Exists(filename))
Console.WriteLine("No file " + filename);
return;

Dictionary overridedict = new Dictionary();
if (File.Exists(filename_override)) //use to override wd assignments in case of systematic errors in main wd run
int nover = 0;
using (StreamReader sr = new StreamReader(filename_override))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
if (words.Length < 2)
continue;
int gnid0 = tryconvert(words[0]);
int gnid1 = tryconvert(words[1]);
if ((gnid0 <= 0) || (gnid1 <= 0))
continue;

if (overridedict.ContainsKey(gnid0))
continue;

overridedict.Add(gnid0, gnid1);
nover++;
Console.WriteLine("noverride = "+nover.ToString());

try
wdtime = File.GetCreationTime(@filename);
Console.WriteLine("wdtime = "+wdtime.ToString());
//Console.WriteLine("");
//Console.ReadLine();

List withwd = new List();

//first pass, in order to doublecheck overridedict
if (overridedict.Count > 0)
Console.WriteLine("First pass...");
using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.WriteLine(gnid.ToString() + tabstring + wdid.ToString() + tabstring + gndict[gnid].area.ToString() + tabstring + gndict[gnid].population_wd.ToString() + tabstring + gndict[gnid].population_wd_iw);

if (words.Length < 4)
continue;

int gnid = tryconvert(words[0]);
withwd.Add(gnid);
foreach (int gnid in withwd) //remove from overridedict those where both have wd match
if (overridedict.ContainsKey(gnid))
if (withwd.Contains(overridedict[gnid]))
overridedict.Remove(gnid);
Console.WriteLine("Remaining in overridedict: " + overridedict.Count.ToString());

using (StreamReader sr = new StreamReader(filename))
while (!sr.EndOfStream)
String line = sr.ReadLine();
string[] words = line.Split(tabchar);
//sw.WriteLine(gnid.ToString() + tabstring + wdid.ToString() + tabstring + gndict[gnid].area.ToString() + tabstring + gndict[gnid].population_wd.ToString() + tabstring + gndict[gnid].population_wd_iw);

if (words.Length < 4)
continue;

int gnid = tryconvert(words[0]);

if (overridedict.ContainsKey(gnid))
Console.WriteLine("Overriding " + gnid.ToString() + " with " + overridedict[gnid].ToString());
gnid = overridedict[gnid];

if (!gndict.ContainsKey(gnid))
continue;

int wdid = tryconvert(words[1]);
if (wdid <= 0)
continue;

nwdtot++;
gndict[gnid].wdid = wdid;

if (!wdgniddict.ContainsKey(wdid))
wdgniddict.Add(wdid, gnid);
else if ( wdgniddict[wdid] != gnid)
{// negative numbers count how many duplicates
if (wdgniddict[wdid] > 0)
wdgniddict[wdid] = -2;
else
wdgniddict[wdid]--;

double area = tryconvertdouble(words[2]);
if (area > 0)
//Console.WriteLine("area>0");
if (verifygeonames && (gndict[gnid].area > 0))
//Console.WriteLine("gnid-area>0");
double eps = 0;
while (areavsarea.ContainsKey(area + eps))
eps += 0.00001;
areavsarea.Add(area + eps, gndict[gnid].area + eps);
gndict[gnid].area = area;

long pop = tryconvertlong(words[3]);
if (pop > 0)
if (verifygeonames && (gndict[gnid].population > 10 ))
Console.WriteLine("pop.vs.pop:"+gndict[gnid].population.ToString() + tabstring+ pop.ToString());
int j=0;
while (popvspop.ContainsKey(pop+j))
j++;
popvspop.Add(pop+j,gndict[gnid].population+j);
if ( ((gndict[gnid].population < minimum_population) || !prefergeonamespop) && !verifygeonames )
gndict[gnid].population = pop;
gndict[gnid].population_wd = pop;
if (words.Length >= 5)
gndict[gnid].population_wd_iw = words[4];

//Console.WriteLine(gndict[gnid].Name + ": " + gndict[gnid].population_wd.ToString() + gndict[gnid].population_wd_iw);
//public static Dictionary wdgnid = new Dictionary(); //from wikidata id to geonames id; negative counts duplicates
//public static Dictionary popvspop = new Dictionary(); //comparing population for same place, wd vs gn
//public static Dictionary areavsarea = new Dictionary(); //comparing area for same place, wd vs gn

catch (IOException e)
string message = e.Message;
Console.Error.WriteLine(message);

public static void read_wd_files(string countrycode)
if (countrycode == "")
foreach (int gnid in countrydict.Keys)
read_wd_file(countrydict[gnid].iso);
else
read_wd_file(countrycode);

public static String stripNonValidXMLCharacters(string textIn)
StringBuilder textOut = new StringBuilder(); // Used to hold the output.
char current; // Used to reference the current character.

if (textIn == null || textIn == string.Empty) return string.Empty; // vacancy test.
for (int i = 0; i < textIn.Length; i++)
current = textIn[i];

if ((current == 0x9 || current == 0xA || current == 0xD) ||
((current >= 0x20) && (current <= 0xD7FF)) ||
((current >= 0xE000) && (current <= 0xFFFD)))
//|| ((current >= 0x10000) && (current <= 0x10FFFF)))
textOut.Append(current);
return textOut.ToString();

public static string get_webpage(string url)
WebClient client = new WebClient();

// Add a user agent header in case the
// requested URI contains a query.

client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");

try
Stream data = client.OpenRead(url);
StreamReader reader = new StreamReader(data);
string s = reader.ReadToEnd();
data.Close();
reader.Close();
return s;
catch (WebException e)
string message = e.Message;
Console.Error.WriteLine(message);
return "";

public static string getxmlpar(string par, XmlNode node)

foreach (XmlNode xn in node.ChildNodes)
if (xn.Name == par)
string s = xn.InnerXml;
return s;

return "";

public static List get_wd_prop_idlist(int propid, XmlDocument cx)
List rl = new List();
XmlDocument propx = new XmlDocument();
XmlNode propnode = get_property_node(propid, cx);
if (propnode == null)
return rl;

propx.AppendChild(propx.ImportNode(propnode, true));
// teamDoc.AppendChild(teamDoc.ImportNode(teamNode, true));
XmlNodeList mslist = propx.GetElementsByTagName("mainsnak");
foreach (XmlNode msn in mslist)
XmlDocument msx = new XmlDocument();
msx.AppendChild(msx.ImportNode(msn, true));
XmlNodeList elemlist = msx.GetElementsByTagName("value");
foreach (XmlNode ee in elemlist)
try
int rs = tryconvert(ee.Attributes.GetNamedItem("numeric-id").Value);
if (rs > 0)
rl.Add(rs);
catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

return rl;

public static string get_wd_prop(int propid, XmlDocument cx)
XmlNode propnode = get_property_node(propid, cx);
if (propnode == null)
return "";

//Console.WriteLine("propnode = " + propnode.ToString());

XmlDocument propx = new XmlDocument();
propx.AppendChild(propx.ImportNode(propnode,true));

XmlNodeList elemlist = propx.GetElementsByTagName("datavalue");
string rs = "";
foreach (XmlNode ee in elemlist)
try
rs = ee.Attributes.GetNamedItem("value").Value;
catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

Console.WriteLine("get_wd_prop:rs: " + rs);
return rs;

public static double[] get_wd_position(XmlDocument cx)
double[] rl = { 9999.0, 9999.0 };

XmlNode propnode = get_property_node(propdict["position"], cx);
if (propnode == null)
return rl;

//Console.WriteLine("propnode = " + propnode.ToString());

XmlDocument propx = new XmlDocument();
propx.AppendChild(propx.ImportNode(propnode, true));

XmlNodeList elemlist = propx.GetElementsByTagName("value");
//string rs = "";
foreach (XmlNode ee in elemlist)
try
rl[0] = tryconvertdouble(ee.Attributes.GetNamedItem("latitude").Value);
rl[1] = tryconvertdouble(ee.Attributes.GetNamedItem("longitude").Value);
catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

Console.WriteLine("get_wd_prop:rl: " + rl[0].ToString() + " | " + rl[1].ToString());
return rl;

public static string wdlink(string prop)
if (!propdict.ContainsKey(prop))
Console.WriteLine("Invalid property: " + prop);
return "";

string s = "{{#property:P"+propdict[prop].ToString()+"}}";
return s;

public static XmlDocument get_wd_xml(int wdid)
string url = "https://www.wikidata.org/w/api.php?action=wbgetentities&format=xml&ids=Q" + wdid.ToString() + "&redirects=yes";
XmlDocument xd = new XmlDocument();
string s = get_webpage(url);
if (String.IsNullOrEmpty(s))
return null;
//Console.WriteLine(s);
try
xd.LoadXml(s);
catch (XmlException e)
string message = e.Message;
Console.Error.WriteLine("tl we " + message);
return null;

return xd;

public static int get_wd_gnid(int wdid)
XmlDocument cx = get_wd_xml(wdid);
if (cx == null)
return -1;
else
return tryconvert(get_wd_prop(propdict["gnid"], cx));

public static XmlNode get_property_node(int propid, XmlDocument cx)
XmlNodeList elemlist = cx.GetElementsByTagName("property");
//Console.WriteLine("get_property_node: elemlist: " + elemlist.Count.ToString());
foreach (XmlNode ee in elemlist)
try
string id = ee.Attributes.GetNamedItem("id").Value;
//Console.WriteLine("id = " + id);
if (id == "P" + propid.ToString())
//Console.WriteLine("get_property_node: found!");
return ee;
catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

//Console.WriteLine("get_property_node: not found");
return null;

public static List get_wd_kids(XmlDocument cx)
List rl = new List();
List rldouble = new List();
XmlDocument ee = new XmlDocument();
XmlNode propnode = get_property_node(150, cx);
if (propnode == null)
return rl;

ee.AppendChild(ee.ImportNode(propnode, true));

//ee.DocumentElement.AppendChild(propnode);

//XmlNode ee = get_property_node(150, cx);
if ( ee == null )
return rl;

XmlNodeList elemlist = ee.GetElementsByTagName("value");
Console.WriteLine("get-wd_kids: elemlist " + elemlist.Count.ToString());

foreach (XmlNode eee in elemlist)
Console.WriteLine("eee.Attributes: " + eee.Attributes.ToString());
try
string etype = eee.Attributes.GetNamedItem("entity-type").Value;
Console.WriteLine("etype = " + etype);
if (etype == "item")
string id = eee.Attributes.GetNamedItem("numeric-id").Value;
Console.WriteLine("id = " + id);
int iid = tryconvert(id);
if (iid > 0)
if (!rl.Contains(iid))
rl.Add(iid);
else if ( !rldouble.Contains(iid))
rldouble.Add(iid);

catch (NullReferenceException e)
Console.WriteLine(e);

foreach (int ii in rldouble)
rl.Remove(ii);

return rl;

public static Dictionary get_wd_sitelinks(XmlDocument cx)
Dictionary rd = new Dictionary();

XmlNodeList elemlist = cx.GetElementsByTagName("sitelink");
foreach (XmlNode ee in elemlist)
try
string lang = ee.Attributes.GetNamedItem("site").Value;
string value = ee.Attributes.GetNamedItem("title").Value;
//Console.WriteLine("get_wd_sitelinks: lang,value : " + lang + " " + value);
if (!rd.ContainsKey(lang))
rd.Add(lang, value);
catch (NullReferenceException e)
eglob = e;

return rd;

public static string iwlinks(XmlDocument cx)
Dictionary rd = get_wd_sitelinks(cx);

string iws = "\n\n";

foreach (string sw in rd.Keys)
string s = sw.Replace("wiki", "");
if (s == makelang)
return "Exists already:"+rd[sw];
if ((s.Length == 2) || (s.Length == 3))
iws += "[[" + s + ":" + rd[sw] + "]]\n";
//Console.WriteLine("iwlinks: " + iws);

return iws;

public static Dictionary get_wd_name_dictionary(XmlDocument cx)

Dictionary rd = new Dictionary();

XmlNodeList elemlist = cx.GetElementsByTagName("label");
foreach (XmlNode ee in elemlist)
try
string lang = ee.Attributes.GetNamedItem("language").Value;
string value = ee.Attributes.GetNamedItem("value").Value;
if (!rd.ContainsKey(lang))
rd.Add(lang, value);
catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

return rd;

public static string get_wd_name_from_xml(XmlDocument cx)
return get_wd_name_from_xml(cx, makelang);

public static string get_wd_name_from_xml(XmlDocument cx, string mainlang)

Dictionary rd = new Dictionary();

if (cx == null)
return "";

XmlNodeList elemlist = cx.GetElementsByTagName("label");
Console.WriteLine("elemlist " + elemlist.Count.ToString());
foreach (XmlNode ee in elemlist)
try
string lang = ee.Attributes.GetNamedItem("language").Value;
string value = ee.Attributes.GetNamedItem("value").Value;
//Console.WriteLine("lang,value = " + lang +"|"+ value);
if (!rd.ContainsKey(lang))
if (lang == mainlang)
return value;
else
rd.Add(lang, value);
catch (NullReferenceException e)
//Console.Error.WriteLine(e.Message);
eglob = e;

//Pick the most common form:
Dictionary namestats = new Dictionary();
foreach (string lang in rd.Keys)
if (!namestats.ContainsKey(rd[lang]))
namestats.Add(rd[lang], 0);
namestats[rd[lang]]++;
string name = "";
int maxuse = 0;
foreach (string nn in namestats.Keys)
Console.WriteLine(nn);
if (namestats[nn] > maxuse)
maxuse = namestats[nn];
name = nn;
Console.WriteLine("maxuse = " + maxuse.ToString());

Console.WriteLine("get_wd_name_from_xml " + name);

return name;

public static string get_wd_name(int wdid)
string url = "https://www.wikidata.org/w/api.php?action=wbgetentities&ids=Q" + wdid.ToString() + "&props=labels&format=xml";
string xmlitem = get_webpage(url);
//Console.WriteLine(xmlitem);
if (String.IsNullOrEmpty(xmlitem))
return "";

XmlDocument cx = new XmlDocument();
cx.LoadXml(xmlitem);

return get_wd_name_from_xml(cx);

public static int get_wd_item_direct(int gnid)

//http://wdq.wmflabs.org/api?q=string[1566:"2715459"]&format=xml
string url0 = "http://wdq.wmflabs.org/api?q=string[1566:\"" + gnid.ToString() + "\"]";
string hit = get_webpage(url0);
string s = "";
if (hit.IndexOf("items\":[") > 0)
s = hit.Substring(hit.IndexOf("items\":[") + 8);
if (s.IndexOf("]") > 0)
s = s.Substring(0, s.IndexOf("]"));
//Console.WriteLine("get_wd_item; s = " + s);
string[] items0 = s.Split(',');
//Console.WriteLine("Direct gnid query");
foreach (string item in items0)
if (tryconvert(item) > 0)
nwdhist.Add("direct gnid");
if (!wdgniddict.ContainsKey(tryconvert(item)))
wdgniddict.Add(tryconvert(item), gnid);
return tryconvert(item);
return -1;

public static int get_wd_item(int gnid)
int wdid = get_wd_item_direct(gnid);
if (wdid > 0)
return wdid;

string url1 = "http://wdq.wmflabs.org/api?q=around[625," + gndict[gnid].latitude.ToString(culture_en) + "," + gndict[gnid].longitude.ToString(culture_en) + ",2]";
string around = get_webpage(url1);
string s = "";
if (around.IndexOf("items\":[") >= 0)
s = around.Substring(around.IndexOf("items\":[") + 8);
if (s.IndexOf("]") > 0)
s = s.Substring(0, s.IndexOf("]"));
string[] items = s.Split(',');

//List withgnid = new List();
//Console.WriteLine("Search by location and gnid");
//foreach (string item in items)
//{
// Console.WriteLine("item = " + item);
// string url2 = "https://www.wikidata.org/w/api.php?action=wbgetclaims&entity=Q" + item + "&format=xml&property=p1566";
// string xmlitem = get_webpage(url2);

// if (String.IsNullOrEmpty(xmlitem))
// continue;

// XmlDocument cx = new XmlDocument();
// cx.LoadXml(xmlitem);

// XmlNodeList elemlist = cx.GetElementsByTagName("datavalue");
// foreach (XmlNode ee in elemlist)
// {
// try
// {
// string value = ee.Attributes.GetNamedItem("value").Value;
// Console.WriteLine("value = " + value);
// if (tryconvert(value) == gnid)
// {
// nwdhist.Add("loc&gnid");
// if (!wdgniddict.ContainsKey(wdid))
// wdgniddict.Add(tryconvert(item), gnid);

// return tryconvert(item);
// }
// else if (tryconvert(value) > 0)
// withgnid.Add(item);
// }
// catch (NullReferenceException e)
// {
// }
// }

//}

Console.WriteLine("Search by name at location");
foreach (string item in items)
//if (withgnid.Contains(item))
// continue;

XmlDocument cx = get_wd_xml(tryconvert(item));
if (cx == null)
continue;
XmlNodeList elemlist = cx.GetElementsByTagName("label");
foreach (XmlNode ee in elemlist)
try
string value = ee.Attributes.GetNamedItem("value").Value;
//Console.WriteLine("value = " + value);
if (value == gndict[gnid].Name)
nwdhist.Add("name at loc");
if (!wdgniddict.ContainsKey(tryconvert(item)))
wdgniddict.Add(tryconvert(item), gnid);
return tryconvert(item);
catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

Console.WriteLine("Search by article name in iwlangs");

string sites = "";
foreach (string iws in iwlang)
if ( !String.IsNullOrEmpty(sites))
sites += "|";
if (iws != makelang)
sites += iws + "wiki";
else if (!String.IsNullOrEmpty(countrydict[countryid[makecountry]].nativewiki))
sites += countrydict[countryid[makecountry]].nativewiki + "wiki";

List titlist = new List();
titlist.Add(gndict[gnid].Name);
if (!titlist.Contains(gndict[gnid].Name_ml))
titlist.Add(gndict[gnid].Name_ml);
foreach (string an in gndict[gnid].altnames)
if (!titlist.Contains(an))
titlist.Add(an);
string titles = "";
foreach (string tit in titlist)
if (!String.IsNullOrEmpty(tit))
if (!String.IsNullOrEmpty(titles))
titles += "|";
titles += tit;

//https://www.wikidata.org/w/api.php?action=wbgetentities&format=xml&sites=enwiki&titles=Austurland&redirects=yes&props=labels

string url3 = "https://www.wikidata.org/w/api.php?action=wbgetentities&format=xml&sites="+sites+"&titles="+titles+"&redirects=yes&props=claims";

//Console.WriteLine(url3);

string xmlitem3 = get_webpage(url3);

if (!String.IsNullOrEmpty(xmlitem3))
XmlDocument cx = new XmlDocument();
cx.LoadXml(xmlitem3);

XmlNodeList elemlist = cx.GetElementsByTagName("entity");
foreach (XmlNode ee in elemlist)
try
int entid = tryconvert(ee.Attributes.GetNamedItem("id").Value.Replace("Q",""));
Console.WriteLine("entid = " + entid.ToString());
if (entid > 0)
XmlDocument ex = new XmlDocument();
ex.AppendChild(ex.ImportNode(ee, true));
double[] latlong = get_wd_position(ex);
if (latlong[0] + latlong[1] > 360.0)
continue;

double dist = get_distance_latlong(latlong[0], latlong[1], gndict[gnid].latitude, gndict[gnid].longitude);

Console.WriteLine("dist = " + dist.ToString());
Console.WriteLine("gnid-latlong = " + gndict[gnid].latitude.ToString() + " | " + gndict[gnid].longitude.ToString());
//Console.WriteLine("");
//Console.ReadLine();

if (dist < 100.0)
nwdhist.Add("iw");
if (!wdgniddict.ContainsKey(entid))
wdgniddict.Add(entid, gnid);
return entid;

catch (NullReferenceException e)
Console.Error.WriteLine(e.Message);

Console.WriteLine("Nothing found");
return -1;

public static string get_name_from_wdid(int wdid)
int nbgnid = -1;
if (wdgniddict.ContainsKey(wdid))
nbgnid = wdgniddict[wdid];
else
nbgnid = get_wd_gnid(wdid);

if (gndict.ContainsKey(nbgnid))
return makegnidlink(nbgnid);
else
return get_wd_name(wdid);

public static void verify_wd()
read_rdf_tree();

Dictionary latlongobjects = new Dictionary();
List withprop = new List();
int maxreadrdf = 10000000;

int n = 0;
//int n1566 = 0;
int n625 = 0;

//public class wdminiclass //minimal wikidata entry needed for verifying Geonames-links
//{
// public int gnid = 0;
// public double latitude = 9999.9;
// public double longitude = 9999.9;
// public List instance_of = new List();
//public double dist = 9999.9;
//public bool okdist = false;
//public bool okclass = false;
//public bool goodmatch = false;

//}

Dictionary wdminidict = new Dictionary();

//Console.WriteLine("First pass");
//using (StreamReader sr = new StreamReader("wikidata-simple-statements.nt"))
using (StreamReader sr = new StreamReader("wikidata-only1566.nt"))
while (!sr.EndOfStream)
String line = sr.ReadLine();
if ( line.Contains("P1566"))
Console.WriteLine(line);
rdfclass rc = rdf_parse(line);
n++;
if ((n % 10000) == 0)
Console.WriteLine("n = " + n.ToString());
if (n > maxreadrdf)
break;

if (rc.obj > 0)
if (!wdminidict.ContainsKey(rc.obj))
wdminiclass wdm = new wdminiclass();
wdminidict.Add(rc.obj, wdm);
if (rc.prop == propdict["gnid"])
wdminidict[rc.obj].gnid = tryconvert(rc.value);
Console.WriteLine("gnid = "+wdminidict[rc.obj].gnid.ToString());
else if (rc.prop == propdict["coordinates"])
if (!latlongobjects.ContainsKey(rc.value))
latlongobjects.Add(rc.value, rc.obj);
n625++;
//else
// Console.WriteLine("Repeated latlong " + rc.value);
else if (rc.prop == propdict["instance"])
if (!wdminidict[rc.obj].instance_of.Contains(rc.objlink))
wdminidict[rc.obj].instance_of.Add(rc.objlink);

else
if (latlongobjects.ContainsKey(rc.objstring))
if (!wdminidict.ContainsKey(latlongobjects[rc.objstring]))
wdminiclass wdm = new wdminiclass();
wdminidict.Add(latlongobjects[rc.objstring], wdm);
if (rc.prop == 6250001)
wdminidict[latlongobjects[rc.objstring]].latitude = tryconvertdouble(rc.value);
else if (rc.prop == 6250002)
wdminidict[latlongobjects[rc.objstring]].longitude = tryconvertdouble(rc.value);

Console.WriteLine("wdminidict: " + wdminidict.Count.ToString());

Dictionary> wddoubles = new Dictionary>();
Dictionary gnidwddict = new Dictionary();

int nmountains = 0;
int nranges = 0;
int nrangesgood = 0;

foreach (int wdid in wdminidict.Keys)
Console.WriteLine(wdid.ToString() + "; " + wdminidict[wdid].gnid.ToString() + "; " + wdminidict[wdid].latitude.ToString() + "; " + wdminidict[wdid].longitude.ToString());
int gnid = wdminidict[wdid].gnid;
if (gndict.ContainsKey(gnid))
if (!wdgniddict.ContainsKey(wdid))
wdgniddict.Add(wdid, gnid);
else if (wdgniddict[wdid] != gnid)
{// negative numbers count how many duplicates
if (wdgniddict[wdid] > 0)
wdgniddict[wdid] = -2;
else
wdgniddict[wdid]--;

if (!gnidwddict.ContainsKey(gnid))
gnidwddict.Add(gnid, wdid);
else if (gnidwddict[gnid] != wdid)
if (!wddoubles.ContainsKey(gnid))
List dlist = new List();
wddoubles.Add(gnid, dlist);
if (gnidwddict[gnid] > 0)
wddoubles[gnid].Add(gnidwddict[gnid]);
wddoubles[gnid].Add(wdid);
Console.WriteLine("Double!");
if (gnidwddict[gnid] > 0)
gnidwddict[gnid] = -2;
else
gnidwddict[gnid]--;

wdminidict[wdid].dist = get_distance_latlong(gndict[gnid].latitude, gndict[gnid].longitude, wdminidict[wdid].latitude, wdminidict[wdid].longitude);
Console.WriteLine("dist = " + wdminidict[wdid].dist.ToString("F2"));

string fcode = gndict[gnid].featurecode;
double maxdist = 10.0; //maximum acceptable distance for a match
if (!featurepointdict[fcode]) //... hundred times larger for non-point features
maxdist = 300 * maxdist;

wdminidict[wdid].okdist = (wdminidict[wdid].dist < maxdist);
Console.WriteLine("dist = " + wdminidict[wdid].dist.ToString("F2") + ", " + wdminidict[wdid].okdist.ToString());

int target = 0;
bool foundtarget = false;
string category = "default";
if (categorydict.ContainsKey(fcode))
category = categorydict[fcode];
Console.WriteLine("category = " + category + ", instances: " + wdminidict[wdid].instance_of.Count.ToString());
if (category == "subdivisions")
target = catwdclass["subdivision1"];
foreach (int inst in wdminidict[wdid].instance_of)
if (search_rdf_tree(target, inst, 0))
foundtarget = true;
break;
if (!foundtarget)
target = catwdclass["subdivision2"];
foreach (int inst in wdminidict[wdid].instance_of)
if (search_rdf_tree(target, inst, 0))
foundtarget = true;
break;
if (!foundtarget)
target = catwdclass["subdivision3"];
foreach (int inst in wdminidict[wdid].instance_of)
if (search_rdf_tree(target, inst, 0))
foundtarget = true;
break;
else if (category == "mountains")
Console.WriteLine("mountains " + fcode);
nmountains++;
if (fcode == "MTS")
nranges++;
target = catwdclass["mountains1"];
foreach (int inst in wdminidict[wdid].instance_of)
if (search_rdf_tree(target, inst, 0))
foundtarget = true;
break;
if (!foundtarget)
target = catwdclass["mountains2"];
foreach (int inst in wdminidict[wdid].instance_of)
if (search_rdf_tree(target, inst, 0))
foundtarget = true;
break;
if (foundtarget)
nrangesgood++;
else
if (!catwdclass.ContainsKey(category))
category = "default";
target = catwdclass[category];
foreach (int inst in wdminidict[wdid].instance_of)
if (search_rdf_tree(target, inst, 0))
foundtarget = true;
break;

wdminidict[wdid].okclass = foundtarget;
else
Console.WriteLine("gnid not found");

int ngood = 0;
foreach (int wdid in wdminidict.Keys)
wdminidict[wdid].goodmatch = (wdminidict[wdid].okclass && wdminidict[wdid].okdist);

foreach (int gnid in wddoubles.Keys)
int nokclass = 0;
int idok = -1;
double bestdist = 9999.9;
int idbestdist = -1;
foreach (int wdid in wddoubles[gnid])
if (wdminidict[wdid].okclass)
nokclass++;
idok = wdid;
if (wdminidict[wdid].okdist && (wdminidict[wdid].dist < bestdist))
idbestdist = wdid;
bestdist = wdminidict[wdid].dist;
if (nokclass > 1)
idok = idbestdist;
foreach (int wdid in wddoubles[gnid])
wdminidict[wdid].goodmatch = (wdid == idok);

Console.WriteLine("wdminidict: " + wdminidict.Count.ToString());

using (StreamWriter sw = new StreamWriter("wikidata-good.nt"))

foreach (int wdid in wdminidict.Keys)
if (wdminidict[wdid].goodmatch)
sw.WriteLine(wdminidict[wdid].gnid.ToString() + tabstring + wdid.ToString());
ngood++;
Console.WriteLine("ngood = " + ngood.ToString());

Page pbad = new Page(makesite, "Användare:Lsjbot/Bad P1566 in Wikidata");
pbad.text = "This is a list of wikidata items with dubious links to GeoNames (P1566)\n\n";

pbad.text += "== Wrong type of object ==\n";
pbad.text += "Feature code on GeoNames does not match InstanceOf (P31) on Wikidata\n\n";

int nwrongtype = 0;
foreach (int wdid in wdminidict.Keys)
if ((!wdminidict[wdid].okclass) && (wdminidict[wdid].okdist))
pbad.text += "* [[:d:Q" + wdid.ToString() + "]]\n";
nwrongtype++;
Console.WriteLine("nwrongtype = " + nwrongtype.ToString());

pbad.text += "== Position mismatch ==\n";
pbad.text += "Latitude/longitude on GeoNames does not match latitude/longitude (P625) on Wikidata\n\n";

int nwrongpos = 0;
foreach (int wdid in wdminidict.Keys)
if ((!wdminidict[wdid].okdist) && (wdminidict[wdid].okclass))
pbad.text += "* [[:d:Q" + wdid.ToString() + "]]\n";
nwrongpos++;

Console.WriteLine("nwrongpos = " + nwrongpos.ToString());

pbad.text += "== Both position and type mismatch ==\n";
pbad.text += "Latitude/longitude on GeoNames does not match latitude/longitude (P625) on Wikidata ''and'' ";
pbad.text += "feature code on GeoNames does not match InstanceOf (P31) on Wikidata\n\n";

int nwrongboth = 0;
foreach (int wdid in wdminidict.Keys)
if ((!wdminidict[wdid].okdist) && (!wdminidict[wdid].okclass))
pbad.text += "* [[:d:Q" + wdid.ToString() + "]]\n";
nwrongboth++;
Console.WriteLine("nwrongboth = " + nwrongboth.ToString());

pbad.text += "== Duplicate entries ==\n";
pbad.text += "Several Wikidata objects have P1566 pointing to same GeoNames entry\n\n";

int ndup = 0;
foreach (int gnid in wddoubles.Keys)
pbad.text += "* GeoNames ID " + gnid.ToString() + "\n";
foreach (int wdid in wddoubles[gnid])
pbad.text += "** [[:d:Q" + wdid.ToString() + "]]\n";
ndup++;
Console.WriteLine("ndup = " + ndup.ToString());

Console.WriteLine("nmountains = " + nmountains.ToString());
Console.WriteLine("nranges = " + nranges.ToString());
Console.WriteLine("nrangesgood = " + nrangesgood.ToString());

//trysave(pbad,3);
using (StreamWriter sw = new StreamWriter("wikidata-bad.txt"))
sw.WriteLine(pbad.text);

public static void verify_wd_online()
Dictionary> wdclass = new Dictionary>();

foreach (int gnid in gndict.Keys)
if (gndict[gnid].wdid < 0)
continue;

XmlDocument cx = get_wd_xml(gndict[gnid].wdid);

int gnidwd = tryconvert(get_wd_prop(propdict["gnid"], cx));

if ((gnidwd > 0) && (gnidwd != gnid))
Console.WriteLine("Different gnid in wikidata");
continue;

double[] latlong = get_wd_position(cx);
double dist = get_distance_latlong(gndict[gnid].latitude, gndict[gnid].longitude, latlong[0], latlong[1]);
Console.WriteLine("distance = " + dist.ToString());
string wdname = get_wd_name_from_xml(cx);
Console.WriteLine(gndict[gnid].Name+" / "+ wdname);

List instances = get_wd_prop_idlist(propdict["instance"], cx);

foreach (int inst in instances)
XmlDocument cxi = get_wd_xml(inst);
string instancename = get_wd_name_from_xml(cxi, "en");
if (!wdclass.ContainsKey(gndict[gnid].featurecode))
Dictionary dd = new Dictionary();
wdclass.Add(gndict[gnid].featurecode, dd);
if (!wdclass[gndict[gnid].featurecode].ContainsKey(instancename))
wdclass[gndict[gnid].featurecode].Add(instancename, 0);
wdclass[gndict[gnid].featurecode][instancename]++;

List subclassof = new List();
int k = 0;
do
subclassof.Clear();
subclassof = get_wd_prop_idlist(propdict["subclass"], cxi);
foreach (int sc in subclassof)
cxi = get_wd_xml(sc);
string scname = get_wd_name_from_xml(cxi, "en");
Console.WriteLine("scname = " + scname);
if (!wdclass.ContainsKey(gndict[gnid].featurecode))
Dictionary dd = new Dictionary();
wdclass.Add(gndict[gnid].featurecode, dd);
if (!wdclass[gndict[gnid].featurecode].ContainsKey(scname))
wdclass[gndict[gnid].featurecode].Add(scname, 0);
wdclass[gndict[gnid].featurecode][scname]++;
k++;
while ((k<7) && (subclassof.Count > 0));

using (StreamWriter sw = new StreamWriter("gnvswiki-instance.txt"))

foreach (string fc in wdclass.Keys)
sw.WriteLine(fc);
foreach (string sc in wdclass[fc].Keys)
sw.WriteLine(" " + sc + " " + wdclass[fc][sc].ToString());

//propdict.Add("instance", 31);
//propdict.Add("subclass", 279);

public static bool climb_wd_class_tree(int wdid, string targetclass, int depth)
if (depth > 7)
return false;

//bool found = false;

XmlDocument cxi = get_wd_xml(wdid);
string scname = get_wd_name_from_xml(cxi, "en");
if (scname == targetclass)
return true;
else
List subclassof = get_wd_prop_idlist(propdict["subclass"], cxi);
foreach (int sc in subclassof)
if (climb_wd_class_tree(sc, targetclass, depth + 1))
return true;

return false;

public static string get_terrain_type2(List farlist, int gnid)
string terrain_type = "unknown";

//int n = 0;
//int nelev = 0;
//double elevationsum = 0.0;
//double elevationvar = 0.0;
//double elevationsquare = 0.0;
//double elevationmean = 0.0;

//double[] elevdirsum = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
//double[] elevdirmean = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
//int[] nelevdir = { 0, 0, 0, 0, 0, 0, 0, 0 };

//foreach (int nb in farlist)
//{
// if (!gndict.ContainsKey(nb))
// continue;

// n++;

// if (gndict[nb].elevation > 0)
// {
// nelev++;
// elevationsum += gndict[nb].elevation;
// elevationsquare += gndict[nb].elevation * gndict[nb].elevation;
// int dir = get_direction(gnid, nb);
// if (dir > 0)
// {
// nelevdir[dir - 1]++;
// elevdirsum[dir - 1] += gndict[nb].elevation;
// }
// }

//}

//if (nelev > 10)
//{
// elevationmean = elevationsum / nelev;
// elevationvar = elevationsquare / nelev - elevationmean * elevationmean;

// Console.WriteLine("elevation mean, var = " + elevationmean.ToString() + ", " + elevationvar.ToString());

// if (statisticsonly)
// evarhist.Add(elevationvar);

// terrain_type = terrain_label(elevationvar,elevationmean);

// if (gndict[gnid].elevation > 0)
// {
// if (elevationmean - gndict[gnid].elevation > 500)
// terrain_type += " valley";
// else if (elevationmean - gndict[gnid].elevation < -500)
// terrain_type += " peak";
// }

// // 1
// // 7 5
// // 3 4
// // 8 6
// // 2

// int ndir = 0;
// if (nelev > 20)
// {
// for (int i = 0; i < 8; i++)
// {
// if (nelevdir[i] > 0)
// {
// elevdirmean[i] = elevdirsum[i] / nelevdir[i];
// ndir++;
// }
// else
// elevdirmean[i] = -1.0;
// }
// }

// if (statisticsonly)
// ndirhist.Add(ndir);

//}

//if (statisticsonly)
//{
// Console.WriteLine(gndict[gnid].Name + ": " + terrain_type);
// terrainhist.Add(terrain_type);
//}
return terrain_type;

public static int get_altitude(int gnid)
string dir = extractdir;
double lat = gndict[gnid].latitude;
double lon = gndict[gnid].longitude;
string filename = make_hgt_filename(lat, lon);

int[,] map = get_hgt_array(dir + filename);
int mapsize = map.GetLength(0);

double xfraction = lon - Math.Floor(lon);
int pixx = Convert.ToInt32(mapsize * xfraction); //x counted in positive longitude direction
double yfraction = lat - Math.Floor(lat);
int pixy = mapsize - Convert.ToInt32(mapsize * yfraction); //y counted in negative latitude direction
if (pixx >= mapsize)
pixx = mapsize - 1;
if (pixy >= mapsize)
pixy = mapsize - 1;
//Console.WriteLine(gnid.ToString() + ": " + pixx.ToString() + ", " + pixy.ToString());
int alt = map[pixx, pixy];
if (alt == 32768) //bad pixel
alt = 0;
return alt;

public static string classify_terrain(double elevationvar,double elevationmean)
string terrain_type = "";

if (elevationvar < 2500.0) //rms < 50
terrain_type = "flat";
if ( elevationvar < 100) //rms < 10
terrain_type = "very "+terrain_type;
if (elevationmean > 1500) //average higher than 1500
terrain_type += " high";
else if (elevationvar < 62500.0) //rms < 250
terrain_type = "hilly";
if ( elevationvar < 20000) //rms < 140
terrain_type = "somewhat "+terrain_type;
if (elevationmean > 1500) //average higher than 1500
terrain_type += " high";
else if (elevationvar < 122500.0) //rms < 350
terrain_type = "low-mountains";
else if (elevationvar < 250000.0) //rms < 500
terrain_type = "medium-mountains";
else
terrain_type = "high-mountains";

return terrain_type;

public static int next_dir(int dir)
// 1
// 7 5
// 3 4
// 8 6
// 2

switch (dir)
case 1:
return 5;
case 5:
return 4;
case 4:
return 6;
case 6:
return 2;
case 2:
return 8;
case 8:
return 3;
case 3:
return 7;
case 7:
return 1;
default:
return -1;

public static void put_mountains_on_map(ref int[,] map, double lat, double lon)
int mapsize = map.GetLength(0);
List farlist = getneighbors(lat, lon, 20.0);
foreach (int nb in farlist)
if (is_height(gndict[nb].featurecode))
int xnb = get_x_pixel(gndict[nb].longitude, lon);
if ((xnb < 0) || (xnb >= mapsize))
continue;
int ynb = get_y_pixel(gndict[nb].latitude, lat);
if ((ynb < 0) || (ynb >= mapsize))
continue;
map[xnb, ynb] = nb;

public static int get_summit(int gnid, out double slat, out double slon) //seeks proper DEM summit of a mountain.
Console.WriteLine("get_summit");
double lat = gndict[gnid].latitude;
double lon = gndict[gnid].longitude;
int[,] map = get_3x3map(lat, lon);
int mapsize = map.GetLength(0);
//double scale = Math.Cos(lat * 3.1416 / 180);
//double pixkmx = scale * 40000 / (360 * 1200);
//double pixkmy = 40000.0 / (360.0 * 1200.0);

int x0 = get_x_pixel(lon, lon);
int y0 = get_y_pixel(lat, lat);

int[,] donemap = new int[mapsize, mapsize];

for (int i = 0; i < mapsize; i++)
for (int j = 0; j < mapsize; j++)
donemap[i, j] = 0;
put_mountains_on_map(ref donemap, lat, lon);

donemap[x0, y0] = -1; //negative for done, positive for mountain gnid

int tolerance = -1; //maximum dip before going up

int xhout = -1;
int yhout = -1;

//Console.WriteLine("x0,yo = " + x0.ToString() + " " + y0.ToString());
int mtgnid = seek_highest(ref map, ref donemap, x0, y0, tolerance,out xhout,out yhout);
//Console.WriteLine("xh,yh = " + xhout.ToString() + " " + yhout.ToString());

double one1200 = 1.0 / 1200.0;
double dlon = (xhout - x0) * one1200;
double dlat = -(yhout - y0) * one1200; //reverse sign because higher pixel number is lower latitude
slat = gndict[gnid].latitude + dlat;
slon = gndict[gnid].longitude + dlon;

if (mtgnid > 0) //Another mountain on summit
return -1;
else if (xhout < 0)
return -1;
else
return map[xhout, yhout];

public static int seek_highest(ref int[,] map, ref int[,] donemap, int x0, int y0, int tolerance, out int xhout, out int yhout)
//Find nearest mountain from x0,y0.
//Assumes donemap contains gnid of mountains in appropriate cells.
//Donemap = 0: untouched empty cell
//Donemap = -1: cell touched here
//Donemap = -2: cell to skip
//Tolerance = permitted dip before terrain turns upwards (negative!)

Console.WriteLine("seek_highest");

int mapsize = map.GetLength(0);

int maxx = x0;
int maxy = y0;
int minx = x0;
int miny = y0;
int xhigh = x0;
int yhigh = y0;
int xhighest = -1;
int yhighest = -1;
xhout = 0;
yhout = 0;

int x = x0;
int y = y0;
int newhigh = map[x0, y0];
int highesthigh = newhigh;
int nsame = 0;
int nround = 0;
int maxround = 1000;

int maxsame = 1000;
int[] xh = new int[maxsame];
int[] yh = new int[maxsame];

while (((newhigh - map[x0, y0]) >= tolerance) && (newhigh - highesthigh >= 5 * tolerance))
nround++;
//Console.WriteLine("nround = " + nround.ToString());
if (nround > maxround)
break;

newhigh = tolerance - 1;
for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy; j++)
if (donemap[i, j] == -1)
//Console.WriteLine("i,j=" + i.ToString() +","+ j.ToString());
for (int u = -1; u <= 1; u++)
if ((i + u > 0) && (i + u < mapsize))
for (int v = -1; v <= 1; v++)
if ((j + v > 0) && (j + v < mapsize))
if (donemap[i + u, j + v] >= 0)
if (map[i + u, j + v] > newhigh)
newhigh = map[i + u, j + v];
xhigh = i + u;
yhigh = j + v;
nsame = 0;
else if (map[i + u, j + v] == newhigh)
if (nsame < maxsame)
xh[nsame] = i + u;
yh[nsame] = j + v;
nsame++;
//xyh.Add(Tuple.Create(i + u,j+v));

//Console.WriteLine("newhigh = " + newhigh.ToString());
if (newhigh > highesthigh)
highesthigh = newhigh;
xhighest = xhigh;
yhighest = yhigh;
Console.WriteLine("seek_highest: highesthigh " + highesthigh.ToString() + " xh,yh = " + xhighest.ToString() + ", " + yhighest.ToString());

if ((newhigh - map[x0, y0]) > tolerance)
if (donemap[xhigh, yhigh] > 0)
break;

donemap[xhigh, yhigh] = -1;

if (nsame > 0)
//Console.WriteLine("seek_highest: nsame = " + nsame.ToString());
//foreach (Tuple xy in xyh)
// donemap[xy.Item1,xy.Item2] = nround;
for (int isame = 0; isame < nsame; isame++)
donemap[xh[isame], yh[isame]] = -1;

if (xhigh > maxx)
maxx = xhigh;
Console.WriteLine("maxx = " + maxx.ToString());
if (maxx >= mapsize)
newhigh = -9999;
if (maxx >= x0 + 500)
newhigh = -9999;
if (xhigh < minx)
minx = xhigh;
Console.WriteLine("minx = " + minx.ToString());
if (minx <= 0)
newhigh = -9999;
if (minx <= x0 - 500)
newhigh = -9999;
if (yhigh > maxy)
maxy = yhigh;
Console.WriteLine("maxy = " + maxy.ToString());
if (maxy >= mapsize)
newhigh = -9999;
if (maxy >= y0 + 500)
newhigh = -9999;
if (yhigh < miny)
miny = yhigh;
Console.WriteLine("miny = " + miny.ToString());
if (miny <= 0)
newhigh = -9999;
if (miny <= y0 - 500)
newhigh = -9999;

if (newhigh <= 0)
break;

//Console.WriteLine("xhigh,yhigh = " + xhigh.ToString() + ", " + yhigh.ToString());

xhout = xhighest;
yhout = yhighest;
return donemap[xhigh, yhigh];

public static int seek_mountain(ref int[,] map, ref int[,] donemap, int x0, int y0, int tolerance)
//Find nearest mountain from x0,y0.
//Assumes donemap contains gnid of mountains in appropriate cells.
//Donemap = 0: untouched empty cell
//Donemap = -1: cell touched here
//Donemap = -2: cell to skip
//Tolerance = permitted dip before terrain turns upwards (negative!)

Console.WriteLine("seek_mountain");

int mapsize = map.GetLength(0);

int maxx = x0;
int maxy = y0;
int minx = x0;
int miny = y0;
int xhigh = 0;
int yhigh = 0;

int x = x0;
int y = y0;
int newhigh = map[x0, y0];
int highesthigh = newhigh;
int nsame = 0;
int nround = 0;
int maxround = 10000;

int maxsame = 1000;
int[] xh = new int[maxsame];
int[] yh = new int[maxsame];

while (donemap[x, y] <= 0 && ((newhigh - map[x0, y0]) > tolerance) && (newhigh - highesthigh > 5*tolerance))
nround++;
if (nround > maxround)
break;

newhigh = tolerance - 1;
for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy; j++)
if (donemap[i, j] == -1)
for (int u = -1; u <= 1; u++)
if ((i + u > 0) && (i + u < mapsize))
for (int v = -1; v <= 1; v++)
if ((j + v > 0) && (j + v < mapsize))
if (donemap[i + u, j + v] >= 0)
if (map[i + u, j + v] > newhigh)
newhigh = map[i + u, j + v];
xhigh = i + u;
yhigh = j + v;
nsame = 0;
else if (map[i + u, j + v] == newhigh)
if (nsame < maxsame)
xh[nsame] = i + u;
yh[nsame] = j + v;
nsame++;
//xyh.Add(Tuple.Create(i + u,j+v));

if (newhigh > highesthigh)
highesthigh = newhigh;

if ((newhigh - map[x0, y0]) > tolerance)
if (donemap[xhigh, yhigh] > 0)
break;

donemap[xhigh, yhigh] = -1;

if (nsame > 0)
Console.WriteLine("seek_mountain: nsame = " + nsame.ToString());
//foreach (Tuple xy in xyh)
// donemap[xy.Item1,xy.Item2] = nround;
for (int isame = 0; isame < nsame; isame++)
donemap[xh[isame], yh[isame]] = -1;

if (xhigh > maxx)
maxx = xhigh;
Console.WriteLine("maxx = " + maxx.ToString());
if (maxx >= mapsize)
newhigh = -9999;
if (maxx >= x0+500)
newhigh = -9999;
if (xhigh < minx)
minx = xhigh;
Console.WriteLine("minx = " + minx.ToString());
if (minx <= 0)
newhigh = -9999;
if (minx <= x0-500)
newhigh = -9999;
if (yhigh > maxy)
maxy = yhigh;
Console.WriteLine("maxy = " + maxy.ToString());
if (maxy >= mapsize)
newhigh = -9999;
if (maxy >= y0+500)
newhigh = -9999;
if (yhigh < miny)
miny = yhigh;
Console.WriteLine("miny = " + miny.ToString());
if (miny <= 0)
newhigh = -9999;
if (miny <= y0-500)
newhigh = -9999;

if (newhigh <= 0)
break;

//Console.WriteLine("xhigh,yhigh = " + xhigh.ToString() + ", " + yhigh.ToString());

return donemap[xhigh, yhigh];

public static bool between_mountains(int gnid, out int mgnid1, out int mgnid2) //calculates whether gnid is part of a mountain. Intended for use with spurs etc.
Console.WriteLine("Between_mountains");
double lat = gndict[gnid].latitude;
double lon = gndict[gnid].longitude;
int[,] map = get_3x3map(lat, lon);
int mapsize = map.GetLength(0);
//double scale = Math.Cos(lat * 3.1416 / 180);
//double pixkmx = scale * 40000 / (360 * 1200);
//double pixkmy = 40000.0 / (360.0 * 1200.0);
mgnid1 = -1;
mgnid2 = -1;

int x0 = get_x_pixel(lon, lon);
int y0 = get_y_pixel(lat, lat);

int[,] donemap = new int[mapsize, mapsize];

for (int i = 0; i < mapsize; i++)
for (int j = 0; j < mapsize; j++)
donemap[i, j] = 0;
put_mountains_on_map(ref donemap, lat, lon);

if (donemap[x0, y0] > 0)
return false;

donemap[x0, y0] = -1; //negative for done, positive for mountain gnid

int tolerance = -10; //maximum dip before going up

mgnid1 = seek_mountain(ref map, ref donemap, x0, y0, tolerance);

if (!gndict.ContainsKey(mgnid1))
return false;

double dlat = gndict[mgnid1].latitude - lat;
double dlon = gndict[mgnid1].longitude - lon;

if (Math.Abs(dlat) > Math.Abs(dlon))
if (dlat > 0) //North (smaller y!)
for (int i = 0; i < y0; i++)
for (int j = 0; j < mapsize; j++)
donemap[j,i] = -2;
else //South (larger y!)
for (int i = y0+1; i < mapsize; i++)
for (int j = 0; j < mapsize; j++)
donemap[j,i] = -2;
else
if ( dlon > 0 ) //East
for (int i = x0+1; i < mapsize; i++)
for (int j = 0; j < mapsize; j++)
donemap[i, j] = -2;
else //West
for (int i = 0; i < x0; i++)
for (int j = 0; j < mapsize; j++)
donemap[i, j] = -2;
mgnid2 = seek_mountain(ref map, ref donemap, x0, y0, tolerance);

if (!gndict.ContainsKey(mgnid2))
return false;

return true;

public static int attach_to_mountain(int gnid) //calculates whether gnid is part of a mountain. Intended for use with spurs etc.
Console.WriteLine("attach_to_mountain");
double lat = gndict[gnid].latitude;
double lon = gndict[gnid].longitude;
int[,] map = get_3x3map(lat, lon);
int mapsize = map.GetLength(0);
//double scale = Math.Cos(lat * 3.1416 / 180);
//double pixkmx = scale * 40000 / (360 * 1200);
//double pixkmy = 40000.0 / (360.0 * 1200.0);

int x0 = get_x_pixel(lon, lon);
int y0 = get_y_pixel(lat, lat);

int[,] donemap = new int[mapsize, mapsize];

for (int i = 0; i < mapsize; i++)
for (int j = 0; j < mapsize; j++)
donemap[i, j] = 0;
put_mountains_on_map(ref donemap, lat, lon);
if (donemap[x0, y0] > 0)
return donemap[x0, y0];
donemap[x0, y0] = -1; //negative for done, positive for mountain gnid

int tolerance = -10; //maximum dip before going up

return seek_mountain(ref map,ref donemap, x0, y0, tolerance);

public static int get_prominence(int gnid, out double width) //calculates the topographic prominence of a mountain
Console.WriteLine("get_prominence");

double lat = gndict[gnid].latitude;
double lon = gndict[gnid].longitude;
//Console.WriteLine("lat, lon = " + lat.ToString() + " " + lon.ToString());
int[,] map = get_3x3map(lat, lon);
int mapsize = map.GetLength(0);
double scale = Math.Cos(lat * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
double pixkmy = 40000.0 / (360.0 * 1200.0);
width = 0;

int x0 = get_x_pixel(lon, lon);
int y0 = get_y_pixel(lat, lat);

int[,] donemap = new int[mapsize, mapsize];

bool higherfound = false;
for (int i = 0; i < mapsize; i++)
for (int j = 0; j < mapsize; j++)
donemap[i, j] = 0;
if (map[i, j] > gndict[gnid].elevation)
higherfound = true;

if (!higherfound) //if highest point in map, algorithm won't work
return -1;

donemap[x0, y0] = 1;
int maxx = x0;
int maxy = y0;
int minx = x0;
int miny = y0;

int lowesthigh = 9999;
int newhigh = -1;
int badhigh = 9999;
int xhigh = 0;
int yhigh = 0;
int nround = 1;
int nroundlow = -1;
int nsame = 0;
int ntotal = 0;
int maxtotal = 100000;
//Dictionary xyhdict = new Dictionary();
//List> xyh = new List>();
int maxsame = 1000;
int[] xh = new int[maxsame];
int[] yh = new int[maxsame];

while ((newhigh < gndict[gnid].elevation) || (nround < 6)) //disregards the first 5 pixels, in case of slight position mismatch
newhigh = -1;
for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy; j++)
if (donemap[i, j] > 0)
for (int u = -1; u <= 1; u++)
if ((i + u > 0) && (i + u < mapsize))
for (int v = -1; v <= 1; v++)
if ((j + v > 0) && (j + v < mapsize))
if (donemap[i + u, j + v] == 0)
if (map[i + u, j + v] > newhigh)
newhigh = map[i + u, j + v];
xhigh = i + u;
yhigh = j + v;
nsame = 0;
else if (map[i + u, j + v] == newhigh)
if (nsame < maxsame)
xh[nsame] = i + u;
yh[nsame] = j + v;
nsame++;
//xyh.Add(Tuple.Create(i + u,j+v));
nround++;
Console.WriteLine("get_prominence: nround,ntotal,newhigh = " + nround.ToString()+", "+ntotal+", "+newhigh);

ntotal += nsame + 1;
if (ntotal > maxtotal)
newhigh = badhigh;

donemap[xhigh, yhigh] = nround;
if (nsame > 0)
//Console.WriteLine("get_prominence: nsame = " + nsame.ToString());
//foreach (Tuple xy in xyh)
// donemap[xy.Item1,xy.Item2] = nround;
for (int isame = 0; isame < nsame; isame++)
donemap[xh[isame], yh[isame]] = nround;

if (newhigh < lowesthigh)
lowesthigh = newhigh;
nroundlow = nround;

if (xhigh > maxx)
maxx = xhigh;
if ( maxx >= mapsize )
newhigh = badhigh;
if (xhigh < minx)
minx = xhigh;
if ( minx <= 0 )
newhigh = badhigh;
if (yhigh > maxy)
maxy = yhigh;
if ( maxy >= mapsize )
newhigh = badhigh;
if (yhigh < miny)
miny = yhigh;
if ( miny <= 0 )
newhigh = badhigh;

if ( newhigh <= 0 )
newhigh = badhigh;

double r2max = 0;
int xr2max = 0;
int yr2max = 0;
int npix = 0;
for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy; j++)
if (( donemap[i,j] > 0 ) && (donemap[i,j] < nroundlow))
npix++;
double r2 = scale*scale*(i-x0)*(i-x0)+(j-y0)*(j-y0);
if ( r2 > r2max )
r2max = r2;
xr2max = i;
yr2max = j;

Console.WriteLine("get_promince: npix = " + npix.ToString());

if ( npix <= 1 )
return -1;

if (newhigh == badhigh)
return -1;

r2max = 0;
int xw = 0;
int yw = 0;

for (int i = minx; i <= maxx; i++)
for (int j = miny; j <= maxy; j++)
if (( donemap[i,j] > 0 ) && (donemap[i,j] < nroundlow))

double r2 = scale*scale*(i-xr2max)*(i-xr2max)+(j-yr2max)*(j-yr2max);
if ( r2 > r2max )
r2max = r2;
xw = i;
yw = j;

width = Math.Sqrt(r2max)*pixkmy;

Console.WriteLine("get_promince: nroundfinal = " + nround.ToString());

if (lowesthigh < gndict[gnid].elevation)
return gndict[gnid].elevation-lowesthigh;
else
return -1;

public static string get_terrain_type3(int gnid, double radius)
string terrain_type = get_terrain_type_latlong(ref gndict[gnid].elevation, gndict[gnid].latitude, gndict[gnid].longitude,radius);
if (statisticsonly)
Console.WriteLine(gndict[gnid].Name + ": " + terrain_type);
string[] tp = terrain_type.Split('|');
foreach (string ttp in tp)
terrainhist.Add(ttp);
return terrain_type;

public static string get_terrain_type_latlong(ref int elevation, double lat,double lon,double radius)

string terrain_type = "unknown";

Console.WriteLine("get_terrain_type3");

//int n = 0;
int nelev = 0;
int ndry = 0;
int nocean = 0;
int ncentral = 0;
double elevationsum = 0.0;
double elevationvar = 0.0;
double elevationsquare = 0.0;
double elevationmean = 0.0;
double centralsum = 0.0;
double centralvar = 0.0;
double centralsquare = 0.0;
double centralmean = 0.0;
double r2ocean = 9999.9;
int oceanmindir = -1;

int[,] map = get_3x3map(lat,lon);
int mapsize = map.GetLength(0);

int x0 = get_x_pixel(lon,lon);
int y0 = get_y_pixel(lat,lat);
Console.WriteLine(lat.ToString() + " " + lon.ToString() + " " + x0.ToString() + " " + y0.ToString());

if (elevation <= 0)
elevation = map[x0, y0];
else if (statisticsonly)
elevdiffhist.Add(1.0 * (elevation - map[x0, y0]));

double scale = Math.Cos(lat * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
double pixkmy = 40000.0 / (360.0 * 1200.0);

double[] elevdirsum = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
double[] elevdirmean = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
double[] elevdirsquare = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
double[] elevdirvar = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

int[] nelevdir = { 0, 0, 0, 0, 0, 0, 0, 0 };
int[] noceandir = { 0, 0, 0, 0, 0, 0, 0, 0 };
int[] ndrydir = { 0, 0, 0, 0, 0, 0, 0, 0 };
double slope1sum = 0;
double slope5sum = 0;
double slope1mean = 0;
double slope5mean = 0;
int nslope = 0;

int r = Convert.ToInt32(radius/pixkmx);
double r2max = radius/pixkmy * radius/pixkmy;
double r2central = r2max/16; //central part is one quarter the radius

for (int x = x0-r; x < x0+r; x++)
if (( x > 0 ) && (x < mapsize-1))
for (int y = y0-r; y < y0+r; y++)
if ((y > 0) && (y < mapsize-1))
double r2 = scale * scale * (x - x0) * (x - x0) + (y - y0) * (y - y0);
if (r2 < r2max)
int weight = 1;
if (4 * r2 < r2max)
weight = 4;
else if (3 * r2 < r2max)
weight = 3;
else if (2 * r2 < r2max)
weight = 2;
int dir = get_pix_direction(x, y, x0, y0, scale);
if (map[x, y] != 0) //not ocean
if (map[x, y] != 32768) //bad pixel
nelev++;
ndry += weight;
elevationsum += map[x, y];
elevationsquare += map[x, y] * map[x, y];
if ( dir > 0 )
ndrydir[dir - 1] += weight;
if ( r2 < r2central )
centralsum += map[x, y];
centralsquare += map[x, y] * map[x, y];
ncentral++;
else if (dir > 0)
nelevdir[dir - 1]++;
elevdirsum[dir - 1] += map[x, y];
elevdirsquare[dir - 1] += map[x, y]*map[x, y];
slope1sum += Math.Abs(0.001*(map[x,y]-map[x,y-1]))/pixkmy; //0.001 because vertical meters and horizontal km
slope1sum += Math.Abs(0.001*(map[x,y]-map[x-1,y]))/pixkmx;
if ( y>5 )
slope5sum += Math.Abs(0.0002*(map[x,y]-map[x,y-5]))/pixkmy; //0.0002 = 0.001/5 bec
if ( x>5)
slope5sum += Math.Abs(0.0002*(map[x,y]-map[x-5,y]))/pixkmx;
nslope +=2;
else
nocean += weight;
if (dir > 0)
noceandir[dir - 1] += weight;
if (r2 < r2ocean)
r2ocean = r2;
oceanmindir = dir;

if (nelev > 10)
elevationmean = elevationsum / nelev;
elevationvar = elevationsquare / nelev - elevationmean * elevationmean;

slope1mean = slope1sum / nslope;
slope5mean = slope5sum / nslope;

if (statisticsonly)
double sloperms = 10000 * slope1mean / (Math.Sqrt(elevationvar)+20);
Console.WriteLine(sloperms.ToString());
slopermshist.Add(sloperms);

Console.WriteLine("elevation mean, var = " + elevationmean.ToString() + ", " + elevationvar.ToString());
Console.WriteLine("slope mean1,mean5 = " + slope1mean.ToString() + ", " + slope5mean.ToString());

if (statisticsonly)
evarhist.Add(elevationvar);
if (elevation > 0)
evarhist.Add(1.0 * (elevation - map[x0, y0]));
slope1hist.Add(100.0*slope1mean);
slope5hist.Add(100.0*slope5mean);

terrain_type = classify_terrain(elevationvar,elevationmean);

// 1
// 7 5
// 3 4
// 8 6
// 2

int ndir = 0;
string[] terrtype_sector = new string[9];

if (ncentral > 10)
centralmean = centralsum / ncentral;
centralvar = centralsquare / ncentral - centralmean * centralmean;
Console.WriteLine("Central elevation mean, var = " + centralmean.ToString() + ", " + centralvar.ToString());
terrtype_sector[8] = classify_terrain(centralvar, centralmean);

terrain_type += "|central " + terrtype_sector[8];

if (nelev > 20)
//Dictionary terrtype = new Dictionary();
for (int i = 0; i < 8; i++)
terrtype_sector[i] = "";
if (nelevdir[i] > 10)
elevdirmean[i] = elevdirsum[i] / nelevdir[i];
//elevationvar = elevationsquare / nelev - elevationmean * elevationmean;
elevdirvar[i] = elevdirsquare[i]/ nelevdir[i] - elevdirmean[i]*elevdirmean[i];
terrtype_sector[i] = classify_terrain(elevdirvar[i],elevdirmean[i]);
//if (!terrtype.ContainsKey(terrtype_sector[i]))
// terrtype.Add(terrtype_sector[i], 0);
//terrtype[terrtype_sector[i]]++;
terrain_type += "|dir"+(i+1).ToString()+" "+terrtype_sector[i];
ndir++;
else
elevdirmean[i] = -99999.0;
//if (!terrtype.ContainsKey(terrtype_sector[8]))
// terrtype.Add(terrtype_sector[8], 0);
//terrtype[terrtype_sector[8]]++;
//Console.WriteLine("Types in sectors: " + terrtype.Count.ToString());
//if (statisticsonly)
// nsameterrhist.Add(terrtype.Count.ToString());

//int[] getdircoord(int dir)

if (statisticsonly)
ndirhist.Add(ndir);

int nwet = 0;
int nbitwet = 0;
if (nocean > 10)
int iwet = -1;
for (int i = 0; i < 8; i++)
if (noceandir[i] > ndrydir[i])
nwet++;
iwet = i;
else if (2*noceandir[i] > ndrydir[i])
nbitwet++;

if (nwet > 0) //at least one sector has mostly ocean
terrain_type += "|ocean ";
if ((nwet == 1) && (nbitwet == 0))
terrain_type += "bay " + (iwet+1).ToString();
else
int xsum = 0;
int ysum = 0;
for (int i = 0; i < 8; i++)
if (noceandir[i] > ndrydir[i])
int[] cdir = getdircoord(i + 1);
xsum += cdir[0];
ysum += cdir[1];

terrain_type += "coast" + get_NSEW_from_xysum(xsum, ysum);

if (nwet == 0)
double triggerdiff = Math.Sqrt(elevationvar); //enough height difference to call it a terrain feature; more needed in rugged terrain
if (triggerdiff < 20)
triggerdiff = 20;
Console.WriteLine("triggerdiff = " + triggerdiff.ToString());
if (elevation > 0)
if (elevationmean - elevation > triggerdiff)
terrain_type += "|valley ";
else if (elevationmean - elevation < -triggerdiff)
terrain_type += "|peak ";

double xsum = 0; //measures east-west slope
double ysum = 0; //measure north-south slope
double x0sum = 0; //centerline NS altitude
double y0sum = 0; //centerline EW altitude
double x2sum = 0; //periphery NS altitude
double y2sum = 0; //periphery EW altitude
double xysum = 0;

for (int i = 0; i < 8; i++)
int[] cdir = getdircoord(i + 1);
xsum += cdir[0] * (elevdirmean[i]-elevationmean) / 6;
ysum += cdir[1] * (elevdirmean[i]-elevationmean) / 6;
x0sum += (1 - cdir[0] * cdir[0]) * (elevdirmean[i]-elevationmean) / 2;
y0sum += (1 - cdir[1] * cdir[1]) * (elevdirmean[i]-elevationmean) / 2;
x2sum += cdir[0] * cdir[0] * (elevdirmean[i]-elevationmean) / 6;
y2sum += cdir[1] * cdir[1] * (elevdirmean[i]-elevationmean) / 6;
xysum += cdir[0] * cdir[1] * (elevdirmean[i]-elevationmean) / 4;

//xsum -= elevationmean;
//ysum -= elevationmean;
//x0sum -= elevationmean;
//y0sum -= elevationmean;
//x2sum -= elevationmean;
//y2sum -= elevationmean;
//xysum -= elevationmean;

double nsridge = x0sum - x2sum; // centerline > periphery
double ewridge = y0sum - y2sum; // centerline > periphery

if (terrain_type.Contains("valley"))
Console.WriteLine("xsum = " + xsum.ToString());
Console.WriteLine("ysum = " + ysum.ToString());
Console.WriteLine("x0sum = " + x0sum.ToString());
Console.WriteLine("y0sum = " + y0sum.ToString());
Console.WriteLine("x2sum = " + x2sum.ToString());
Console.WriteLine("y2sum = " + y2sum.ToString());
Console.WriteLine("xysum = " + xysum.ToString());
if ((nsridge < -triggerdiff) && (ewridge > -triggerdiff / 2))
terrain_type += "NS.."; //North-south valley
else if ((ewridge < -triggerdiff) && (nsridge > -triggerdiff / 2))
terrain_type += "EW..";
else if (xysum > triggerdiff)
terrain_type += "SWNE";
else if (xysum < -triggerdiff)
terrain_type += "SENW";
//Console.WriteLine(terrain_type);
//Console.ReadLine();
else if (terrain_type.Contains("peak"))
if ((nsridge > triggerdiff) && (ewridge < triggerdiff / 2))
terrain_type += "NS.."; //North-south ridge
else if ((ewridge > triggerdiff) && (nsridge < triggerdiff / 2))
terrain_type += "EW..";
else if (xysum > triggerdiff)
terrain_type += "SENW";
else if (xysum < -triggerdiff)
terrain_type += "SWNE";
else if ( Math.Abs(elevationmean - elevation) < triggerdiff/2 )
if (Math.Abs(xsum) > 3*Math.Abs(ysum)+triggerdiff/5)
if (xsum > 0)
terrain_type += "|slope E"; //upwards to the East
else
terrain_type += "|slope W";
if (Math.Abs(xsum) > triggerdiff)
terrain_type += " steep";
else if (Math.Abs(ysum) > 3 * Math.Abs(xsum) + triggerdiff / 5)
if (ysum > 0)
terrain_type += "|slope N";
else
terrain_type += "|slope S";
if (Math.Abs(ysum) > triggerdiff+100)
terrain_type += " steep";

return terrain_type;

public static string get_terrain_type(int gnid,double radius)
//List farlist = getneighbors(gnid, 20.0);
//return get_terrain_type2(farlist,gnid);
return get_terrain_type3(gnid,radius);

public static string get_terrain_type_island(int gnid)
string terrain_type = "unknown";

Console.WriteLine("get_terrain_type_island");

//int n = 0;
int nelev = 0;
//int nocean = 0;
double elevationsum = 0.0;
double elevationvar = 0.0;
double elevationsquare = 0.0;
double elevationmean = 0.0;
double elevationmax = 0.0;
//double r2ocean = 9999.9;
//int oceanmindir = -1;

int[,] map = get_3x3map(gndict[gnid].latitude, gndict[gnid].longitude);
int mapsize = map.GetLength(0);

int x0 = get_x_pixel(gndict[gnid].longitude, gndict[gnid].longitude);
int y0 = get_y_pixel(gndict[gnid].latitude, gndict[gnid].latitude);

if (gndict[gnid].elevation <= 0)
gndict[gnid].elevation = map[x0, y0];
else if (statisticsonly)
elevdiffhist.Add(1.0 * (gndict[gnid].elevation - map[x0, y0]));

byte[,] fillmap = new byte[mapsize, mapsize];

for (int x = 0; x < mapsize; x++)
for (int y = 0; y < mapsize; y++)
fillmap[x, y] = 1;

floodfill(ref fillmap, ref map, x0,y0, 0, 0, false);

if (fillmap[0, 0] == 3) //fill failure
return terrain_type;

double scale = Math.Cos(gndict[gnid].latitude * 3.1416 / 180);
//double pixkmx = scale * 40000 / (360 * 1200);
//double pixkmy = 40000.0 / (360.0 * 1200.0);

double[] elevdirsum = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
double[] elevdirmean = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
int[] nelevdir = { 0, 0, 0, 0, 0, 0, 0, 0 };
int[] noceandir = { 0, 0, 0, 0, 0, 0, 0, 0 };

//int r = Convert.ToInt32(radius / pixkmx);
//double r2max = radius / pixkmy * radius / pixkmy;

for (int x = 0; x < mapsize; x++)
for (int y = 0; y < mapsize; y++)
if (fillmap[x, y] == 2)
if (map[x, y] != 32768) //bad pixel
int dir = get_pix_direction(x, y, x0, y0, scale);
nelev++;
elevationsum += map[x, y];
elevationsquare += map[x, y] * map[x, y];
if (map[x, y] > elevationmax)
elevationmax = map[x, y];
if (dir > 0)
nelevdir[dir - 1]++;
elevdirsum[dir - 1] += map[x, y];

if (nelev > 10)
elevationmean = elevationsum / nelev;
elevationvar = elevationsquare / nelev - elevationmean * elevationmean;

Console.WriteLine("elevation mean, var = " + elevationmean.ToString() + ", " + elevationvar.ToString());

if (statisticsonly)
evarhist.Add(elevationvar);
if (gndict[gnid].elevation > 0)
evarhist.Add(1.0 * (gndict[gnid].elevation - map[x0, y0]));

terrain_type = classify_terrain(elevationvar, elevationmean);

// 1
// 7 5
// 3 4
// 8 6
// 2

int ndir = 0;
if (nelev > 20)
for (int i = 0; i < 8; i++)
if (nelevdir[i] > 0)
elevdirmean[i] = elevdirsum[i] / nelevdir[i];
ndir++;
else
elevdirmean[i] = -99999.0;

//int[] getdircoord(int dir)

if (statisticsonly)
ndirhist.Add(ndir);

if (elevationmax > 0)
terrain_type += "|elevation_max " + elevationmax.ToString();

if (statisticsonly)
Console.WriteLine(gndict[gnid].Name + ": " + terrain_type);
terrainhist.Add(terrain_type);
return terrain_type;

public static int get_direction_from_NSEW(string NSEW)
switch (NSEW.Trim())
case "N.":
return 1;
case "S.":
return 2;
case ".W":
return 3;
case ".E":
return 4;
case "NE":
return 5;
case "SE":
return 6;
case "NW":
return 7;
case "SW":
return 8;
default:
return -1;

public static string get_NSEW_from_xysum(int xsum, int ysum)
string rs = "";
if (xsum > 0)
if (ysum > 0)
rs = " NE";
else if (ysum < 0)
rs = " SE";
else
rs = " .E";
else if (xsum < 0)
if (ysum > 0)
rs = " NW";
else if (ysum < 0)
rs = " SW";
else
rs = " .W";
else if (ysum > 0)
rs = " N.";
else if (ysum < 0)
rs = " S.";
else
rs = " C.";
return rs;

public static string terrain_label(string terr)
string rt = "";
if (terr.Contains("flat"))
if (terr.Contains("high"))
rt = mp(183, null);
else if (terr.Contains("very "))
rt = mp(182, null);
else
rt = mp(109, null);
else if (terr.Contains("hilly"))
if (terr.Contains("somewhat"))
rt = mp(184, null);
else
rt = mp(111, null);
else if (terr.Contains("high-mountains"))
rt = mp(112, null);
else if (terr.Contains("low-mountains"))
rt = mp(185, null);
else if (terr.Contains("mountains"))
rt = mp(110, null);
else
rt = mp(186,null);
return rt;

public static bool is_height(string fcode)
if (fcode == "MTS")
return false;
else if (fcode == "HLLS")
return false;
else if (fcode == "NTKS")
return false;
else if (fcode == "PKS")
return false;
else if (categorydict[fcode] == "mountains")
return true;
else if (categorydict[fcode] == "hills")
return true;
else if (categorydict[fcode] == "volcanoes")
return true;
else
return false;

public static int imp_mountainpart(string fcode)
switch (fcode)
case "SPUR":
case "PROM":
return 197;
case "BNCH":
case "CLF":
case "RKFL":
case "SLID":
case "TAL":
case "CRQ":
case "CRQS":
return 203;
default:
return -1;

public static bool human_touched(Page p, Site site) //determines if an article has been edited by a human user with account (not ip or bot)
string xmlSrc;
bool ht = false;
try
xmlSrc = site.PostDataAndGetResult(site.address + "/w/api.php", "action=query&format=xml&prop=revisions&titles=" + HttpUtility.UrlEncode(p.title) + "&rvlimit=20&rvprop=user");
catch (WebException e)
string message = e.Message;
Console.Error.WriteLine(message);
return true;

XmlDocument xd = new XmlDocument();
xd.LoadXml(xmlSrc);

XmlNodeList elemlist = xd.GetElementsByTagName("rev");

Console.WriteLine("elemlist.Count = " + elemlist.Count);
//Console.WriteLine(xmlSrc);

foreach (XmlNode ee in elemlist)

try

string username = ee.Attributes.GetNamedItem("user").Value;
Console.WriteLine(username);
if (!username.ToLower().Contains("bot") && (get_alphabet(username) != "none"))
ht = true;
break;

catch (NullReferenceException e)
string message = e.Message;
Console.Error.WriteLine(message);

return ht;

public static string terrain_text(string terrain_type, int gnid)
if (terrain_type == "")
return "";
if (terrain_type == "unknown")
return "";
string rt = "";
string[] p98 = { gndict[gnid].Name_ml };

string[] words = terrain_type.Split('|');
string main_terrain = words[0];
Dictionary terrsector = new Dictionary();
List maintype = new List();
maintype.Add("flat");
maintype.Add("hilly");
maintype.Add("mountains");
int nsector = 0;
string centralterrain = "";
string majorterrain = "";
string minorterrain = "";
foreach (string w in words)
if ((w.IndexOf("dir") == 0) || (w.IndexOf("central") == 0))
foreach (string ttype in maintype)
if (w.Contains(ttype))
if (!terrsector.ContainsKey(ttype))
terrsector.Add(ttype, 0);
terrsector[ttype]++;
nsector++;
if (w.IndexOf("central") == 0)
centralterrain = ttype;

bool allsame = false;
bool varied = false;
bool singlediff = false;
int tmax = -1;
string tmaxtype = "";
string tmintype = "";
//string dirterrain = "";
if (terrsector.Count <= 1)
allsame = true;
else if ( terrsector.Count == 2)
foreach (string ttype in terrsector.Keys)
if (terrsector[ttype] > tmax)
tmax = terrsector[ttype];
tmaxtype = ttype;
if (2 * tmax >= nsector) //at least half are same type
int xsum = 0;
int ysum = 0;
foreach (string ttype in terrsector.Keys)
if (ttype != tmaxtype)
tmintype = ttype;
if (nsector - tmax == 1) //a single sector different
singlediff = true;
foreach (string w in words)
if ((w.IndexOf("dir") == 0) && (w.Contains(tmintype)))
int i = tryconvert(w.Substring(3, 1));
int[] cdir = getdircoord(i);
xsum += cdir[0];
ysum += cdir[1];
minorterrain = tmintype + get_NSEW_from_xysum(xsum, ysum);
majorterrain = tmaxtype;
else //minority more than a single sector
xsum = 0;
ysum = 0;
foreach (string w in words)
if ((w.IndexOf("dir") == 0) && (w.Contains(tmaxtype)))
int i = tryconvert(w.Substring(3, 1));
int[] cdir = getdircoord(i);
xsum += cdir[0];
ysum += cdir[1];
string major_NSEW = get_NSEW_from_xysum(xsum, ysum);
int majordir = get_direction_from_NSEW(major_NSEW);
if (majordir <= 0)
varied = true;
main_terrain = "mixed";
else
bool rightmajor = false;
foreach (string w in words)
if (w.Contains("dir" + majordir.ToString()))
if (w.Contains(tmaxtype))
rightmajor = true;
if (!rightmajor)
varied = true;
main_terrain = "mixed";
else
majorterrain = tmaxtype + major_NSEW;
xsum = 0;
ysum = 0;
foreach (string w in words)
if ((w.IndexOf("dir") == 0) && (w.Contains(tmintype)))
int i = tryconvert(w.Substring(3, 1));
int[] cdir = getdircoord(i);
xsum += cdir[0];
ysum += cdir[1];
minorterrain = tmintype + get_NSEW_from_xysum(xsum, ysum);
else
varied = true;
main_terrain = "mixed";
else
varied = true;
main_terrain = "mixed";

Console.WriteLine("majorterrain=" + majorterrain);
Console.WriteLine("minorterrain=" + minorterrain);

//terrain header:

bool peakvalley = true; //true if it should be written that something is on a peak or in a valley

if (categorydict[gndict[gnid].featurecode] == "peninsulas")
rt = mp(170, p98) + " "; //terrain landwards from a peninsula
peakvalley = false;
else if (categorydict[gndict[gnid].featurecode] == "islands")
rt = mp(194, p98) + " "; //terrain ON an island
peakvalley = false;
else if (featurepointdict[gndict[gnid].featurecode])
rt = mp(98, p98) + " "; //terrain around a point
peakvalley = !is_height(gndict[gnid].featurecode);

else
rt = mp(141, p98) + " "; //terrain in an area
peakvalley = false;

//terrain label:

if (allsame)
if (terrain_type.Contains("peak") || terrain_type.Contains("valley")) //add "mostly" if peak or valley. Sounds funny if combined with "flat" otherwise.
rt += mp(187, null) + " ";
rt += terrain_label(main_terrain); //main terrain
else if (singlediff)
rt += mp(187, null) + " " + terrain_label(tmaxtype); //mostly
if (minorterrain.Contains(centralterrain))
rt += ", " + mp(188, null) + " " + terrain_label(centralterrain);
else
string NSEW = minorterrain.Replace(tmintype, "").Trim();
int dir = get_direction_from_NSEW(NSEW);
if (dir > 0)
string[] p189 = new string[] { mp(120 + dir, null) };
rt += ", " + mp(189, p189) + " " + terrain_label(tmintype);
else if (varied)
rt += terrain_label("mixed");
else
string major_NSEW = majorterrain.Replace(tmaxtype, "").Trim();
string minor_NSEW = minorterrain.Replace(tmintype, "").Trim();
int majordir = get_direction_from_NSEW(major_NSEW);
int minordir = get_direction_from_NSEW(minor_NSEW);

if (majordir <= 0)
rt += terrain_label("mixed");
else
rt += terrain_label(majorterrain) + " " + mp(120 + majordir, null); //main terrain

if (minordir > 0)
string[] p189 = new string[] { mp(120 + minordir, null) };
rt += ", " + mp(189, p189) + " " + terrain_label(minorterrain);

//coast, peak/valley:

if (featurepointdict[gndict[gnid].featurecode])

foreach (string w in words)
if (w.Contains("ocean"))
if (w.Contains("coast"))
string NSEW = w.Substring(w.IndexOf("coast") + 6, 2);
int dir = get_direction_from_NSEW(NSEW);
if (dir > 0)
if (!String.IsNullOrEmpty(rt))
rt += ". ";
string[] p144 = new string[2] { gndict[gnid].Name_ml, mp(120 + dir, null) };
rt += initialcap(mp(144, p144));
else if (w.Contains("bay"))
string NSEW = w.Substring(w.IndexOf("bay") + 4, 1);
int dir = tryconvert(NSEW);
if (dir > 0)
if (!String.IsNullOrEmpty(rt))
rt += ". ";
string[] p144 = new string[2] { gndict[gnid].Name_ml, mp(120 + dir, null) };
rt += initialcap(mp(145, null) + " " + mp(144, p144));
else if ((w.Contains("peak")) || w.Contains("valley"))
string[] p205 = new string[1] { get_nsew(w) };
if (peakvalley || !String.IsNullOrEmpty(p205[0]))
if (w.Contains("peak"))
if (!String.IsNullOrEmpty(rt))
rt += ". ";

rt += mp(154, p98);

if (!String.IsNullOrEmpty(p205[0]))
rt += " " + mp(205, p205);

else if (w.Contains("valley"))
//if ((nsridge < -triggerdiff) && (ewridge > -triggerdiff / 2))
// terrain_type += "NS.."; //North-south valley
//else if ((ewridge < -triggerdiff) && (nsridge > -triggerdiff / 2))
// terrain_type += "EW..";
//else if (xysum > triggerdiff)
// terrain_type += "SWNE";
//else if (xysum < -triggerdiff)
// terrain_type += "SENW";

if (!String.IsNullOrEmpty(rt))
rt += ". ";

rt += mp(149, p98);
//string[] p205 = new string[1] { get_nsew(w) };

if (!String.IsNullOrEmpty(p205[0]))
rt += " " + mp(205, p205);
else if (w.Contains("slope"))
if (!String.IsNullOrEmpty(rt) && (allsame || varied))
rt += ", " + mp(147, null);
else
if (!String.IsNullOrEmpty(rt))
rt += ". ";
rt += mp(146, p98);
if (w.Contains("steep"))
rt += " " + mp(148, null);
if (w.Contains("N"))
rt += " " + mp(122, null); //reverse because NSEW-coding is upwards and text should be downwards slope
else if (w.Contains("S"))
rt += " " + mp(121, null);
else if (w.Contains("E"))
rt += " " + mp(123, null);
else if (w.Contains("W"))
rt += " " + mp(124, null);

if ( !String.IsNullOrEmpty(rt))
rt += ".";
return rt;

public static string get_nsew(double angle) //compass angle in radians. East = 0.
double ang = angle;
if (ang < 0)
ang += Math.PI;
ang *= 180.0/Math.PI; //convert to degrees

if ((ang < 25) || (ang > 155))
return get_nsew("EW..");
else if ((ang >= 25) && (ang <= 65))
return get_nsew("SWNE");
else if ((ang > 65) && (ang < 115))
return get_nsew("NS..");
else if ((ang >= 115) && (ang <= 155))
return get_nsew("SENW");
else
return "";

public static string get_nsew(string nsew)
string rt = "";
if (nsew.Contains("NS.."))
rt = mp(150, null);
else if (nsew.Contains("EW.."))
rt = mp(151, null);
else if (nsew.Contains("SWNE"))
rt = mp(152, null);
else if (nsew.Contains("SENW"))
rt = mp(153, null);
return rt;

public static string get_overrep(double lat, double lon,double nbradius)
//double nbradius = 20.0;
List farlist = getneighbors(lat,lon, nbradius);

int nnb = 0;
Dictionary nbcount = new Dictionary();

foreach (int nb in farlist)
if (!gndict.ContainsKey(nb))
continue;

if (catnormdict.ContainsKey(categorydict[gndict[nb].featurecode]))
nnb++;

if (!nbcount.ContainsKey(categorydict[gndict[nb].featurecode]))
nbcount.Add(categorydict[gndict[nb].featurecode], 0);
nbcount[categorydict[gndict[nb].featurecode]]++;

Console.WriteLine("nnb = " + nnb.ToString());

List overrep = new List();
//int nbsum = 0;

foreach (string scat in nbcount.Keys)
//nbsum += nbcount[scat];
//Console.WriteLine(scat + ": " + (nbcount[scat] / (1.0*nnb)).ToString("F", culture) + " (" + catnormdict[scat].ToString("F", culture) + ")");
if ((nbcount[scat] > 3 * catnormdict[scat]*nnb) && (nbcount[scat] > (catnormdict[scat] * nnb + 5)))
Console.WriteLine("Overrepresented! " + scat);
overrep.Add(categoryml[scat]);
foverrephist.Add(scat);
//Console.WriteLine("nbsum = " + nbsum);

string overlist = "";
if (overrep.Count > 0)
int noo = 0;
foreach (string oo in overrep)
noo++;
if (noo > 1)
if (noo == overrep.Count)
overlist += mp(97, null);
else
overlist += ",";
overlist += " " + oo;

return overlist.Trim();

public static string get_overrep(int gnid,double nbradius)

string overlist = get_overrep(gndict[gnid].latitude, gndict[gnid].longitude,nbradius);
Console.WriteLine("overlist = " + overlist);
if (String.IsNullOrEmpty(overlist))
return "";

string[] p133 = { gndict[gnid].Name_ml, overlist };
string[] p138 = { nbradius.ToString("F0") };
string text = " " + mp(133, p133) + addnote(mp(138, p138) + geonameref(gnid));
return text;

public static double[] get_nearhigh(int gnid, List farlist, double radius, double minradius, out int nearhigh, out int altitude)
nearhigh = -1;
altitude = 0;
double[] latlong = { 9999.9, 9999.9 };

if (!gndict.ContainsKey(gnid))
return latlong;

double emax = 0.0;
double emin = 9999.9;
int nmax = 0;
int nmin = 0;

double maxelevation = 0.0;
double elevation = 0.0;

//double minradius = 1.0;

foreach (int nb in farlist)
if (!gndict.ContainsKey(nb))
continue;

if (gndict[nb].elevation > 0)
if (gndict[nb].elevation > emax)
emax = gndict[nb].elevation;
nmax = nb;
if (gndict[nb].elevation < emin)
emin = gndict[nb].elevation;
nmin = nb;

if ((gndict[gnid].elevation > 0) && (gndict[gnid].elevation < gndict[nb].elevation))
double dist = get_distance(gnid, nb);
if (dist > minradius)
elevation = (gndict[nb].elevation - gndict[gnid].elevation) / dist;
if (elevation > maxelevation)
maxelevation = elevation;
nearhigh = nb;

int[,] map = get_3x3map(gndict[gnid].latitude, gndict[gnid].longitude);
int mapsize = map.GetLength(0);

int x0 = get_x_pixel(gndict[gnid].longitude, gndict[gnid].longitude);
int y0 = get_y_pixel(gndict[gnid].latitude, gndict[gnid].latitude);

double scale = Math.Cos(gndict[gnid].latitude * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
double pixkmy = 40000.0 / (360.0 * 1200.0);

double[] elevdirsum = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
double[] elevdirmean = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
int[] nelevdir = { 0, 0, 0, 0, 0, 0, 0, 0 };
int[] noceandir = { 0, 0, 0, 0, 0, 0, 0, 0 };

int r = Convert.ToInt32(radius / pixkmx);
double r2max = radius / pixkmy * radius / pixkmy;
double r2min = minradius / pixkmy * minradius / pixkmy;
double maxelevationdem = 0;

for (int x = x0 - r; x < x0 + r; x++)
if ((x > 0) && (x < mapsize))
for (int y = y0 - r; y < y0 + r; y++)
if ((y > 0) && (y < mapsize))
double r2 = scale * scale * (x - x0) * (x - x0) + (y - y0) * (y - y0);
if ((r2 < r2max) && (r2 > r2min ))
if (map[x, y] != 0) //not ocean
if (map[x, y] != 32768) //bad pixel
double dist = Math.Sqrt(r2) * pixkmy;
elevation = (map[x,y] - gndict[gnid].elevation) / dist;
if (elevation > maxelevationdem)
maxelevationdem = elevation;
double one1200 = 1.0 / 1200.0;
double dlon = (x - x0) * one1200;
double dlat = -(y - y0) * one1200; //reverse sign because higher pixel number is lower latitude
latlong[0] = gndict[gnid].latitude + dlat;
latlong[1] = gndict[gnid].longitude + dlon;
altitude = map[x, y];

if (maxelevationdem > 1.1 * maxelevation)
nearhigh = -1;
else if ( gndict.ContainsKey(nearhigh))
latlong[0] = gndict[nearhigh].latitude;
latlong[1] = gndict[nearhigh].longitude;
altitude = gndict[nearhigh].elevation;

return latlong;

public static double[] get_highest(int gnid, double radius, out int altitude)
//Find highest DEM point within radius
altitude = 0;
double[] latlong = { 9999.9, 9999.9 };

if (!gndict.ContainsKey(gnid))
return latlong;

double elevation = 0.0;

int[,] map = get_3x3map(gndict[gnid].latitude, gndict[gnid].longitude);
int mapsize = map.GetLength(0);

int x0 = get_x_pixel(gndict[gnid].longitude, gndict[gnid].longitude);
int y0 = get_y_pixel(gndict[gnid].latitude, gndict[gnid].latitude);

double scale = Math.Cos(gndict[gnid].latitude * 3.1416 / 180);
double pixkmx = scale * 40000 / (360 * 1200);
double pixkmy = 40000.0 / (360.0 * 1200.0);

double[] elevdirsum = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
double[] elevdirmean = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
int[] nelevdir = { 0, 0, 0, 0, 0, 0, 0, 0 };
int[] noceandir = { 0, 0, 0, 0, 0, 0, 0, 0 };

int r = Convert.ToInt32(radius / pixkmx);
double r2max = radius / pixkmy * radius / pixkmy;
//double r2min = minradius / pixkmy * minradius / pixkmy;
double maxelevationdem = 0;

Console.WriteLine("pixkmx = " + pixkmx.ToString());
Console.WriteLine("r = " + r.ToString());
Console.WriteLine("r2max = " + r2max.ToString());

for (int x = x0 - r; x < x0 + r; x++)
if ((x > 0) && (x < mapsize))
for (int y = y0 - r; y < y0 + r; y++)
if ((y > 0) && (y < mapsize))
double r2 = scale * scale * (x - x0) * (x - x0) + (y - y0) * (y - y0);
if (r2 < r2max)
if (map[x, y] != 0) //not ocean
if (map[x, y] != 32768) //bad pixel
//double dist = Math.Sqrt(r2) * pixkmy;
elevation = map[x, y];
if (elevation > maxelevationdem)
maxelevationdem = elevation;
double one1200 = 1.0 / 1200.0;
double dlon = (x - x0) * one1200;
double dlat = -(y - y0) * one1200; //reverse sign because higher pixel number is lower latitude
latlong[0] = gndict[gnid].latitude + dlat;
latlong[1] = gndict[gnid].longitude + dlon;
altitude = map[x, y];

return latlong;

public static string make_town(int gnid)
string text = "";

//List nearlist = getneighbors(gnid, 10.0);
double nbradius = 20.0;
List farlist = getneighbors(gnid, nbradius);

double ttradius = 10.0;
string terrain_type = get_terrain_type3(gnid,ttradius);

string[] p158 = {ttradius.ToString("F0")};
text += "\n\n" + terrain_text(terrain_type, gnid) + addnote(mp(158, p158) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

//Dictionary nbcount = new Dictionary();
//Dictionary catcount = new Dictionary();

//double nnb = 0;
long popmax = gndict[gnid].population;
long pop3 = 3 * gndict[gnid].population;
long totalpop = gndict[gnid].population;
int npopmax = 0;
int npopnear = 0;
int nppl = 0;
double popmindist = 9999.9;

foreach (int nb in farlist)
if (!gndict.ContainsKey(nb))
continue;

//if (catnormdict.ContainsKey(categorydict[gndict[nb].featurecode]))
//{
// nnb += 1;

// if (!nbcount.ContainsKey(categorydict[gndict[nb].featurecode]))
// nbcount.Add(categorydict[gndict[nb].featurecode], 0);
// nbcount[categorydict[gndict[nb].featurecode]]++;
//}

if (gndict[nb].featureclass == 'P')
nppl++;
totalpop += gndict[nb].population;

if (gndict[nb].population > popmax)
popmax = gndict[nb].population;
npopmax = nb;
if (gndict[nb].population > pop3)
double dist = get_distance(gnid, nb);
if (dist < popmindist)
popmindist = dist;
npopnear = nb;

string[] p113 = { gndict[gnid].Name_ml };

int nhalt = 0;
int nearhigh = -1;
double[] nhlatlong = get_nearhigh(gnid, farlist, nbradius, 1.0, out nearhigh, out nhalt);

if (nearhigh > 0)
string[] p116 = { makegnidlink(nearhigh), fnum(gndict[nearhigh].elevation) };
text += " " + mp(116, p116) + ", " + fnum(get_distance(gnid, nearhigh)) + " km " + mp(100 + get_direction(gnid, nearhigh), null) + " " + gndict[gnid].Name_ml + "." + addnote(mp(137, null) + geonameref(gnid));
else if (gndict[gnid].elevation > nhalt)
text += " " + mp(165, p113) + ".";
else if ( nhlatlong[0]+nhlatlong[1] < 720.0 )
string[] p172 = { fnum(nhalt) };
text += " " + mp(172, p172) + ", " + fnum(get_distance_latlong(gndict[gnid].latitude, gndict[gnid].longitude, nhlatlong[0], nhlatlong[1])) + " km " + mp(100 + get_direction_latlong(gndict[gnid].latitude, gndict[gnid].longitude, nhlatlong[0], nhlatlong[1]), null) + " " + gndict[gnid].Name_ml + "." + addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200,null));

//double popdensity = totalpop / (3.14 * nbradius * nbradius);
//if (popdensity < 10.0)
//{
// text += " " + mp(117, null) + ".";
//}
//else if (popdensity > 100.0)
//{
// text += " " + mp(118, null) + ".";
//}
//else if (popdensity > 400.0)
//{
// text += " " + mp(119, null) + ".";
//}

text += make_popdensity(gnid);

if (nppl == 0)
if (getghostneighbors(gndict[gnid].latitude, gndict[gnid].longitude, 0.5 * nbradius))
text += " " + mp(115, null) + ".";
else if (npopmax > 0)
int nbig = npopmax;
if (npopnear > 0)
if (gndict[nbig].population < 3 * gndict[npopnear].population)
nbig = npopnear;
string[] p114 = { makegnidlink(nbig) };
text += " " + mp(114, p114) + ", " + fnum(get_distance(gnid, nbig)) + " km " + mp(100 + get_direction(gnid, nbig), null) + " " + gndict[gnid].Name_ml + ".";
else
text += " " + mp(113, p113) + ".";

text += make_landcover(gnid);

text += get_overrep(gnid,20.0);

//public static double get_distance(int gnidfrom, int gnidto)
//public static int get_direction(int gnidfrom, int gnidto)

//public static Dictionary categorydict = new Dictionary(); //from featurecode to category
//public static Dictionary parentcategory = new Dictionary(); //from category to parent category
//public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang

return text;

public static string viewfinder_ref()
if (makelang == "sv")
return "{{Webbref |url= {{Viewfinderlänk}}|titel= Viewfinder Panoramas Digital elevation Model|hämtdatum= 2015-06-21|format= |verk= }}";
else
return "{{Cite web |url= {{Viewfinderlink}}|title= Viewfinder Panoramas Digital elevation Model|date= 2015-06-21|format= }}";

public static string make_point(int gnid) //make any pointlike place, from mountains to oases
string text = "";

//List nearlist = getneighbors(gnid, 10.0);
double nbradius = 20.0;
List farlist = getneighbors(gnid, nbradius);

if (!(categorydict[gndict[gnid].featurecode] == "seabed") && !(categorydict[gndict[gnid].featurecode] == "navigation") && !(categorydict[gndict[gnid].featurecode] == "bays") && !(categorydict[gndict[gnid].featurecode] == "reefs"))
double ttradius = 10.0;
string terrain_type = get_terrain_type3(gnid, ttradius);

string[] p158 = { ttradius.ToString("F0") };
text += "\n\n" + terrain_text(terrain_type, gnid) + addnote(mp(158, p158) + addref("vp", viewfinder_ref()) + " " + mp(200, null));
//Dictionary nbcount = new Dictionary();
//Dictionary catcount = new Dictionary();

//double nnb = 0;
long popmax = 0;
long pop3 = 3000;
long totalpop = 0;
int npopmax = 0;
int npopnear = 0;
int nppl = 0;
int nbpop = -1;
double popmindist = 9999.9;

foreach (int nb in farlist)
if (!gndict.ContainsKey(nb))
continue;

//if (catnormdict.ContainsKey(categorydict[gndict[nb].featurecode]))
//{
// nnb += 1;

// if (!nbcount.ContainsKey(categorydict[gndict[nb].featurecode]))
// nbcount.Add(categorydict[gndict[nb].featurecode], 0);
// nbcount[categorydict[gndict[nb].featurecode]]++;
//}

if ((gndict[nb].featureclass == 'P')||(gndict[nb].featurecode == "STNB"))
nppl++;
nbpop = nb;
totalpop += gndict[nb].population;

if (gndict[nb].population > popmax)
popmax = gndict[nb].population;
npopmax = nb;
if (gndict[nb].population > pop3)
double dist = get_distance(gnid, nb);
if (dist < popmindist)
popmindist = dist;
npopnear = nb;

string[] p113 = { gndict[gnid].Name_ml };

int nhalt = 0;
int nearhigh = -1;
double[] nhlatlong = get_nearhigh(gnid, farlist, nbradius, 1.0, out nearhigh, out nhalt);

if (nearhigh > 0)
string[] p116 = { makegnidlink(nearhigh), fnum(gndict[nearhigh].elevation) };
text += " " + mp(116, p116) + ", " + fnum(get_distance(gnid, nearhigh)) + " km " + mp(100 + get_direction(gnid, nearhigh), null) + " " + gndict[gnid].Name_ml + "." + addnote(mp(137, null) + geonameref(gnid));
else if (gndict[gnid].elevation > nhalt)
text += " " + mp(165, p113) + ".";
else if (nhlatlong[0] + nhlatlong[1] < 720.0)
string[] p172 = { fnum(nhalt) };
text += " " + mp(172, p172) + ", " + fnum(get_distance_latlong(gndict[gnid].latitude, gndict[gnid].longitude, nhlatlong[0], nhlatlong[1])) + " km " + mp(100 + get_direction_latlong(gndict[gnid].latitude, gndict[gnid].longitude, nhlatlong[0], nhlatlong[1]), null) + " " + gndict[gnid].Name_ml + "." + addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));
Console.WriteLine("nhlatlong = " + nhlatlong[0].ToString() + " " + nhlatlong[1].ToString());

//double popdensity = totalpop / (3.14 * nbradius * nbradius);
//if (popdensity < 10.0)
//{
// text += " " + mp(117, null) + ".";
//}
//else if (popdensity > 100.0)
//{
// text += " " + mp(118, null) + ".";
//}
//else if (popdensity > 400.0)
//{
// text += " " + mp(119, null) + ".";
//}

text += make_popdensity(gnid);

if (nppl == 0)
if (!getghostneighbors(gndict[gnid].latitude, gndict[gnid].longitude, 0.5*nbradius))
text += " " + mp(169, null) + ".";
else if (npopmax > 0)
int nbig = npopmax;
if (npopnear > 0)
if (gndict[nbig].population < 3 * gndict[npopnear].population)
nbig = npopnear;
string[] p114 = { makegnidlink(nbig) };
text += " " + mp(114, p114) + ", " + fnum(get_distance(gnid, nbig)) + " km " + mp(100 + get_direction(gnid, nbig), null) + " " + gndict[gnid].Name_ml + ".";
else if (nbpop > 0)
string[] p212 = { makegnidlink(nbpop) };
text += " " + mp(212, p212) + ", " + fnum(get_distance(gnid, nbpop)) + " km " + mp(100 + get_direction(gnid, nbpop), null) + " " + gndict[gnid].Name_ml + ".";
else
Console.WriteLine("Should never come here. make_point ");
Console.ReadLine();

text += make_landcover(gnid);

int imp = imp_mountainpart(gndict[gnid].featurecode);
if ( imp > 0)
int mtgnid = attach_to_mountain(gnid);
if (gndict.ContainsKey(mtgnid))
text += " " + gndict[gnid].Name_ml + " " + mp(imp, null) + " " + makegnidlink(mtgnid) + "." + addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

if (categorydict[gndict[gnid].featurecode] == "passes")
int mtgnid1 = -1;
int mtgnid2 = -1;
if (between_mountains(gnid, out mtgnid1, out mtgnid2))
string[] p199 = { makegnidlink(mtgnid1), makegnidlink(mtgnid2) };
text += " " + mp(199, p199) + "." + addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

text += get_overrep(gnid, 20.0);

//public static double get_distance(int gnidfrom, int gnidto)
//public static int get_direction(int gnidfrom, int gnidto)

//public static Dictionary categorydict = new Dictionary(); //from featurecode to category
//public static Dictionary parentcategory = new Dictionary(); //from category to parent category
//public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang

return text;

public static string make_adm(int gnid)
string text = "";
Dictionary fc = new Dictionary();
fc.Add('H',0);
fc.Add('L',0);
fc.Add('A',0);
fc.Add('P',0);
fc.Add('S',0);
fc.Add('T',0);
fc.Add('U',0);
fc.Add('V',0);

string[] p79 = new string[1] { gndict[gnid].Name_ml };

//bordering:

if (wdid > 0)
List neighbors = get_wd_prop_idlist(propdict["borders"], currentxml);

if (neighbors.Count > 1)
text += " " + mp(96, p79);
int i = 0;
foreach (int wdnb in neighbors)
i++;
if (i == neighbors.Count)
text += mp(97, null);
else if (i > 1)
text += ",";
text += " " + get_name_from_wdid(wdnb);
text += ". ";
else if (neighbors.Count == 1)
text += " " + mp(96, p79);
text += " " + get_name_from_wdid(neighbors[0]);
text += ".";

//terrain type

if (gndict[gnid].area > 0)
double nbradius = Math.Sqrt(gndict[gnid].area);

string terrain_type = get_terrain_type3(gnid, nbradius);

//string[] p136 = { nbradius.ToString("F0") };
text += "\n\n" + terrain_text(terrain_type, gnid);
//if ( makelang == "sv" )
text += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

//administrative subdivisions:

if (gndict[gnid].children.Count > 1)
Console.WriteLine("Subdivisions from gnid-kids");
text += "\n\n" + mp(79, p79) + "\n";
foreach (int kid in gndict[gnid].children)
text += "* " + makegnidlink(kid) + "\n";
else if ( wdid > 0)

List kidlist = get_wd_kids(currentxml);
Console.WriteLine("Subdivisions from wikidata-kids, "+kidlist.Count.ToString());
if (kidlist.Count > 0)
text += "\n\n" + mp(79, p79) + "\n";
foreach (int kid in kidlist)
text += "* " + get_name_from_wdid(kid) + "\n";

//feature lists:
if (gndict[gnid].features.Count > 0)
foreach (int ff in gndict[gnid].features)
fc[gndict[ff].featureclass]++;
// H: vatten
// L: diverse människoskapade områden
// P: samhällen
// S: Byggnadsverk
// T: diverse naturfenomen; berg, dalar, öar...
// U: undervattensfenomen
// V: växtlighet; skogar, hedar...

SortedDictionary flist = new SortedDictionary();

if (fc['P'] > 0)
text += "\n\n" + mp(80, p79) + "\n";
foreach (int ff in gndict[gnid].features)
if (gndict[ff].featureclass == 'P')
long pop = gndict[ff].population;
while (flist.ContainsKey(pop))
pop++;
flist.Add(pop, ff);
string sorted = "";
foreach (int fpop in flist.Keys)
//sorted = "\n* " + makegnidlink(flist[fpop]) + " (" + fpop.ToString("N0",nfi) + " " + mp(81, null) + ")" + sorted; //With pop
sorted = "\n* " + makegnidlink(flist[fpop]) + sorted; //Without pop
text += sorted;

SortedDictionary fl2 = new SortedDictionary();

if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 10)
//public static Dictionary categorydict = new Dictionary(); //from featurecode to category
//public static Dictionary parentcategory = new Dictionary(); //from category to parent category
//public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang

text += "\n\n" + mp(91, p79) + "\n";

foreach (string cat in categoryml.Keys)
fl2.Clear();
foreach (int ff in gndict[gnid].features)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
if (categorydict[gndict[ff].featurecode] == cat)
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);

if (fl2.Count > 0)
text += "\n\n* " + initialcap(categoryml[cat]) + ":\n";
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n:* " + makegnidlink(fl2[fname]) + " (" + linkfeature(gndict[fl2[fname]].featurecode,fl2[fname]) + ")";
text += sorted;

else if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 0)
fl2.Clear();

text += "\n\n" + mp(91, p79) + "\n";
foreach (int ff in gndict[gnid].features)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n* " + makegnidlink(fl2[fname]) + " (" + linkfeature(gndict[fl2[fname]].featurecode, fl2[fname]) + ")";
text += sorted;

return text;

public static string make_island(int gnid, Page p)
string text = "";
Dictionary fc = new Dictionary();
fc.Add('H', 0);
fc.Add('L', 0);
fc.Add('A', 0);
fc.Add('P', 0);
fc.Add('S', 0);
fc.Add('T', 0);
fc.Add('U', 0);
fc.Add('V', 0);

string[] p79 = new string[1] { gndict[gnid].Name_ml };

//terrain type

if (gndict[gnid].area > 0)
//double nbradius = Math.Sqrt(gndict[gnid].area);

string terrain_type = get_terrain_type_island(gnid);

//string[] p136 = { nbradius.ToString("F0") };
string tt = terrain_text(terrain_type, gnid);
if (!String.IsNullOrEmpty(tt))
text += "\n\n" + tt;
//if (makelang == "sv")
text += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

string heightmarker = "|elevation_max";
if (terrain_type.Contains(heightmarker))
string elmax = terrain_type.Substring(terrain_type.IndexOf(heightmarker) + heightmarker.Length + 1);
int maxheight = tryconvert(elmax);
if (maxheight > gndict[gnid].elevation)
string[] p163 = { fnum(maxheight) };
text += " " + mp(163, p163);
p.SetTemplateParameter("geobox", "highest_elevation", elmax, true);

if ((islanddict.ContainsKey(gnid)) && ((islanddict[gnid].kmns + islanddict[gnid].kmew) > 1.0))
string[] p164 = { islanddict[gnid].kmns.ToString("F1", culture), islanddict[gnid].kmew.ToString("F1", culture) };
text += " " + mp(164, p164);
//if (makelang == "sv")
text += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

if (islanddict[gnid].kmew > 2 * islanddict[gnid].kmns)
p.SetTemplateParameter("geobox", "length", islanddict[gnid].kmew.ToString(), true);
p.SetTemplateParameter("geobox", "width", islanddict[gnid].kmns.ToString(), true);
p.SetTemplateParameter("geobox", "length_orientation", "EW", true);
p.SetTemplateParameter("geobox", "width_orientation", "NS", true);
else if (islanddict[gnid].kmns > 2 * islanddict[gnid].kmew)
p.SetTemplateParameter("geobox", "length", islanddict[gnid].kmns.ToString(), true);
p.SetTemplateParameter("geobox", "width", islanddict[gnid].kmew.ToString(), true);
p.SetTemplateParameter("geobox", "length_orientation", "NS", true);
p.SetTemplateParameter("geobox", "width_orientation", "EW", true);

if ( gndict[gnid].area < 300 )
text += " " + make_landcover(gnid);

//feature lists:
if (islanddict.ContainsKey(gnid))
if (islanddict[gnid].onisland.Count > 0)
foreach (int ff in islanddict[gnid].onisland)
if (!categorydict.ContainsKey(gndict[ff].featurecode))
Console.WriteLine(gndict[ff].featurecode + " missing in categorydict");
Console.ReadLine();
continue;
if (categorydict[gndict[ff].featurecode] != "islands") //Not island on island
fc[gndict[ff].featureclass]++;
// H: vatten
// L: diverse människoskapade områden
// P: samhällen
// S: Byggnadsverk
// T: diverse naturfenomen; berg, dalar, öar...
// U: undervattensfenomen
// V: växtlighet; skogar, hedar...

SortedDictionary flist = new SortedDictionary();

if (fc['P'] > 0)
text += "\n\n" + mp(159, p79) + "\n";
foreach (int ff in islanddict[gnid].onisland)
if (gndict[ff].featureclass == 'P')
long pop = gndict[ff].population;
while (flist.ContainsKey(pop))
pop++;
flist.Add(pop, ff);
string sorted = "";
foreach (int fpop in flist.Keys)
//sorted = "\n* " + makegnidlink(flist[fpop]) + " (" + fpop.ToString("N0",nfi) + " " + mp(81, null) + ")" + sorted;
sorted = "\n* " + makegnidlink(flist[fpop]) + sorted;
text += sorted;

SortedDictionary fl2 = new SortedDictionary();

if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 10)
//public static Dictionary categorydict = new Dictionary(); //from featurecode to category
//public static Dictionary parentcategory = new Dictionary(); //from category to parent category
//public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang

text += "\n\n" + mp(160, p79) + "\n";

foreach (string cat in categoryml.Keys)
if (cat == "islands") //not island on island
continue;

fl2.Clear();
foreach (int ff in islanddict[gnid].onisland)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
if (categorydict[gndict[ff].featurecode] == cat)
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);

if (fl2.Count > 0)
text += "\n* " + initialcap(categoryml[cat]) + ":\n";
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n** " + makegnidlink(fl2[fname]) + " (" + linkfeature(gndict[fl2[fname]].featurecode, fl2[fname]) + ")";
text += sorted;

else if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 0)
fl2.Clear();

text += "\n\n" + mp(160, p79) + "\n";
foreach (int ff in islanddict[gnid].onisland)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
if (categorydict[gndict[ff].featurecode] != "islands") //Not island on island
continue;
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n* " + makegnidlink(fl2[fname]) + " (" + linkfeature(gndict[fl2[fname]].featurecode, fl2[fname]) + ")";
text += sorted;

return text;

public static string make_range(int gnid, Page p)
string text = "";
Dictionary fc = new Dictionary();
fc.Add('H', 0);
fc.Add('L', 0);
fc.Add('A', 0);
fc.Add('P', 0);
fc.Add('S', 0);
fc.Add('T', 0);
fc.Add('U', 0);
fc.Add('V', 0);

if (rangedict.ContainsKey(gnid))
string[] p207 = new string[3] { gndict[gnid].Name_ml, fnum(rangedict[gnid].length), get_nsew(rangedict[gnid].angle) };

text += "\n\n" + mp(207, p207) + addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));
if (rangedict[gnid].maxheight > 0)
string[] p208 = new string[1] { fnum(rangedict[gnid].maxheight) };
text += " " + mp(208,p208);
else if ( gndict.ContainsKey(-rangedict[gnid].maxheight))
int hgnid = -rangedict[gnid].maxheight;
string[] p209 = new string[2] { makegnidlink(hgnid),fnum(gndict[hgnid].elevation) };
text += " " + mp(209, p209);

string[] p79 = new string[1] { gndict[gnid].Name_ml };

//feature lists:
if (rangedict[gnid].inrange.Count > 0)
foreach (int ff in rangedict[gnid].inrange)
fc[gndict[ff].featureclass]++;
// H: vatten
// L: diverse människoskapade områden
// P: samhällen
// S: Byggnadsverk
// T: diverse naturfenomen; berg, dalar, öar...
// U: undervattensfenomen
// V: växtlighet; skogar, hedar...

SortedDictionary fl2 = new SortedDictionary();

if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 0)

text += "\n\n" + mp(206, p79) + "\n";
foreach (int ff in rangedict[gnid].inrange)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n* " + makegnidlink(fl2[fname]);
text += sorted;

return text;

public static string make_lake(int gnid,Page p)
string text = "";
Dictionary fc = new Dictionary();
fc.Add('H', 0);
fc.Add('L', 0);
fc.Add('A', 0);
fc.Add('P', 0);
fc.Add('S', 0);
fc.Add('T', 0);
fc.Add('U', 0);
fc.Add('V', 0);

if (lakedict.ContainsKey(gnid))
if ((lakedict[gnid].kmns + lakedict[gnid].kmew < 5.0) && (lakedict[gnid].kmns + lakedict[gnid].kmew > 0))
int nhalt = 0;
int nearhigh = -1;
double nbradius = 20.0;
List farlist = getneighbors(gnid, nbradius);

double[] nhlatlong = get_nearhigh(gnid, farlist, nbradius, 1.0, out nearhigh, out nhalt);

if (nearhigh > 0)
string[] p116 = { makegnidlink(nearhigh), fnum(gndict[nearhigh].elevation) };
text += " " + mp(116, p116) + ", " + fnum(get_distance(gnid, nearhigh)) + " km " + mp(100 + get_direction(gnid, nearhigh), null) + " " + gndict[gnid].Name_ml + "." + addnote(mp(137, null) + geonameref(gnid));
else if (nhlatlong[0] + nhlatlong[1] < 720.0)
string[] p172 = { fnum(nhalt) };
text += " " + mp(172, p172) + ", " + fnum(get_distance_latlong(gndict[gnid].latitude, gndict[gnid].longitude, nhlatlong[0], nhlatlong[1])) + " km " + mp(100 + get_direction_latlong(gndict[gnid].latitude, gndict[gnid].longitude, nhlatlong[0], nhlatlong[1]), null) + " " + gndict[gnid].Name_ml + "." + addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

text += make_landcover(gnid);

string[] p79 = new string[1] { gndict[gnid].Name_ml };

if (lakedict[gnid].kmns + lakedict[gnid].kmew > 1.0)
string[] p164 = { lakedict[gnid].kmns.ToString("F1", culture), lakedict[gnid].kmew.ToString("F1", culture) };
text += " " + mp(164, p164);

if (lakedict[gnid].kmew > 2 * lakedict[gnid].kmns)
p.SetTemplateParameter("geobox", "length", lakedict[gnid].kmew.ToString(), true);
p.SetTemplateParameter("geobox", "width", lakedict[gnid].kmns.ToString(), true);
p.SetTemplateParameter("geobox", "length_orientation", "EW", true);
p.SetTemplateParameter("geobox", "width_orientation", "NS", true);
else if (lakedict[gnid].kmns > 2 * lakedict[gnid].kmew)
p.SetTemplateParameter("geobox", "length", lakedict[gnid].kmns.ToString(), true);
p.SetTemplateParameter("geobox", "width", lakedict[gnid].kmew.ToString(), true);
p.SetTemplateParameter("geobox", "length_orientation", "NS", true);
p.SetTemplateParameter("geobox", "width_orientation", "EW", true);

//feature lists:
if (lakedict[gnid].inlake.Count > 0)
text += "\n\n" + mp(91, p79) + "\n";
foreach (int il in lakedict[gnid].inlake)
text += "\n* " + makegnidlink(il) + " (" + linkfeature(gndict[il].featurecode, il) + ")";
text += "\n\n";

if (lakedict[gnid].atlake.Count > 0)
foreach (int ff in lakedict[gnid].atlake)
fc[gndict[ff].featureclass]++;
// H: vatten
// L: diverse människoskapade områden
// P: samhällen
// S: Byggnadsverk
// T: diverse naturfenomen; berg, dalar, öar...
// U: undervattensfenomen
// V: växtlighet; skogar, hedar...

SortedDictionary flist = new SortedDictionary();

if (fc['P'] > 0)
text += "\n\n" + mp(161, p79) + "\n";
foreach (int ff in lakedict[gnid].atlake)
if (gndict[ff].featureclass == 'P')
long pop = gndict[ff].population;
while (flist.ContainsKey(pop))
pop++;
flist.Add(pop, ff);
string sorted = "";
foreach (int fpop in flist.Keys)
sorted = "\n* " + makegnidlink(flist[fpop]) + " (" + fpop.ToString("N0", nfi) + " " + mp(81, null) + ")" + sorted;
text += sorted;

SortedDictionary fl2 = new SortedDictionary();

if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 10)
//public static Dictionary categorydict = new Dictionary(); //from featurecode to category
//public static Dictionary parentcategory = new Dictionary(); //from category to parent category
//public static Dictionary categoryml = new Dictionary(); //from category to category name in makelang

text += "\n\n" + mp(162, p79) + "\n";

foreach (string cat in categoryml.Keys)
fl2.Clear();
foreach (int ff in lakedict[gnid].atlake)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
if (categorydict[gndict[ff].featurecode] == cat)
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);

if (fl2.Count > 0)
text += "\n* " + initialcap(categoryml[cat]) + ":\n";
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n** " + makegnidlink(fl2[fname]) + " (" + linkfeature(gndict[fl2[fname]].featurecode, fl2[fname]) + ")";
text += sorted;

else if (fc['H'] + fc['T'] + fc['U'] + fc['V'] > 0)
fl2.Clear();

text += "\n\n" + mp(168, p79) + "\n";
foreach (int ff in lakedict[gnid].atlake)
if ((gndict[ff].featureclass == 'H') || (gndict[ff].featureclass == 'T') || (gndict[ff].featureclass == 'U') || (gndict[ff].featureclass == 'V'))
string ffname = gndict[ff].Name_ml;
while (fl2.ContainsKey(ffname))
ffname += " ";
fl2.Add(ffname, ff);
string sorted = "";
foreach (string fname in fl2.Keys)
sorted += "\n* " + makegnidlink(fl2[fname]) + " (" + linkfeature(gndict[fl2[fname]].featurecode, fl2[fname]) + ")";
text += sorted;

return text;

public static string geonameref(int gnid)

//string gn = "[http://www.geonames.org/gnidgnid/asciiascii.html " + gndict[gnid].Name + "] at [http://www.geonames.org/about.html Geonames.Org (cc-by)]; updated "+gndict[gnid].moddate;
string gn = "[{{" + mp(173, null) + "|gnid=" + gnid.ToString() + "|name=" + gndict[gnid].asciiname.ToLower().Replace(" ", "%20").Replace("\"", "%22") + "}} " + gndict[gnid].Name + "] "+mp(293,null)+" [{{Geonamesabout}} Geonames.org (cc-by)]; post " + mp(179, null) + " " + gndict[gnid].moddate + "; " + mp(180, null) + " " + dumpdate;
//gn = gn.Replace("gnidgnid", gnid.ToString());
//gn = gn.Replace("asciiascii", gndict[gnid].asciiname.ToLower().Replace(" ", "%20"));
return addref("gn" + gnid.ToString(), gn);

public static string nasaref()

string gn = "";
if (makelang == "sv")
gn = "{{webbref |url= http://neo.sci.gsfc.nasa.gov/dataset_index.php|titel= NASA Earth Observations Data Set Index|hämtdatum=30 januari 2016 |efternamn= |förnamn= |datum= |verk= |utgivare= NASA}}";
else
gn = "{{Cite web |url= http://neo.sci.gsfc.nasa.gov/dataset_index.php|title= NASA Earth Observations Data Set Index|access-date = 30 Enero 2016 |publisher= NASA}}";
//{{Webbref |url= |titel= |hämtdatum= |författare= |efternamn= |förnamn= |författarlänk= |efternamn2= |förnamn2= |datum= |år= |månad= |format= |verk= |utgivare= |sid= |språk= |doi= |arkivurl= |arkivdatum= |citat= |ref= }}
return addref("nasa", gn);

public static string nasapopref()

string gn = "";
if (makelang == "sv")
gn = "{{webbref |url= http://neo.sci.gsfc.nasa.gov/view.php?datasetId=SEDAC_POP|titel= NASA Earth Observations: Population Density|hämtdatum=30 januari 2016 |efternamn= |förnamn= |datum= |verk= |utgivare= NASA/SEDAC}}";
else
gn = "{{Cite web |url= http://neo.sci.gsfc.nasa.gov/view.php?datasetId=SEDAC_POP|title= NASA Earth Observations: Population Density|access-date = 30 Enero 2016 |publisher= NASA/SEDAC}}";
//{{Webbref |url= |titel= |hämtdatum= |författare= |efternamn= |förnamn= |författarlänk= |efternamn2= |förnamn2= |datum= |år= |månad= |format= |verk= |utgivare= |sid= |språk= |doi= |arkivurl= |arkivdatum= |citat= |ref= }}
return addref("nasapop", gn);

public static string nasarainref()

string gn = "";
if (makelang == "sv")
gn = "{{webbref |url= http://neo.sci.gsfc.nasa.gov/view.php?datasetId=TRMM_3B43M&year=2014|titel= NASA Earth Observations: Rainfall (1 month - TRMM)|hämtdatum=30 januari 2016 |efternamn= |förnamn= |datum= |verk= |utgivare= NASA/Tropical Rainfall Monitoring Mission}}";
else
gn = "{{Cite web |url= http://neo.sci.gsfc.nasa.gov/view.php?datasetId=TRMM_3B43M&year=2014|title= NASA Earth Observations: Rainfall (1 month - TRMM)|access-date = 30 Enero 2016 |publisher= NASA/Tropical Rainfall Monitoring Mission}}";
//{{Webbref |url= |titel= |hämtdatum= |författare= |efternamn= |förnamn= |författarlänk= |efternamn2= |förnamn2= |datum= |år= |månad= |format= |verk= |utgivare= |sid= |språk= |doi= |arkivurl= |arkivdatum= |citat= |ref= }}
return addref("nasarain", gn);

public static string nasalandcoverref()

string gn = "";
if (makelang == "sv")
gn = "{{webbref |url= http://neo.sci.gsfc.nasa.gov/view.php?datasetId=MCD12C1_T1|titel= NASA Earth Observations: Land Cover Classification|hämtdatum=30 januari 2016 |efternamn= |förnamn= |datum= |verk= |utgivare= NASA/MODIS}}";
else
gn = "{{Cite web |url= http://neo.sci.gsfc.nasa.gov/view.php?datasetId=MCD12C1_T1|title= NASA Earth Observations: Land Cover Classification|access-date = 30 Enero 2016 |publisher= NASA/MODIS}}";
//{{Webbref |url= |titel= |hämtdatum= |författare= |efternamn= |förnamn= |författarlänk= |efternamn2= |förnamn2= |datum= |år= |månad= |format= |verk= |utgivare= |sid= |språk= |doi= |arkivurl= |arkivdatum= |citat= |ref= }}
return addref("nasalandcover", gn);

public static string koppenref()
string gn = "";
if (makelang == "sv")
gn = "{{Tidskriftsref | författare = | redaktör = | rubrik = Updated world map of the Köppen-Geiger climate classification| url = http://www.hydrol-earth-syst-sci.net/11/1633/2007/hess-11-1633-2007.html| år = 2007| tidskrift = Hydrology and Earth System Sciences| volym = 11| utgivningsort = | utgivare = | nummer = | sid = 1633-1644| hämtdatum = 2016-01-30| id = | doi = 10.5194/hess-11-1633-2007| issn = | citat = | språk = | förnamn =M C| förnamn2 = B L| förnamn3 = T A| efternamn = Peel| efternamn2 = Finlayson| efternamn3 =McMahon| ref = }}";
else
gn = "{{cite journal |last= Peel|first= M C|last2= Finlayson|first2= B L|date= |title= Updated world map of the Köppen-Geiger climate classification| url = http://www.hydrol-earth-syst-sci.net/11/1633/2007/hess-11-1633-2007.html |journal= Hydrology and Earth System Sciences|publisher= |volume= 11|issue= |pages= 1633-1644|doi= 10.5194/hess-11-1633-2007|access-date=30 Enero 2016}}";
return addref("koppen", gn);

public static string wikiref(string iwlang)
Console.WriteLine("wikiref " + iwlang);

if ((iwlang == "Wikidata") || (iwlang == "wd"))
return addref(iwlang, "(" + mp(130, null) + " " + iwlang + DateTime.Now.ToString("yyyy-MM-dd") + ")");
else
return addref(iwlang + "wiki", "(" + mp(130, null) + " " + iwlang + "wiki " + wdtime.ToString("yyyy-MM-dd") + ")");

public static string makegnidlink(int gnid)
string link = "[[";

if (!gndict.ContainsKey(gnid))
Console.WriteLine("Bad gnid in makegnidlink " + gnid.ToString());
return "";

string aname = gndict[gnid].articlename;
if (aname == "XXX")
return "[["+gndict[gnid].Name_ml+"]]"; //no article name - return Name_ml linked!

if ( aname.Contains("*"))
aname = aname.Replace("*","");

if (gndict[gnid].Name_ml == aname)
link += aname;
else
link += aname + "|" + gndict[gnid].Name_ml;
link += "]]";
return link;

public static string make_catname(string catcode, string adm, bool countrylevel)
string catname = "";
if (countrylevel)
catname = mp(1, null) + initialcap(categoryml[catcode]) + " " + mp(75, null) + " " + adm;
if ((makelang == "sv") && ((catcode == "geography") || (catcode == "islands")))
catname = mp(1, null) + adm + "s " + categoryml[catcode];
if (catname.Contains("ss " + categoryml[catcode]))
catname = catname.Replace("ss " + categoryml[catcode], "s " + categoryml[catcode]);
if (catname.Contains("zs " + categoryml[catcode]))
catname = catname.Replace("zs " + categoryml[catcode], "z " + categoryml[catcode]);

else
catname = mp(1, null) + initialcap(categoryml[catcode]) + " " + mp(75, null) + " " + adm;
return catname;

public static void make_x_in_country(string catcode, string countrynameml)
string catname = make_catname(catcode, countrynameml, true);

if (donecats.Contains(catname))
return;
else
stats.Adddonecat();
donecats.Add(catname);
Page pc = new Page(makesite, catname);
tryload(pc, 1);
if (pc.Exists())
return;

string[] p95 = new string[1] { categoryml[catcode] };

if (parentcategory[catcode] == "top")
pc.text = mp(120, null);
pc.AddToCategory(initialcap(mp(95, p95))+"|"+countrynameml);
pc.AddToCategory(countrynameml);
trysave(pc, 2);

Page pc1 = new Page(makesite, mp(1, null) + mp(95, p95));
tryload(pc1, 1);
if (!pc1.Exists())
pc1.text = mp(120, null);
pc1.AddToCategory(initialcap(categoryml[catcode]));
trysave(pc1, 2);

else
pc.text = mp(120, null);
pc.AddToCategory(make_catname(parentcategory[catcode], countrynameml, true));
pc.AddToCategory(initialcap(mp(95, p95)) + "|" + countrynameml);
trysave(pc, 2);
make_x_in_country(parentcategory[catcode], countrynameml);

public static void make_x_in_adm1(string catcode, int admgnid,string countrynameml)
string admname = getartname(admgnid);
string catname = make_catname(catcode,admname,false);
Console.WriteLine(catname);

if (donecats.Contains(catname))
return;
else
stats.Adddonecat();
donecats.Add(catname);

Page pc = new Page(makesite,catname);
if (!tryload(pc, 1))
return;
if (pc.Exists())
return;

if (parentcategory[catcode] == "top")
pc.text = mp(120, null);
string[] p93 = new string[3] { categoryml[catcode], countrynameml, getadmlabel(makecountry, 1,admgnid) };
pc.AddToCategory(initialcap(mp(93, p93)));
//pc.AddToCategory(getgnidname(admgnid));
trysave(pc, 2);

Page pc1 = new Page(makesite, mp(1,null)+mp(93,p93));
tryload(pc1, 1);
if (!pc1.Exists())
pc1.text = mp(120, null);
pc1.AddToCategory(make_catname(catcode,countrynameml,true));
trysave(pc1, 2);
Page pc2 = new Page(makesite, make_catname(catcode, countrynameml, true));
tryload(pc2, 1);
if (!pc2.Exists())
pc2.text = mp(120, null);
pc2.AddToCategory(countrynameml);
trysave(pc2, 2);

else
string parentcat = make_catname(parentcategory[catcode], admname, false);
Console.WriteLine(parentcat);
pc.text = mp(120, null);
pc.AddToCategory(parentcat);
pc.AddToCategory(make_catname(catcode, countrynameml, true));
trysave(pc, 2);
make_x_in_adm1(parentcategory[catcode], admgnid, countrynameml);
make_x_in_country(catcode, countrynameml);

public static void merge_refs(Page p, string refstring)
//Console.WriteLine(refstring);
if (!p.text.Contains(""))
Console.WriteLine("no ");
return;

p.text = p.text.Replace("", refstring.Replace("", "") + "\n");

//Reference list:

//if (hasnotes)
// p.text += mp(175, null).Replace("XX", "\n\n");

//reflist += "\n\n\n";
//p.text += "\n\n== " + mp(51, null) + " ==\n\n" + reflist;

public static void retrofit_nasa(int gnid)
Console.WriteLine("============");

hasnotes = false;

if (!gndict.ContainsKey(gnid))
Console.WriteLine("Bad gnid in retrofit_nasa " + gnid.ToString());
return;

Console.WriteLine(gndict[gnid].featureclass.ToString() + "." + gndict[gnid].featurecode);

if (createclass != ' ')
if (gndict[gnid].featureclass != createclass)
Console.WriteLine("Wrong class in retrofit_nasa");
return;
if (createfeature != "")
if (gndict[gnid].featurecode != createfeature)
Console.WriteLine("Wrong feature in retrofit_nasa");
return;

if (createexceptfeature != "")
if (gndict[gnid].featurecode == createexceptfeature)
Console.WriteLine("Wrong feature in retrofit_nasa");
return;

if (createunit > 0)
bool admfound = false;
for (int i = 0; i < 5; i++)
if (gndict[gnid].adm[i] == createunit)
admfound = true;
if (!admfound)
Console.WriteLine("Wrong adm-unit in retrofit_nasa");
return;

if (createexceptunit > 0)
bool admfound = false;
for (int i = 0; i < 5; i++)
if (gndict[gnid].adm[i] == createexceptunit)
admfound = true;
if (admfound)
Console.WriteLine("Wrong adm-unit in retrofit_nasa");
return;

string prefix = testprefix;
//string maintitle = "";

if (gndict[gnid].articlename == "XXX")
Console.WriteLine("No articlename");
gndict[gnid].articlename = gndict[gnid].Name_ml;
//return;

if (String.IsNullOrEmpty(gndict[gnid].articlename))
Console.WriteLine("Null articlename");
gndict[gnid].articlename = gndict[gnid].Name_ml;
return;

Console.WriteLine("gnid = " + gnid);

string countryname = "";
int icountry = -1;
if (countrydict.ContainsKey(gndict[gnid].adm[0]))
icountry = gndict[gnid].adm[0];
countryname = countrydict[gndict[gnid].adm[0]].Name;
//Console.WriteLine("country name = " + countryname);
//Console.WriteLine("Native wiki = "+countrydict[icountry].nativewiki);
else
Console.WriteLine("Invalid country " + gndict[gnid].adm[0].ToString());

string countrynameml = countryname;
if (countryml.ContainsKey(countryname))
countrynameml = countryml[countryname];

string cleanname = remove_disambig(gndict[gnid].articlename);
string oldname = gndict[gnid].articlename.Replace("*","");
if (!is_latin(cleanname))
string latinname = gndict[gnid].asciiname;
if ((get_alphabet(cleanname) == "cyrillic") && (makelang == "sv"))
latinname = cyrillic.Transliterate(cleanname, countrydict[icountry].nativewiki);
gndict[gnid].articlename = gndict[gnid].articlename.Replace(cleanname, latinname);
if (!is_latin(gndict[gnid].Name_ml))
gndict[gnid].Name_ml = latinname;

Page p = new Page(makesite, prefix + getartname(gnid));

tryload(p, 3);

string origtext = "";

if (p.Exists())
if (!p.text.Contains(mp(195, null)))
Console.WriteLine("Not botmade");
return;

if (!p.text.Contains(gnid.ToString()))
Console.WriteLine("Wrong gnid in old article");
return;

if (p.text.Contains("NASA Earth"))
Console.WriteLine("Already done");
return;

origtext = p.text;

string mp117 = mp(117, null);
string mp118 = mp(118, null);
string mp119 = mp(119, null);

string mpop = make_popdensity(gnid).Trim();
bool popdone = false;
if (!String.IsNullOrEmpty(mpop))
if (p.text.Contains(mp117))
p.text = p.text.Replace(mp117, mpop);
popdone = true;
else if (p.text.Contains(mp118))
p.text = p.text.Replace(mp118, mpop);
popdone = true;
else if (p.text.Contains(mp119))
p.text = p.text.Replace(mp119, mpop);
popdone = true;

string climate = make_climate(gnid);
string lc = make_landcover(gnid);

string total = lc;
if ( !popdone && !String.IsNullOrEmpty(mpop))
if (!String.IsNullOrEmpty(total))
total += " ";
total += mpop;
if (!String.IsNullOrEmpty(climate))
if (!String.IsNullOrEmpty(total))
total += " ";
total += climate;

total = total.Trim();

string mp175 = mp(175, null).Replace("XX","").Substring(0,11);

string replacekey = "XXXX";
if (p.text.Contains(mp(51, null)))
replacekey = "== " + mp(51, null);
if (p.text.Contains(mp175))
replacekey = mp175;

Console.WriteLine("replacekey = " + replacekey);

p.text = p.text.Replace(replacekey, total + "\n\n" + replacekey);

merge_refs(p, reflist);

p.text = p.text.Replace("Mer om algoritmen finns här: [[Användare:Lsjbot/Algoritmer]].", "{{Lsjbot-algoritmnot}}").Replace("\n\n\n","\n\n");
p.text = p.text.Replace("at [{{Geonames", mp(293,null)+" [{{Geonames");

//Reference list:

//if (hasnotes)
// p.text += mp(175, null).Replace("XX", "\n\n");

//reflist += "\n\n\n";
//p.text += "\n\n== " + mp(51, null) + " ==\n\n" + reflist;

if (p.text != origtext)
trysave(p, 3);

public static void retrofit_nasa()
makesite.defaultEditComment = mp(219, null) + " " + countryml[makecountryname];

int iremain = gndict.Count;
int iremain0 = iremain;

foreach (int gnid in gndict.Keys)
iremain--;
if ((resume_at > 0) && (resume_at != gnid))
stats.Addskip();
continue;
else
resume_at = -1;

if (stop_at == gnid)
break;

reflist = "";
refnamelist.Clear();

retrofit_nasa(gnid);

Console.WriteLine(iremain.ToString() + " remaining.");

if (firstround && (iremain0 - iremain < 5))
Console.WriteLine("");
Console.ReadLine();

public static void test_nasa()
foreach (int gnid in gndict.Keys)
make_climate(gnid);
foreach (string s in climatemismatchdict.Keys)
Console.WriteLine(s+": "+ climatemismatchdict[s]);

while (true)
Console.WriteLine("Latitude:");
double lat = tryconvertdouble(Console.ReadLine());
Console.WriteLine("Longitude:");
double lon = tryconvertdouble(Console.ReadLine());

Console.WriteLine("Climate: " + make_climate(lat, lon));
Console.WriteLine("Landcover: " + make_landcover(lat, lon));
Console.WriteLine("Pop density: " + make_popdensity(lat, lon));

public static string make_climate_chart(nasaclass nc, string name)
string s = mp(220,null);
if (String.IsNullOrEmpty(s))
return s;

s = "{| border=\"1\"\n" + s + "\n| " + name + "\n";
int rainmax = 0;
bool validrain = true;
for (int i = 1; i < 13; i++)
if ((nc.month_temp_day[i] < -100) || (nc.month_temp_night[i] < -100))
Console.WriteLine("Invalid temperature data in make_climate_chart");
return "";
else if (nc.month_rain[i] < 0)
validrain = false;
for (int i = 1; i < 13; i++)
s += "| " + nc.month_temp_night[i].ToString(culture_en) + "| " + nc.month_temp_day[i].ToString(culture_en);
if ( validrain)
s += "| " + nc.month_rain[i].ToString() + "\n";
else
s += "| 0\n";
if (nc.month_rain[i] > rainmax)
rainmax = nc.month_rain[i];
if (rainmax > 750)
s += "|maxprecip = " + rainmax.ToString() + "\n";

s += "|float=left\n|clear=left\n|source = " + nasaref() + "\n}}\n|}";

//|maxprecip =
//|float =
//|clear =
//|units =
//|source =
//}}
return s;

public static void read_nasa()
string fname = geonamesfolder + @"nasa.txt";
Console.WriteLine("read_nasa " + fname);
if (!File.Exists(fname))
return;

//if (stats)
//{
// pophist.SetBins(0.0, 10000.0, 100);
// rainhist.SetBins(0.0, 10000.0, 100);
// rainminmaxhist.SetBins(0.0, 2000.0, 20);
// daynighthist.SetBins(-20.0, 40.0, 30);
//}

using (StreamReader sr = new StreamReader(fname))
int n = 0;

while (!sr.EndOfStream)
String line = sr.ReadLine();

//if (n > 250)
// Console.WriteLine(line);

string[] words = line.Split(tabchar);

//foreach (string s in words)
// Console.WriteLine(s);

//Console.WriteLine(words[0] + "|" + words[1]);

// public static Dictionary> adm1dict = new Dictionary>();

int code = tryconvert(words[0]);
//Console.WriteLine("code = " + code);
if (code < 0)
continue;

n++;

nasaclass nc = new nasaclass();
//public class nasaclass
//{
// public int landcover = -1; //Landcover code 1-17 http://eospso.nasa.gov/sites/default/files/atbd/atbd_mod12.pdf
// public int popdensity = -1; //people per square km
// public int temp_average = -999; //average across months and day-night
// public int temp_max = -999; //temp of hottest month
// public int month_max = -999; //hottest month (1-12)
// public int temp_min = -999; //temp of coldest month
// public int month_min = -999; //coldest month
// public int temp_daynight = -999; //average difference between day and night
// public int rainfall = -999; //mm per year
//}

nc.landcover = tryconvert(words[1]);
nc.popdensity = tryconvert(words[2]);
nc.temp_average = tryconvert(words[3]);
nc.temp_max = tryconvert(words[4]);
nc.month_max = tryconvert(words[5]);
nc.temp_min = tryconvert(words[6]);
nc.month_min = tryconvert(words[7]);
nc.temp_daynight = tryconvert(words[8]);
nc.rainfall = tryconvert(words[9]);
nc.rain_max = tryconvert(words[10]);
nc.rain_month_max = tryconvert(words[11]);
nc.rain_min = tryconvert(words[12]);
nc.rain_month_min = tryconvert(words[13]);
nc.koppen = tryconvert(words[14]);
if (words.Length > 15)
for (int i = 0; i < 13; i++)
nc.month_temp_day[i] = tryconvert(words[15 + i]);
for (int i = 0; i < 13; i++)
nc.month_temp_night[i] = tryconvert(words[28 + i]);
for (int i = 0; i < 13; i++)
nc.month_rain[i] = tryconvert(words[41 + i]);

int ndaynight = 0;
double daynightdiff = 0;
for (int i = 1; i < 13; i++) //remove zeroes that might be invalid
//if ( Math.Abs(nc.month_temp_day[i]) < 0.01 )
// nc.month_temp_day[i] = -999;
//if (Math.Abs(nc.month_temp_night[i]) < 0.01)
// nc.month_temp_night[i] = -999;
if ((nc.month_temp_day[i] > -100) && (nc.month_temp_night[i] > -100) && (Math.Abs(nc.month_temp_day[i]) > 0.01) && (Math.Abs(nc.month_temp_night[i]) > 0.01))
daynightdiff += nc.month_temp_day[i] - nc.month_temp_night[i];
ndaynight++;
if (ndaynight > 0)
nc.temp_daynight = Convert.ToInt32(daynightdiff / (1.0 * ndaynight));

if (ndaynight < 12)
for (int i = 1; i < 13; i++) //remove zeroes that might be invalid, if expected temp outside +-2
if (Math.Abs(nc.month_temp_day[i]) < 0.01)
if (nc.month_temp_night[i] > -100)
double expected = nc.month_temp_night[i] + nc.temp_daynight;
if (Math.Abs(expected) > 2)
nc.month_temp_day[i] = -999;
else
nc.month_temp_day[i] = -999;
if (Math.Abs(nc.month_temp_night[i]) < 0.01)
if (nc.month_temp_day[i] > -100)
double expected = nc.month_temp_day[i] - nc.temp_daynight;
if (Math.Abs(expected) > 2)
nc.month_temp_night[i] = -999;
else
nc.month_temp_night[i] = -999;

int ntemp = 0;
double tempsum = 0;

nc.temp_max = -999;
nc.month_max = -1;
nc.temp_min = 999;
nc.month_min = -1;

for (int i = 1; i < 13; i++) //estimate missing values
if ((nc.month_temp_day[i] > -100) && (nc.month_temp_night[i] > -100))
tempsum += nc.month_temp_day[i];
tempsum += nc.month_temp_night[i];
ntemp += 2;
double tmean = 0.5*(nc.month_temp_day[i]+nc.month_temp_night[i]);
if (tmean > nc.temp_max)
nc.temp_max = Convert.ToInt32(tmean);
nc.month_max = i;
if (tmean < nc.temp_min)
nc.temp_min = Convert.ToInt32(tmean);
nc.month_min = i;
else if (nc.month_temp_day[i] > -100)
tempsum += nc.month_temp_day[i];
tempsum += nc.month_temp_day[i] - nc.temp_daynight;
ntemp += 2;
else if (nc.month_temp_night[i] > -100)
tempsum += nc.month_temp_night[i];
tempsum += nc.month_temp_night[i] + nc.temp_daynight;
ntemp += 2;

if ( ntemp > 0 )
nc.temp_average = Convert.ToInt32(tempsum / (1.0*ntemp));

nasadict.Add(code, nc);

//if (stats)
//{
// pophist.Add(Convert.ToDouble(nc.popdensity));
// rainhist.Add(Convert.ToDouble(nc.rainfall));
// rainminmaxhist.Add(Convert.ToDouble(nc.rain_max - nc.rain_min));
// daynighthist.Add(Convert.ToDouble(nc.temp_daynight));
//}
Console.WriteLine("readnasa " + n);

//if (stats)
//{
// Console.WriteLine("Pophist:");
// pophist.PrintDHist();
// Console.ReadLine();
// Console.WriteLine("Rainhist:");
// rainhist.PrintDHist();
// Console.ReadLine();
// Console.WriteLine("Rainminmaxhist:");
// rainminmaxhist.PrintDHist();
// Console.ReadLine();
// Console.WriteLine("Daynighthist:");
// daynighthist.PrintDHist();
// Console.ReadLine();
//}

public static string make_popdensity(int gnid)
if (gndict.ContainsKey(gnid))
return " " + make_popdensity(gndict[gnid].latitude, gndict[gnid].longitude).Replace("XXX", gndict[gnid].Name_ml);
else
return "";

public static string make_popdensity(double lat, double lon)
int row = Convert.ToInt32(10 * (90 - lat));
int col = Convert.ToInt32(10 * (180 + lon));
int code = 10000 * row + col;

if (!nasadict.ContainsKey(code))
return "";

if (nasadict[code].popdensity >= 0)
if (nasadict[code].popdensity < 2)
return " " + mp(239, null) + nasapopref();
else
int[] poplevels = { 7, 20, 50, 250, 1000, 99999 };
int i = 0;
while (poplevels[i] < nasadict[code].popdensity)
i++;
string[] p238 = new string[] { mp(240 + i, null), fnum(nasadict[code].popdensity) };
return mp(238, p238) + nasapopref();

return "";

public static string make_landcover(int gnid)
if (gndict.ContainsKey(gnid))
return " " + make_landcover(gndict[gnid].latitude, gndict[gnid].longitude).Replace("XXX", gndict[gnid].Name_ml);
else
return "";

public static string make_landcover(double lat, double lon)
int row = Convert.ToInt32(10 * (90 - lat));
int col = Convert.ToInt32(10 * (180 + lon));
int code = 10000 * row + col;

if (!nasadict.ContainsKey(code))
return "";

if ((nasadict[code].landcover > 0) && (nasadict[code].landcover <= 17))
return mp(220 + nasadict[code].landcover, null)+nasalandcoverref();

return "";

public static string make_climate(int gnid)
if (gndict.ContainsKey(gnid))
return " " + make_climate(gndict[gnid].latitude, gndict[gnid].longitude, gndict[gnid].elevation, gndict[gnid].Name_ml).Replace("XXX", gndict[gnid].Name_ml);
else
return "";

public static string make_climate(double lat, double lon)
return make_climate(lat, lon, 0,"");

public static string make_climate(double lat, double lon, int altitude,string name)
Dictionary koppendict = new Dictionary();
koppendict.Add(1, "rainforest");
koppendict.Add(2,"monsoon");
koppendict.Add(3,"savanna");
koppendict.Add(4,"desert hot");
koppendict.Add(5,"desert cold");
koppendict.Add(6,"steppe hot");
koppendict.Add(7,"steppe cold");
koppendict.Add(8,"mediterranean");
koppendict.Add(9,"mediterranean");
koppendict.Add(10,"subalpine");
koppendict.Add(11,"humid subtropical");
koppendict.Add(12,"Cwb");
koppendict.Add(13, "Cwc");
koppendict.Add(14, "humid subtropical");
koppendict.Add(15,"oceanic");
koppendict.Add(16,"Cfc");
koppendict.Add(17,"continental");
koppendict.Add(18,"hemiboreal");
koppendict.Add(19,"boreal");
koppendict.Add(20,"continental subarctic");
koppendict.Add(21, "continental");
koppendict.Add(22, "hemiboreal");
koppendict.Add(23, "boreal");
koppendict.Add(24, "continental subarctic");
koppendict.Add(25, "continental");
koppendict.Add(26, "hemiboreal");
koppendict.Add(27, "boreal");
koppendict.Add(28, "continental subarctic");
koppendict.Add(29, "tundra");
koppendict.Add(30, "arctic");

string s = "";
//string dummyname = "XXX";

int row = Convert.ToInt32(10 * (90 - lat));
int col = Convert.ToInt32(10 * (180 + lon));
int code = 10000 * row + col;

Console.WriteLine("lat,lon = " + lat + ", " + lon);
Console.WriteLine("row,col,code = " + row + ", " + col + ", " + code);

if (!nasadict.ContainsKey(code))
return "";

string climate = "unknown";
string koppenclimate = "";
Console.WriteLine("koppen = " + nasadict[code].koppen);
if (nasadict[code].koppen > 0)
koppenclimate = koppendict[nasadict[code].koppen];

if (koppenclimate.Contains("Cw"))
if (Math.Abs(lat) < 30)
koppenclimate = "tropical highland";
else
koppenclimate = "oceanic";

if ( koppenclimate == "Cfc")
if (Math.Abs(lat) > 60)
koppenclimate = "subarctic oceanic";
else
koppenclimate = "oceanic";

if (koppenclimate == "tundra")
if (nasadict[code].temp_max > 13)
koppenclimate = "unknown";

if ( koppenclimate == "subalpine" )
if (altitude < 500 )
koppenclimate = "unknown";

//{{cite journal | author=Peel, M. C. and Finlayson, B. L. and McMahon, T. A. | year=2007 | title= Updated world map of the Köppen-Geiger climate classification | journal=Hydrol. Earth Syst. Sci. | volume=11 | pages=1633-1644 | url=http://www.hydrol-earth-syst-sci.net/11/1633/2007/hess-11-1633-2007.html | issn = 1027-5606}}

if (nasadict[code].rainfall >= 0)
if (nasadict[code].temp_average > -100)
if (nasadict[code].temp_min >= 18) //Tropical
if (nasadict[code].rain_min > 60)
climate = "rainforest";
else if (nasadict[code].rain_min > 100 - nasadict[code].rainfall / 25)
climate = "monsoon";
else
int potevapo = 20 * nasadict[code].temp_average;
if (Math.Abs(nasadict[code].rain_month_max - nasadict[code].month_max) <= 5)
potevapo += 280 - 56 * Math.Abs(nasadict[code].rain_month_max - nasadict[code].month_max);
if (nasadict[code].rainfall < 0.5 * potevapo)
climate = "desert";
else if (nasadict[code].rainfall < potevapo)
climate = "steppe";
else
climate = "savanna";

else if (nasadict[code].temp_max < 0) //Arctic
climate = "arctic";
else if (nasadict[code].temp_max < 10) //Tundra
climate = "tundra";
else
int potevapo = 20 * nasadict[code].temp_average;
if (Math.Abs(nasadict[code].rain_month_max - nasadict[code].month_max) <= 5)
potevapo += 280 - 56 * Math.Abs(nasadict[code].rain_month_max - nasadict[code].month_max);
if (nasadict[code].rainfall < 0.5 * potevapo)
climate = "desert";
else if (nasadict[code].rainfall < potevapo)
climate = "steppe";
else
if (nasadict[code].temp_min > -3)
climate = "temperate";
else
climate = "continental";
else if (String.IsNullOrEmpty(koppenclimate)) //temperature and Köppen unknown
int[] rainlevels = { 300, 1000, 99999 };
int i = 0;
while (rainlevels[i] < nasadict[code].rainfall)
i++;
//string[] p238 = new string[] { mp(240 + i, null), fnum(nasadict[code].popdensity) };
//s += " " + mp(238, p238);
if (i == 0)
climate = "dry";
else if (i >= 2)
climate = "wet";

else if (nasadict[code].temp_average > -100)
if (nasadict[code].temp_min >= 18) //Tropical
climate = "tropical";
else if (nasadict[code].temp_max < 0) //Arctic
climate = "arctic";
else if (nasadict[code].temp_max < 10) //Tundra
climate = "tundra";
else
if (nasadict[code].temp_min > -3)
climate = "temperate";
else
climate = "continental";

Console.WriteLine("koppen, climate 1 = " + koppenclimate + ", " + climate);

if (!String.IsNullOrEmpty(koppenclimate)) //check consistency
if (koppenclimate.Contains(climate))
climate = koppenclimate;
else if (climate == "unknown")
climate = koppenclimate;
else
if (climate == "temperate")
if (((nasadict[code].koppen == 5)||(nasadict[code].koppen >= 7)) && (nasadict[code].koppen <= 28))
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - "+climate;
if ( !climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc,0);
climatemismatchdict[kc]++;

climate = "unknown";
else if (climate == "continental")
if ((nasadict[code].koppen == 5) || ((nasadict[code].koppen >= 7) && (nasadict[code].koppen <= 10)) || ((nasadict[code].koppen >= 15) && (nasadict[code].koppen <= 29)))
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - "+climate;
if ( !climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc,0);
climatemismatchdict[kc]++;
climate = "unknown";
else if ((climate == "desert") || (climate == "steppe"))
if (koppenclimate.Contains("desert") || koppenclimate.Contains("steppe"))
climate = koppenclimate;
else if (( koppenclimate.Contains("cold")) && ((climate == "tundra" )|| (climate == "continental")))
climate = koppenclimate;
else if (koppenclimate == "mediterranean")
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - " + climate;
if (!climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc, 0);
climatemismatchdict[kc]++;
climate = "unknown";
else if ((climate == "tropical") || (climate == "rainforest") || (climate == "monsoon"))
if (koppenclimate.Contains("tropical") || koppenclimate.Contains("rainforest") || koppenclimate.Contains("monsoon") || koppenclimate.Contains("savanna"))
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - " + climate;
if (!climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc, 0);
climatemismatchdict[kc]++;
climate = "unknown";
else if (climate == "savanna")
if ((koppenclimate == "steppe hot")||(koppenclimate == "mediterranean")||(koppenclimate == "monsoon")||(koppenclimate == "humid subtropical"))
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - " + climate;
if (!climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc, 0);
climatemismatchdict[kc]++;
climate = "unknown";
else if (climate == "tundra")
if (koppenclimate.Contains("boreal") || koppenclimate.Contains("suba"))
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - " + climate;
if (!climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc, 0);
climatemismatchdict[kc]++;
climate = "unknown";
else if ((climate == "dry") || (climate == "wet"))
climate = koppenclimate;
else
Console.WriteLine("Koppen = " + koppenclimate + ", climate = " + climate);
string kc = koppenclimate + " - " + climate;
if (!climatemismatchdict.ContainsKey(kc))
climatemismatchdict.Add(kc, 0);
climatemismatchdict[kc]++;
climate = "unknown";

Console.WriteLine("koppen, climate 2 = " + koppenclimate + ", " + climate);

if ( climate != "unknown")

switch (climate)
case "rainforest":
s = mp(250, null);
break;
case "monsoon":
s = mp(251, null);
break;
case "savanna":
s = mp(252, null);
break;
case "desert":
s = mp(253, null);
break;
case "desert hot":
s = mp(254, null);
break;
case "desert cold":
s = mp(255, null);
break;
case "steppe":
s = mp(256, null);
break;
case "steppe hot":
s = mp(257, null);
break;
case "steppe cold":
s = mp(258, null);
break;
case "mediterranean":
s = mp(259, null);
break;
case "subalpine":
s = mp(260, null);
break;
case "humid subtropical":
s = mp(261, null);
break;
case "oceanic":
s = mp(262, null);
break;
case "subarctic oceanic":
s = mp(263, null);
break;
case "tropical highland":
s = mp(264, null);
break;
case "continental":
s = mp(265, null);
break;
case "hemiboreal":
s = mp(266, null);
break;
case "boreal":
s = mp(267, null);
break;
case "continental subarctic":
s = mp(268, null);
break;
case "tundra":
s = mp(269, null);
break;
case "arctic":
s = mp(270, null);
break;
case "tropical":
s = mp(271, null);
break;
case "dry":
s = mp(272, null);
break;
case "wet":
s = mp(273, null);
break;
case "temperate":
s = mp(274, null);
break;
default:
break;
if (climate == koppenclimate)
s += koppenref();

if (nasadict[code].temp_average > -100)
string[] p248 = new string[] { fnum(nasadict[code].temp_average) };
s += " " + mp(248, p248);
if (nasadict[code].month_max > 0)
string[] p249 = new string[] { mp(280 + nasadict[code].month_max, null), fnum(nasadict[code].temp_max), mp(280 + nasadict[code].month_min, null), fnum(nasadict[code].temp_min) };
s += " " + mp(249, p249);
s += nasaref();

if (nasadict[code].rainfall >= 0)
string[] p246 = new string[] { fnum(nasadict[code].rainfall) };
s += " " + mp(246, p246);
if (nasadict[code].rain_month_max > 0)
string[] p247 = new string[] { mp(280 + nasadict[code].rain_month_max, null), fnum(nasadict[code].rain_max), mp(280 + nasadict[code].rain_month_min, null), fnum(nasadict[code].rain_min) };
s += " " + mp(247, p247);
s += nasarainref();

if (nasadict[code].temp_average > -100)
s += "\n\n" + make_climate_chart(nasadict[code],name);

return s.Trim();

public static string from_capital(int gnid)
string fromcapital = "";;
int capitalgnid = countrydict[gndict[gnid].adm[0]].capital_gnid;

if (capitalgnid == gnid) //capital itself is not far from capital :)
return fromcapital;

if (gndict.ContainsKey(capitalgnid))
double dist = get_distance(gnid, capitalgnid);
double mindistcapital = 1.5 + 0.004*Math.Sqrt(gndict[capitalgnid].population);

if (dist > mindistcapital)
int intdist = Convert.ToInt32(dist);
if (intdist > 300)
intdist = 100 * Convert.ToInt32(0.01 * dist);
else if (intdist > 30)
intdist = 10 * Convert.ToInt32(0.1 * dist);

fromcapital = ", " + fnum(intdist) + " km " + mp(100 + get_direction(capitalgnid, gnid), null) + " " + mp(132, null) + " " + makegnidlink(capitalgnid);
else //coinciding with capital location
if (gndict[gnid].featureclass == 'A')
fromcapital = ". " + initialcap(mp(132, null)) + " " + makegnidlink(capitalgnid) + " " + mp(77,null) + " " + gndict[gnid].Name_ml;
else if (featurepointdict[gndict[gnid].featurecode])
fromcapital = ", " + mp(198,null) + " " + mp(132, null) + " " + makegnidlink(capitalgnid);

return fromcapital;

public static string getmonthstring()
DateTime thismonth = DateTime.Now;
string monthstring = thismonth.Month.ToString();
while (monthstring.Length < 2)
monthstring = "0" + monthstring;
return thismonth.Year.ToString() + "-" + monthstring;

public static void fill_wdid_buffer()
foreach (int gnid in gndict.Keys)
if ((resume_at_wdid > 0) && (resume_at_wdid != gnid))
continue;
else
resume_at_wdid = -1;

if (createclass != ' ')
if (gndict[gnid].featureclass != createclass)
Console.WriteLine("Wrong class in thread");
continue;
if (createfeature != "")
if (gndict[gnid].featurecode != createfeature)
Console.WriteLine("Wrong feature in thread");
continue;

if (createexceptfeature != "")
if (gndict[gnid].featurecode == createexceptfeature)
Console.WriteLine("Wrong feature in thread");
continue;

if (gndict[gnid].wdid <= 0)
int wdid = get_wd_item_direct(gnid);
Console.WriteLine("THREAD: gnid,wdid: " + gnid.ToString() + ", " + wdid.ToString());
if (!wdid_buffer.ContainsKey(wdid))
wdid_buffer.Add(gnid, wdid);

Console.WriteLine("End of fill_wdid_buffer");

public static void make_article(int gnid)

Console.WriteLine("============");

hasnotes = false;

if (!gndict.ContainsKey(gnid))
Console.WriteLine("Bad gnid in make_article " + gnid.ToString());
return;

Console.WriteLine(gndict[gnid].featureclass.ToString() + "." + gndict[gnid].featurecode);

if (createclass != ' ')
if (gndict[gnid].featureclass != createclass)
Console.WriteLine("Wrong class in make_article");
return;
if (createfeature != "")
if (gndict[gnid].featurecode != createfeature)
Console.WriteLine("Wrong feature in make_article");
return;

if (createexceptfeature != "")
if (gndict[gnid].featurecode == createexceptfeature)
Console.WriteLine("Wrong feature in make_article");
return;

if ((gndict[gnid].featureclass == 'A') && (gndict[gnid].featurecode.Length > 3) && (gndict[gnid].featurecode.Contains("ADM"))) //check so ADMx supported
int iadm = tryconvert(gndict[gnid].featurecode.Substring(3, 1));
Console.WriteLine("iadm = " + iadm.ToString());
if (String.IsNullOrEmpty(getadmlabel(makecountry,iadm,gnid)))
//if (iadm > admdict[makecountry].maxadm)
Console.WriteLine("Unsupported ADM-label in make_article");
//Console.ReadLine();
return;

if (createunit > 0)
bool admfound = false;
for (int i = 0; i < 5; i++)
if (gndict[gnid].adm[i] == createunit)
admfound = true;
if (!admfound)
Console.WriteLine("Wrong adm-unit in make_article");
return;

if (createexceptunit > 0)
bool admfound = false;
for (int i = 0; i < 5; i++)
if (gndict[gnid].adm[i] == createexceptunit)
admfound = true;
if (admfound)
Console.WriteLine("Wrong adm-unit in make_article");
return;

string prefix = testprefix;
string maintitle = "";

if (gndict[gnid].articlename.Contains("*"))
Console.WriteLine("Exists already");
if (makedoubles)
prefix = doubleprefix;
maintitle = getartname(gnid);
Page pmain = new Page(makesite,maintitle);
if (tryload(pmain,1))
if (!pmain.Exists())
prefix = testprefix;
maintitle = "";
else if (pmain.text.Contains(mp(195, null)) && !human_touched(pmain, makesite))
prefix = testprefix;
maintitle = "";

else
return;

if (gndict[gnid].articlename == "XXX")
Console.WriteLine("No articlename");
gndict[gnid].articlename = gndict[gnid].Name_ml;
//return;

if (String.IsNullOrEmpty(gndict[gnid].articlename))
Console.WriteLine("Null articlename");
gndict[gnid].articlename = gndict[gnid].Name_ml;
return;

string countryname = "";
int icountry = -1;
if (countrydict.ContainsKey(gndict[gnid].adm[0]))
icountry = gndict[gnid].adm[0];
countryname = countrydict[gndict[gnid].adm[0]].Name;
//Console.WriteLine("country name = " + countryname);
//Console.WriteLine("Native wiki = "+countrydict[icountry].nativewiki);
else
Console.WriteLine("Invalid country " + gndict[gnid].adm[0].ToString());

Console.WriteLine("gnid = " + gnid);

string countrynameml = countryname;
if (countryml.ContainsKey(countryname))
countrynameml = countryml[countryname];

string cleanname = remove_disambig(gndict[gnid].articlename);
string oldname = gndict[gnid].articlename.Replace("*","");
if (!is_latin(cleanname))
string latinname = gndict[gnid].asciiname;
if ((get_alphabet(cleanname) == "cyrillic") && (makelang == "sv"))
latinname = cyrillic.Transliterate(cleanname, countrydict[icountry].nativewiki);
gndict[gnid].articlename = gndict[gnid].articlename.Replace(cleanname, latinname);
if (!is_latin(gndict[gnid].Name_ml))
gndict[gnid].Name_ml = latinname;

if (gndict[gnid].articlename != oldname)
int ilang = -1;
if (langtoint.ContainsKey(countrydict[icountry].nativewiki))
ilang = langtoint[countrydict[icountry].nativewiki];
make_redirect(oldname, getartname(gnid), "", ilang);

//TEMPORARY!
//if (getartname(gnid).Contains(","))
//{
// string namenocomma = getartname(gnid).Replace(",", "");
// Page pagenc = new Page(makesite, namenocomma);
// tryload(pagenc, 1);
// if ( pagenc.Exists())
// if (pagenc.text.Contains("obotskapad") && (pagenc.text.Contains(gnid.ToString())))
// {
// make_redirect_override(pagenc, getartname(gnid),"",-1);
// //Console.ReadLine();
// }
//}
//TEMPORARY!

Page p = new Page(makesite, prefix + getartname(gnid));

tryload(p, 3);

bool ok_to_overwrite = false;
string origtext = "";

if (p.Exists())
Console.WriteLine("Exists already 1: " + p.title);
if ((overwrite && ( p.text.Contains(mp(195, null)) && !p.text.Contains(mp(69,null)) ) || p.IsRedirect()) && !human_touched(p, makesite) && p.text.Contains(gnid.ToString()))
//p.text = "";
ok_to_overwrite = true;
else if (makedoubles && !p.text.Contains(mp(195,null)))
prefix = doubleprefix;
maintitle = p.title;
p.title = doubleprefix + p.title;
Console.WriteLine("Prefix 1: "+p.title);
else
return;

if ((makecountry == "CN") && (gndict[gnid].featurecode == "ADM4"))
if (is_zhen(gnid))
gndict[gnid].oldarticlename = gndict[gnid].articlename;
gndict[gnid].articlename = gndict[gnid].articlename.Replace("("+mp(298,null),"("+mp(297,null));

if ((!String.IsNullOrEmpty(gndict[gnid].oldarticlename)) && (gndict[gnid].oldarticlename.Replace("*", "") != gndict[gnid].articlename.Replace("*", "")))
Page pold = new Page(makesite, gndict[gnid].oldarticlename.Replace("*", ""));
tryload(pold, 1);
if (pold.Exists())
if (human_touched(pold, makesite)) //old article exists and is edited; don't make new, redirect and return instead
make_redirect(getartname(gnid), pold.title, "", -1);
return;
else if (!is_fork(pold))
make_redirect_override(pold, getartname(gnid), "", -1); //redirect from old to new

//if ( gndict[gnid].wdid <= 0 )
// gndict[gnid].wdid = get_wd_item_direct(gnid);

if (gndict[gnid].wdid <= 0)
if (makespecificarticles)
gndict[gnid].wdid = get_wd_item_direct(gnid);
else
int nwait = 0;
while (!wdid_buffer.ContainsKey(gnid) && (nwait < 10))
Console.WriteLine("Waiting for wdid thread.");
Thread.Sleep(5000);//milliseconds
nwait++;
if (wdid_buffer.ContainsKey(gnid))
gndict[gnid].wdid = wdid_buffer[gnid];
Console.WriteLine("Getting from wdid thread.");

wdid = gndict[gnid].wdid;

if (wdid > 0)
currentxml = get_wd_xml(wdid);
if (currentxml == null)
wdid = -1;

if (wdid > 0)
double areawd = -1.0;
long popwd = -1;
string iwss = "";

bool preferurban = (gndict[gnid].featureclass == 'P');
get_wd_area_pop(wdid, currentxml, out areawd, out popwd, out iwss,preferurban);
if (popwd > 0)
Console.WriteLine("popwd = " + popwd.ToString());
gndict[gnid].population_wd = popwd;
if (( gndict[gnid].population < minimum_population ) || (!prefergeonamespop))
gndict[gnid].population = popwd;
gndict[gnid].population_wd_iw = iwss;
//npop++;

if (areawd > 0)
gndict[gnid].area = areawd;
//narea++;

else
currentxml = null;

//Console.WriteLine(gndict[gnid].Name + ": " + gndict[gnid].population_wd.ToString() + gndict[gnid].population_wd_iw);

Console.WriteLine("wdid = " + wdid.ToString());

string commonscat = "";

if (wdid > 0)
Console.WriteLine("get_wd_sitelinks");
Dictionary sitelinks = get_wd_sitelinks(currentxml);
foreach (string lang in sitelinks.Keys)
if (lang == makelang + "wiki")
Console.WriteLine("Already iw to makelang (1)");
if (String.IsNullOrEmpty(prefix))
make_redirect(getartname(gnid), sitelinks[lang], "", -1);

if (makedoubles)
Page psl = new Page(makesite, sitelinks[lang]);
tryload(psl, 2);
if ( psl.Exists() && !psl.IsRedirect())
if ((p.title != sitelinks[lang]) || !ok_to_overwrite )
Console.WriteLine("Setting double");
prefix = doubleprefix;
if (!p.title.Contains(doubleprefix))
p.title = doubleprefix + p.title;
maintitle = sitelinks[lang];
Console.WriteLine("Prefix 2: " + p.title);

else
return;
if (lang == "commonswiki")
commonscat = sitelinks[lang];
Console.WriteLine("get_wd_commonscat");
if ( String.IsNullOrEmpty(commonscat))
commonscat = get_wd_prop(propdict["commonscat"], currentxml);

string[] p10 = new string[3] { botname, countrynameml, getmonthstring() };

origtext = p.text;
p.text = mp(10, p10) + "\n";

if (is_disambig(p.title)) //top link to disambig page
string forktitle = "";
Page pfork = new Page(makesite, remove_disambig(p.title));
if (tryload(pfork, 1))
if (is_fork(pfork))
forktitle = pfork.title;
else
pfork.title += " (" + mp(67, null) + ")";
pfork.text = null;
if (tryload(pfork, 1))
if (pfork.Exists())
forktitle = pfork.title;

if (!String.IsNullOrEmpty(forktitle))
String[] p181 = { forktitle };
p.text += mp(181, p181) + "\n";
//pauseaftersave = true;

if (p.title.Contains("&#"))
saveanomaly(p.title, "Contains broken html?");

if ((gndict[gnid].featureclass != 'P') && (gndict[gnid].featureclass != 'A'))
gndict[gnid].population = 0;
gndict[gnid].population_wd = 0;

if (gndict[gnid].featureclass == 'P')
gndict[gnid].area = 0;

p.text += fill_geobox(gnid) + "\n\n";

//Native names:
Dictionary nativenames = new Dictionary();

if ((icountry > 0 ) && (altdict.ContainsKey(gnid)))
foreach (altnameclass ac in altdict[gnid])
if (ac.altname != gndict[gnid].Name_ml)
if (countrydict[icountry].languages.Contains(ac.ilang))
if (!nativenames.ContainsKey(ac.altname))
nativenames.Add(ac.altname, ac.ilang);

string nativestring = "";
if (nativenames.Count > 0)
int nname = 0;
bool commaneeded = false;
nativestring = "(";

int prevlang = -1;

foreach (string nn in nativenames.Keys)
int ilang = nativenames[nn];
if (langdict.ContainsKey(ilang))
if (langdict[ilang].name.ContainsKey(makelang))
if (commaneeded)
nativestring += ", ";
if (ilang != prevlang)
nativestring += "[[" + langdict[ilang].name[makelang] + "]]: ";
nativestring += "'''" + nn + "'''";
nname++;
commaneeded = true;
prevlang = ilang;

if (nname > 0)
nativestring += ") ";
else
nativestring = "";

bool namestart = false; //true if previous sentence started with article name; used to avoid repetitive language.
string sent = "";
string pronoun = gndict[gnid].Name_ml;
string flabel = getfeaturelabelindet(makecountry, gndict[gnid].featurecode, gnid);
if (makelang == "sv")
if (flabel.StartsWith("en "))
pronoun = "den";
else if (flabel.StartsWith("ett "))
pronoun = "det";
else
pronoun = "de";
else if (makelang == "ceb")
if (featuredict[gndict[gnid].featurecode].StartsWith("mga "))
pronoun = "sila";
else
pronoun = "siya";

/// X är en Y i landet Z
sent += "'''"+gndict[gnid].Name_ml + "''' " + nativestring + mp(4,null) + " " + linkfeature(gndict[gnid].featurecode,gnid);
if (countrydict.ContainsKey(gndict[gnid].adm[0]))
sent += " " + mp(75,null) + " " + countrytitle(gndict[gnid].adm[0]) + linkcountry(gndict[gnid].adm[0]);
if (gndict[gnid].altcountry.Count > 0)
string countrylist = "";
int noo = 0;
foreach (int oo in gndict[gnid].altcountry)
if (countrydict.ContainsKey(oo))
if ( motherdict.ContainsKey(makecountry))
if ( oo == countryid[motherdict[makecountry]] )
continue;
noo++;
if (noo > 1)
if (noo == gndict[gnid].altcountry.Count)
countrylist += mp(97, null);
else
countrylist += ",";
countrylist += " " + countrydict[oo].Name_ml;
if (noo > 0)
sent += ", " + mp(134, null) + countrylist;

p.text += sent + "." + geonameref(gnid);
namestart = true;

// X ligger i (kommun), (provins) och (region)

sent = "";
int maxadm = 0;
int minadm = 999;

for (int i = 4; i > 0; i--)
if (!String.IsNullOrEmpty(getgnidname(gndict[gnid].adm[i])) && (gndict[gnid].adm[i] != gnid))
if (i > maxadm)
maxadm = i;
if (i < minadm)
minadm = i;

//int capitalgnid = -1;
string fromcapital = "";
if (countrydict.ContainsKey(gndict[gnid].adm[0]))
fromcapital = from_capital(gnid);

string countrypart = mp(getcountrypart(gnid),null);
if ((makelang == "sv") && (motherdict.ContainsKey(makecountry)))
countrypart = countrypart.Replace("delen av landet","delen av "+countrynameml);

if (maxadm > 0)
sent += " " + mp(135,null) + " " + gndict[gnid].Name_ml + " " + mp(77, null) + " ";
for (int i = 4; i > 0; i--)
if (!String.IsNullOrEmpty(getgnidname(gndict[gnid].adm[i])))
sent += getadmdet(makecountry, i, gndict[gnid].adm[i]) + " " + comment("ADM"+i.ToString())+ makegnidlink(gndict[gnid].adm[i]);
if (i > minadm)
if (i == minadm + 1)
sent += " " + mp(3, null) + " ";
else
sent += ", ";
sent += ", " + countrypart + fromcapital + ".";

else
sent += " " + gndict[gnid].Name_ml + " " + mp(92, null) + " " + countrypart + fromcapital + ".";

if (!String.IsNullOrEmpty(sent))
if (sent.Trim().StartsWith(gndict[gnid].Name_ml))
if (namestart)
sent = ReplaceOne(sent, gndict[gnid].Name_ml, initialcap(pronoun), 0);
namestart = false;
else
namestart = true;
else
namestart = false;

p.text += sent;

//population & elevation & area

string[] p99 = new string[2] { gndict[gnid].Name_ml, fnum(gndict[gnid].elevation) };
string[] p100 = new string[1] { comment("pop")+fnum(gndict[gnid].population) };
string[] p100wd = new string[1] { comment("pop") + fnum(gndict[gnid].population_wd) };

sent = "";

Console.WriteLine("elevation/population");
if ((gndict[gnid].elevation > 0) && ((categorydict[gndict[gnid].featurecode] != "peninsulas")) && (featurepointdict[gndict[gnid].featurecode] || (categorydict[gndict[gnid].featurecode] == "lakes")))
string heightref = geonameref(gnid);
if ((gndict[gnid].elevation > 0) && (gndict[gnid].elevation == gndict[gnid].elevation_vp))
heightref = addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

if (gndict[gnid].population > minimum_population)
if ( gndict[gnid].population_wd == gndict[gnid].population )
sent += " " + mp(99, p99) + heightref + mp(97, null) + " " + mp(100, p100wd) + "." + wikiref(gndict[gnid].population_wd_iw);
else
sent += " " + mp(99, p99) + heightref + mp(97, null) + " " + mp(100, p100) + "." + geonameref(gnid);
else
if (gndict[gnid].population_wd > minimum_population)
p100[0] = gndict[gnid].population_wd.ToString();
sent += " " + mp(99, p99) + heightref + mp(97, null) + " " + mp(100, p100wd) + "." + wikiref(gndict[gnid].population_wd_iw);
else
int imp = 99;
bool peak = is_height(gndict[gnid].featurecode);
if (peak)
imp = 178;
//switch (categorydict[gndict[gnid].featurecode])
//{
// case "mountains":
// case "hills":
// case "volcanoes":
// imp = 178;
// break;
// default:
// imp = 99;
// break;

//}
sent += " " + mp(imp, p99);
if ((peak) && (gndict[gnid].prominence > minimum_prominence ))
sent += "," + heightref;
string[] p190 = new string[1] { fnum(gndict[gnid].prominence)};
sent += " " + mp(190, p190);
sent += addnote(mp(191, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));
string[] p192 = new string[1] { fnum(gndict[gnid].width) };
sent += ". " + mp(192, p192) + "." + addnote(mp(193,null));
else
sent += "." + heightref;
else if (gndict[gnid].population > minimum_population)
sent += " " + initialcap(mp(100, p100)) + ".";
if (gndict[gnid].population_wd == gndict[gnid].population)
sent += wikiref(gndict[gnid].population_wd_iw);
else
sent += geonameref(gnid);
else if (gndict[gnid].population_wd > minimum_population)
//p100[0] = comment("pop") + fnum(gndict[gnid].population_wd);
sent += " " + initialcap(mp(100, p100wd)) + "." + wikiref(gndict[gnid].population_wd_iw);

Console.WriteLine("area");
if (gndict[gnid].area > minimum_area)
string[] p129 = new string[2] { gndict[gnid].Name_ml, fnum(gndict[gnid].area) };
sent += " " + mp(129, p129);

if (!String.IsNullOrEmpty(sent))
if (sent.Trim().StartsWith(gndict[gnid].Name_ml))
if (namestart)
sent = ReplaceOne(sent, gndict[gnid].Name_ml, initialcap(pronoun), 0);
namestart = false;
else
namestart = true;

else
namestart = false;

p.text += sent;
sent = "";

if ((gndict[gnid].island > 0) && (categorydict[gndict[gnid].featurecode] != "islands")) //On island; not island-on-island
if (gndict[gndict[gnid].island].area > gndict[gnid].area) //Only if island is bigger than gnid
string[] p139 = new string[2] { gndict[gnid].Name_ml, makegnidlink(gndict[gnid].island) };
sent += " " + mp(139, p139);
//if (makelang == "sv")
sent += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

if (!String.IsNullOrEmpty(sent))
if (sent.Trim().StartsWith(gndict[gnid].Name_ml))
if (namestart)
sent = ReplaceOne(sent, gndict[gnid].Name_ml, initialcap(pronoun), 0);
namestart = false;
else
namestart = true;
else
namestart = false;

p.text += sent;
sent = "";

if ((gndict[gnid].inlake > 0) && (categorydict[gndict[gnid].featurecode] != "lakes"))//In a lake (mainly islands); not lake-in-lake
if (gndict[gndict[gnid].inlake].area > gndict[gnid].area) //Only if lake is bigger than gnid
string[] p155 = new string[2] { gndict[gnid].Name_ml, makegnidlink(gndict[gnid].inlake) };
sent += " " + mp(155, p155);
if (makelang == "sv")
sent += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

if (!String.IsNullOrEmpty(sent))
if (sent.Trim().StartsWith(gndict[gnid].Name_ml))
if (namestart)
sent = ReplaceOne(sent, gndict[gnid].Name_ml, initialcap(pronoun), 0);
namestart = false;
else
namestart = true;
else
namestart = false;

p.text += sent;
sent = "";

if (gndict[gnid].atlakes.Count > 0) //Near one or more lakes
if (gndict[gnid].atlakes.Count == 1)
if (gndict[gndict[gnid].atlakes[0]].area > gndict[gnid].area) //Only if lake is bigger than gnid
string[] p156 = new string[2] { gndict[gnid].Name_ml, makegnidlink(gndict[gnid].atlakes[0]) };
sent += " " + mp(156, p156);
if (makelang == "sv")
sent += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));
else
string lakes = "";
int ilakes = 0;
foreach (int lg in gndict[gnid].atlakes)
ilakes++;
if (ilakes == gndict[gnid].atlakes.Count)
lakes += mp(97, null);
lakes += " " + makegnidlink(lg);
string[] p157 = new string[2] { gndict[gnid].Name_ml, lakes };
sent += " " + mp(157, p157);
//if (makelang == "sv")
sent += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));

if (!String.IsNullOrEmpty(sent))
if (sent.Trim().StartsWith(gndict[gnid].Name_ml))
if (namestart)
sent = ReplaceOne(sent, gndict[gnid].Name_ml, initialcap(pronoun), 0);
namestart = false;
else
namestart = true;
else
namestart = false;

p.text += sent;
sent = "";

string rangecat = "";
if ((gndict[gnid].inrange > 0) && (gndict.ContainsKey(gndict[gnid].inrange)))//Part of a mountain range
sent = " " + gndict[gnid].Name_ml + " " + mp(204,null) + " " +makegnidlink(gndict[gnid].inrange) + ".";
sent += addnote(mp(140, null) + addref("vp", viewfinder_ref()) + " " + mp(200, null));
rangecat = getartname(gndict[gnid].inrange);

if (!String.IsNullOrEmpty(sent))
if (sent.Trim().StartsWith(gndict[gnid].Name_ml))
if (namestart)
sent = ReplaceOne(sent, gndict[gnid].Name_ml, initialcap(pronoun), 0);
namestart = false;
else
namestart = true;
else
namestart = false;

p.text += sent;
sent = "";

//p.text += "\n\n";

//separate for different types of features:
Console.WriteLine("Feature-specific");
if ((gndict[gnid].featureclass == 'A') && (gndict[gnid].featurecode.Contains("ADM")) && (!gndict[gnid].featurecode.Contains("ADMD")))
p.text += make_adm(gnid);
else if (gndict[gnid].featureclass == 'P')
if (gndict[gnid].population > minimum_population)
p.text += make_town(gnid);
else
Console.WriteLine("Below minimum population.");
return;
else if (categorydict[gndict[gnid].featurecode] == "islands")
p.text += make_island(gnid,p);
else if (categorydict[gndict[gnid].featurecode] == "lakes")
p.text += make_lake(gnid,p);
else if ((gndict[gnid].featurecode == "MTS")||(gndict[gnid].featurecode == "HLLS"))
p.text += make_range(gnid,p);
else if (featurepointdict[gndict[gnid].featurecode])
p.text += make_point(gnid);
else //Nothing type-specific to add

if (gndict[gnid].featurecode != "ADM1")
p.text += "\n\n" + make_climate(gnid);

//locator map:
if (!locatoringeobox)
string[] p73 = new string[2] { countrynameml, gndict[gnid].Name_ml };
p.text += "\n\n" + mp(72, null) + "|" + locatordict[countryname].get_locator(gndict[gnid].latitude, gndict[gnid].longitude) + " |float = right |width=300 |";
if (makelang != "sv")
p.text += " caption = " + mp(73, p73) + " | ";
p.text += mp(76, null) + " = " + gndict[gnid].Name_ml + "|position=right|background=white|lat=" + gndict[gnid].latitude.ToString(culture_en) + "|long=" + gndict[gnid].longitude.ToString(culture_en);
p.text += "}}\n";

//Reference list:

if (hasnotes)
p.text += mp(175,null).Replace("XX","\n\n");

reflist += "\n\n\n";
p.text += "\n\n== " + mp(51, null) + " ==\n\n" + reflist;

//External links:
if ( !String.IsNullOrEmpty(commonscat))
if (commonscat.Contains("Category:"))
commonscat = commonscat.Replace("Category:", "");
p.text += "\n\n== "+mp(52,null)+"==\n\n{{commonscat|" + commonscat + "|"+ gndict[gnid].Name_ml + "}}\n";

//If testrun only, inactivate categories and iw:

if (!String.IsNullOrEmpty(prefix))
p.text += "\n";

//Categories:

string catcode = categorydict[gndict[gnid].featurecode];

string catname = "";
if (!String.IsNullOrEmpty(getartname(gndict[gnid].adm[1])))
//catname = categoryml[catcode] + " " + mp(75, null) + " " + getgnidname(gndict[gnid].adm[1]);
catname = make_catname(catcode, getartname(gndict[gnid].adm[1]), false);
if (String.IsNullOrEmpty(prefix))
make_x_in_adm1(catcode, gndict[gnid].adm[1], countrynameml);
else
catname = make_catname(catcode, countrynameml, true);
if (String.IsNullOrEmpty(prefix))
make_x_in_country(catcode, countrynameml);
if (!String.IsNullOrEmpty(rangecat))
p.AddToCategory(initialcap(rangecat));
Page rcp = new Page(makesite, mp(1, null) + rangecat);
tryload(rcp, 1);
if (!rcp.Exists())
rcp.text = mp(120, null) + "\n";
rcp.AddToCategory(initialcap(catname));
trysave(rcp, 2);
else if (!rcp.text.Contains(catname))
rcp.AddToCategory(initialcap(catname));
trysave(rcp, 2);

p.AddToCategory(initialcap(catname));

switch (catcode)
case "lakes":
case "islands":
cat_by_size(p, catcode, countrynameml, gndict[gnid].area);
break;
case "mountains":
case "hills":
case "volcanoes":
cat_by_size(p, "mountains", countrynameml, gndict[gnid].elevation);
break;
case "populated places":
double dpop = gndict[gnid].population;
cat_by_size(p, "populated places", countrynameml, dpop, false);
break;
default:
break;

p.text += "\n\n";

//Interwiki:
if (wdid > 0)
string iwl = iwlinks(currentxml);
if (!iwl.Contains("Exists already"))
p.text += "\n"+iwl;
//else
//{
// string oldtit = iwl.Split(':')[1];
// if (!makedoubles)
// {
// make_redirect(prefix + gndict[gnid].articlename, oldtit, "", -1);
// return;
// }
// else
// {
// if ((p.title != oldtit) || !ok_to_overwrite)
// {
// if (!p.title.Contains(doubleprefix))
// p.title = doubleprefix + p.title;
// maintitle = oldtit;
// }
// }
//}
else
if (!String.IsNullOrEmpty(gndict[gnid].artname2))
string iwl = "\n[[";
if (makelang == "sv")
iwl += "ceb:";
else
iwl += "sv:";
iwl += gndict[gnid].artname2 + "]]\n";
p.text += iwl;

if (!String.IsNullOrEmpty(prefix))
p.text += "\n";

if (makedoubles && !String.IsNullOrEmpty(maintitle))
p.text = saveconflict(p.title, maintitle) + p.text;

if (p.text.Contains(mp(213,null)))
p.AddToCategory(mp(214,null));

countryspecials(p,gnid,catcode);

//Clean and save:

p.text = p.text.Replace("{{geobox\n| 0 ", "{{geobox\n| 1 ");
p.text = p.text.Replace("=