diff --git a/build.xml b/build.xml index 829e6e7..87df437 100644 --- a/build.xml +++ b/build.xml @@ -407,6 +407,7 @@ + diff --git a/resources/mkgmap-version.properties b/resources/mkgmap-version.properties index 84825a7..03ba812 100644 --- a/resources/mkgmap-version.properties +++ b/resources/mkgmap-version.properties @@ -1,2 +1,2 @@ -svn.version: 4380 -build.timestamp: 2019-11-28T14:08:04+0000 +svn.version: 4398 +build.timestamp: 2019-12-18T08:25:16+0000 diff --git a/resources/roadNameConfig.txt b/resources/roadNameConfig.txt index a201da4..cfe7a8c 100644 --- a/resources/roadNameConfig.txt +++ b/resources/roadNameConfig.txt @@ -44,8 +44,8 @@ prefix2:it = "del ", "dei ", "della ", "delle ", "di " # portugese -prefix1:pt = "Rua", "Avenida", "Travessa" -prefix2:pt = "da ", "do ", "de ", "das ", "dos " +prefix1:pt = "Rua", "Avenida", "Travessa", "Alameda", "Beco", "Estrada", "Rodovia" +prefix2:pt = "da ", "do ", "de ", "das ", "dos ", "d'" # spanish prefix1:es = "Avenida", "Calle", "Paseo" @@ -56,9 +56,11 @@ # Map three letter ISO country codes to list of used languages for road names. # It is assumed that the style sets mkgmap:country to one of these ISO codes. +lang:AGO = pt lang:AND = es, ca lang:ARG = es lang:BOL = es +lang:BRA = pt lang:CAN = en, fr lang:CHE = de, fr, it lang:CHL = es @@ -72,10 +74,12 @@ lang:ESP = es, gl, eu, ca lang:FRA = fr lang:GBR = en +lang:GNB = pt lang:GTM = es lang:GUY = en lang:HND = es lang:MEX = es +lang:MOZ = pt lang:NIC = es lang:PAN = es lang:PER = es diff --git a/resources/typ-files/mapnik.txt b/resources/typ-files/mapnik.txt new file mode 100644 index 0000000..64b9c3e --- /dev/null +++ b/resources/typ-files/mapnik.txt @@ -0,0 +1,5968 @@ + +[_id] +ProductCode=1 +CodePage=65001 +[End] +[_comments] +;https://github.com/Jorisbo/Mkgmap-Mapnik-Style-Garmin +;Jorisbo@hotmail.com +; +;This program is free software; you can redistribute it and/or modify +;it under the terms of the GNU General Public License version 3 or +;version 2 as published by the Free Software Foundation. +; +;This program is distributed in the hope that it will be useful, but +;WITHOUT ANY WARRANTY; without even the implied warranty of +;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;General Public License for more details. +; +;Auto generated: 20191209 18:13 +;From master: MKG +;As subset: mkgmap +;Based on mkgmap default style version: r4389 +; +[End] +[_drawOrder] +Type=0x04b,1 +Type=0x002,2 +Type=0x003,2 +Type=0x007,2 +Type=0x009,2 +Type=0x00a,2 +Type=0x010,2 +Type=0x011,2 +Type=0x012,2 +Type=0x015,2 +Type=0x016,2 +Type=0x018,2 +Type=0x01a,2 +Type=0x01c,2 +Type=0x01d,2 +Type=0x020,2 +Type=0x021,2 +Type=0x022,2 +Type=0x024,2 +Type=0x025,2 +Type=0x026,2 +Type=0x03b,2 +Type=0x03d,2 +Type=0x046,2 +Type=0x047,2 +Type=0x048,2 +Type=0x04c,2 +Type=0x04d,2 +Type=0x051,2 +Type=0x053,2 +Type=0x008,3 +Type=0x00b,3 +Type=0x00c,3 +Type=0x00f,3 +Type=0x032,3 +Type=0x04e,3 +Type=0x050,3 +Type=0x052,3 +Type=0x06b,3 +Type=0x017,4 +Type=0x04f,4 +Type=0x005,5 +Type=0x019,5 +Type=0x03c,5 +Type=0x03f,5 +Type=0x041,5 +Type=0x006,6 +Type=0x00e,6 +Type=0x013,7 +Type=0x023,7 +Type=0x06a,7 +Type=0x06c,8 +Type=0x004,9 +[End] +[_polygon] +type=0x02 +;GRMN_TYPE: Urban Areas/SMALL_CITY/Small urban area, less than 200 000 inhabitants/Non NT +String1=0x01,Résidentiel +String2=0x02,Wohngebiet +String3=0x04,Residential +String4=0x03,Bebouwing +String7=0x15,Obszar mieszkalny +String8=0x10,Residencial +String9=0x05,Area residenziale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #E0DFDF" +[end] +[_polygon] +type=0x03 +;GRMN_TYPE: Urban Areas/TOWN/Urban area, less than 50 000 inhabitants/Non NT +String1=0x01,Résidentiel +String2=0x02,Wohngebiet +String3=0x04,Residential +String4=0x03,Bebouwing +String7=0x15,Obszar mieszkalny +String8=0x10,Residencial +String9=0x05,Area residenziale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #E0DFDF" +[end] +[_polygon] +type=0x04 +;GRMN_TYPE: Large Manmade Areas/MILITARY_BASE/Military base area/Non NT +String1=0x01,Domaine militaire +String2=0x02,Militärisches Sperrgebiet +String3=0x04,Military +String4=0x03,Militair domein +String7=0x15,Teren wojskowy +String8=0x10,Militar +String9=0x05,Area militare +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #FF958B" +" c none" +" !! !! " +"!! !! " +"! !! !" +" !! !!" +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +"!! !! " +"! !! !" +" !! !!" +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x05 +;GRMN_TYPE: Large Manmade Areas/PARKING_LOT/Parking lot area/Non NT +String1=0x01,Parking +String2=0x02,Parkplatz +String3=0x04,Parking +String4=0x03,Parkeerterrein +String7=0x15,Parking +String8=0x10,Estacionamento +String9=0x05,Parcheggio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #EEEEEE" +[end] +[_polygon] +type=0x06 +;GRMN_TYPE: Large Manmade Areas/PARKING_GARAGE/Parking garage area/Non NT +String1=0x01,Garages +String2=0x02,Garagen +String3=0x04,Garages +String4=0x03,Garages +String7=0x15,Garaże +String8=0x10,Garagem +String9=0x05,Garage +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #DFDDCE" +[end] +[_polygon] +type=0x07 +;GRMN_TYPE: Large Manmade Areas/AIRPORT/Airport area/Non NT +String1=0x01,Aéroport +String2=0x02,Flughafen +String3=0x04,Airport +String4=0x03,Vliegveld +String7=0x15,Lotnisko +String8=0x10,Aeroporto +String9=0x05,Aerodromo +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #E9E7E2" +[end] +[_polygon] +type=0x08 +;GRMN_TYPE: Large Manmade Areas/SHOPPING_AREA/Shopping area/Non NT +String1=0x01,Zone commerciale +String2=0x02,Gewerbegebiet +String3=0x04,Commercial area +String4=0x03,Commercieel gebied +String7=0x15,Obszar handlowy +String8=0x10,Área comercial +String9=0x05,Area commerciale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #F2DAD9" +[end] +[_polygon] +type=0x09 +;GRMN_TYPE: Large Manmade Areas/MARINA/Marina area/Non NT +String1=0x01,Piscine +String2=0x02,Schwimmbad +String3=0x04,Swimmingpool +String4=0x03,Zwembad +String7=0x15,Basen +String8=0x10,Piscina +String9=0x05,Piscina +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x0a +;GRMN_TYPE: Large Manmade Areas/COLLEGE/College or university area/Non NT +String1=0x01,École +String2=0x02,Schule +String3=0x04,Education +String4=0x03,School +String7=0x15,Edukacja +String8=0x10,Escola +String9=0x05,Scuola +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #FFFFE5" +[end] +[_polygon] +type=0x0b +;GRMN_TYPE: Large Manmade Areas/HOSPITAL/Hospital area/Non NT +String1=0x01,Hôpital +String2=0x02,Krankenhaus +String3=0x04,Hospital +String4=0x03,Ziekenhuis +String7=0x15,Szpital +String8=0x10,Hospital +String9=0x05,Ospedale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #FFFFE5" +[end] +[_polygon] +type=0x0c +;GRMN_TYPE: Large Manmade Areas/INDUSTRIAL_COMPLEX/Industrial complex area/Non NT +String1=0x01,Zone industrielle +String2=0x02,Industriegebiet +String3=0x04,Industrial area +String4=0x03,Industriegebied +String7=0x15,Obszar przemysłowy +String8=0x10,Área industrial +String9=0x05,Area industriale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #EBDBE8" +[end] +[_polygon] +type=0x0e +;GRMN_TYPE: Large Manmade Areas/AIRPORT_RUNWAYS/Airport runway area/Non NT +String1=0x01,Aérodrome +String2=0x02,Flugfeld +String3=0x04,Aeroway +String4=0x03,Landingsbaan +String7=0x15,Pas startowy +String8=0x10,Aeródromo +String9=0x05,Pista di aviazione +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #BBBBCC" +[end] +[_polygon] +type=0x0f +;GRMN_TYPE: // +String1=0x01,Zone commerciale +String2=0x02,Gewerbegebiet +String3=0x04,Commercial area +String4=0x03,Commercieel gebied +String7=0x15,Obszar handlowy +String8=0x10,Área comercial +String9=0x05,Area commerciale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #F2DAD9" +[end] +[_polygon] +type=0x10 +;GRMN_TYPE: // +String1=0x01,Résidentiel +String2=0x02,Wohngebiet +String3=0x04,Residential +String4=0x03,Bebouwing +String7=0x15,Obszar mieszkalny +String8=0x10,Residencial +String9=0x05,Area residenziale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #E0DFDF" +[end] +[_polygon] +type=0x11 +;GRMN_TYPE: // +String1=0x01,Domaine militair +String2=0x02,Militärisches Sperrgebiet +String3=0x04,Military dangerzone +String4=0x03,Miltaire gevarenzone +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #FFFFFF" +"# c #FCD8DB" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"#############!###!##############" +"##############!#!###############" +"###############!################" +"##############!#!###############" +"#############!###!##############" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x12 +;GRMN_TYPE: // +String1=0x01,Aire +String2=0x02,Raststätte +String3=0x04,Service +String4=0x03,Snelweg rustplaats +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #EFC8C8" +[end] +[_polygon] +type=0x13 +;GRMN_TYPE: Large Manmade Areas/GENERIC_MANMADE/Other or generic manmade area, such as a building/Non NT +String1=0x01,Bâtiment +String2=0x02,Gebäude +String3=0x04,Building +String4=0x03,Gebouw +String7=0x15,Budynek +String8=0x10,Edifício +String9=0x05,Edificio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #D9D0C9" +[end] +[_polygon] +type=0x15 +;GRMN_TYPE: Park Areas/NATIONAL_PARK/National park/Non NT +String1=0x01,Espace vert +String2=0x02,Grün +String3=0x04,Village green +String4=0x03,Stadsgroen +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #CDEBB0" +[end] +[_polygon] +type=0x16 +;GRMN_TYPE: Park Areas/NATIONAL_PARK_OTHER/Small or misc sized national park/Non NT +String1=0x01,Espace vert +String2=0x02,Grün +String3=0x04,Green area +String4=0x03,Groen +String7=0x15,Zieleń +String8=0x10,Área verde +String9=0x05,Area naturale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #DBF1C6" +[end] +[_polygon] +type=0x17 +;GRMN_TYPE: Park Areas/URBAN_PARK/Small urban park, typically only a few square blocks in size/Non NT +String1=0x01,Parc +String2=0x02,Park +String3=0x04,Park +String4=0x03,Park +String7=0x15,Park +String8=0x10,Parque +String9=0x05,Parco +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #C8FACC" +[end] +[_polygon] +type=0x18 +;GRMN_TYPE: Large Manmade Areas/GOLF_COURSE/Golf course area/Non NT +String1=0x01,Golf +String2=0x02,Golfplatz +String3=0x04,Golf course +String4=0x03,Golfbaan +String7=0x15,Pole golfowe +String8=0x10,Campo de Golfe +String9=0x05,Campo da golf +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #B5E3B5" +[end] +[_polygon] +type=0x19 +;GRMN_TYPE: Large Manmade Areas/SPORTS_COMPLEX/Sports arena or stadium/Non NT +String1=0x01,Terrain de sport +String2=0x02,Sportplatz +String3=0x04,Sports pitch +String4=0x03,Sportveld +String7=0x15,Teren sportowy +String8=0x10,Campo de esportes +String9=0x05,Campo sportivo +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #AAE0CB" +[end] +[_polygon] +type=0x1a +;GRMN_TYPE: Large Manmade Areas/CEMETARY/Cemetary/Non NT +String1=0x01,Cimetière +String2=0x02,Friedhof +String3=0x04,Cemetry +String4=0x03,Begraafplaats +String7=0x15,Cmentarz +String8=0x10,Cemitério +String9=0x05,Cimitero +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #88B78E" +"# c #AACBAF" +"################################" +"################################" +"################################" +"################################" +"#######!!#######################" +"######!!!!######################" +"######!!!!######################" +"######!!!!######################" +"######!!!!######################" +"######!!!!######################" +"######!!!!######################" +"####!!!!!!!!####################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x1c +;GRMN_TYPE: // +String2=0x02,Wiese +String3=0x04,Meadow +String4=0x03,Weide +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #CDEBB0" +[end] +[_polygon] +type=0x1d +;GRMN_TYPE: // +String1=0x01,Parc +String2=0x02,Park +String3=0x04,Park +String4=0x03,Park +String7=0x15,Park +String8=0x10,Parque +String9=0x05,Parco +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #C8FACC" +[end] +[_polygon] +type=0x20 +;GRMN_TYPE: Park Areas/STATE_PARK_OTHER/Small or misc sized state park/Non NT +String1=0x01,Jardin +String2=0x02,Garten +String3=0x04,Garden +String4=0x03,Tuin +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #8CB873" +"# c #CDEBB0" +"################################" +"################################" +"################################" +"###!#######!#######!#######!####" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"###!#######!#######!#######!####" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"###!#######!#######!#######!####" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"###!#######!#######!#######!####" +"################################" +"################################" +"################################" +"################################" +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x21 +;GRMN_TYPE: // +String3=0x04,Tourism +String4=0x03,Toeristische attractie +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #CDEBB0" +[end] +[_polygon] +type=0x22 +;GRMN_TYPE: // +String1=0x01,Patrimoine historique +String2=0x02,Historisches Gebiet / Gebäude +String3=0x04,Historic area/building +String4=0x03,Historisch gebied/gebouw +String7=0x15,Historyczny teren / budynek +String8=0x10,Edifício histórico +String9=0x05,Luogo storico +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #D9D0C9" +[end] +[_polygon] +type=0x23 +;GRMN_TYPE: // +String1=0x01,Bâtiment +String2=0x02,Gebäude +String3=0x04,Building +String4=0x03,Gebouw +String7=0x15,Budynek +String8=0x10,Edifício +String9=0x05,Edificio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #D9D0C9" +[end] +[_polygon] +type=0x24 +;GRMN_TYPE: // +String2=0x02,Konstruktion +String3=0x04,Construction +String4=0x03,Constructie +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #B8B8B8" +[end] +[_polygon] +type=0x25 +;GRMN_TYPE: // +String1=0x01,Zone piétonne +String2=0x02,Fußgängerzone +String3=0x04,Pedestrians +String4=0x03,Voetgangersgebied +String7=0x15,Pieszy +String8=0x10,Pedestre +String9=0x05,Area pedonale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #DDDDE8" +[end] +[_polygon] +type=0x26 +;GRMN_TYPE: // +String1=0x01,Ferme +String2=0x02,Bauernhof +String3=0x04,Farm +String4=0x03,Boerenbedrijf +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #F5DCBA" +[end] +[_polygon] +type=0x32 +;GRMN_TYPE: Water Areas/SEA/Sea/Non NT +String1=0x01,Mer +String2=0x02,Meer +String3=0x04,Sea +String4=0x03,Zee +String7=0x15,Morze +String8=0x10,Mar +String9=0x05,Mare +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x3b +;GRMN_TYPE: // +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x3c +;GRMN_TYPE: Water Areas/LAKE_100MI/Lake less than 250 sq mi but greater or equal to 100 sq mi in area/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x3d +;GRMN_TYPE: Water Areas/LAKE_30MI, LARGE_LAKE/Large lake, typically between 30 and 500 sq mi in area/Non NT +String1=0x01,Baie +String2=0x02,Bucht +String3=0x04,Bay +String4=0x03,Baai +String7=0x15,Zatoka +String8=0x10,Baia +String9=0x05,Baia +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #F2EFE9" +[end] +[_polygon] +type=0x3f +;GRMN_TYPE: Water Areas/LAKE, LAKE_5MI/Medium sized lake, typically between 5 and 30 sq mi in area/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x41 +;GRMN_TYPE: Water Areas/LAKE_LT_1MI, SMALL_LAKE/Small lake, typically less than 5 sq mi in area/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x46 +;GRMN_TYPE: Water Areas/LARGE_RIVER/Major river, typically at least 700 ft in width/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x47 +;GRMN_TYPE: Water Areas/RIVER_GT_700FT/Major river greater or equal to 700 ft in width/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x48 +;GRMN_TYPE: Water Areas/RIVER_100FT, SMALL_RIVER/Minor river, typically less than 700 ft in width/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x4b +;GRMN_TYPE: Map Bounds/DATA_BOUNDS/Bounds of map after creation/Non NT +String1=0x01,Sol +String2=0x02,Hintergrund +String3=0x04,Non mapped area +String4=0x03,Achtergrond +String7=0x15,Obszar niezmapowany +String8=0x10,Área não mapeada +String9=0x05,Sfondo +ExtendedLabels=Y +FontStyle=LargeFont +CustomColor=Day +DaycustomColor:#00FFFF +Xpm="0 0 1 0" +"1 c #F2EFE9" +[end] +[_polygon] +type=0x4c +;GRMN_TYPE: Water Areas/INTERMITTENT_LAKE/An intermittent or dry lake/Non NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +Xpm="0 0 1 0" +"1 c #AAD3DF" +[end] +[_polygon] +type=0x4d +;GRMN_TYPE: Surface Cover Areas/GLACIER/Large area of compacted snow and ice/Non NT +String1=0x01,Glacier +String2=0x02,Gletscher +String3=0x04,Glacier +String4=0x03,Gletsjer +String7=0x15,Lodowiec +String8=0x10,Geleira +String9=0x05,Ghiacciaio +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4DA0D7 +Xpm="0 0 1 0" +"1 c #DDECEC" +[end] +[_polygon] +type=0x4e +;GRMN_TYPE: Surface Cover Areas/ORCHARD/Orchard or plantation area/Non NT +String1=0x01,Agriculture +String2=0x02,Ackerland +String3=0x04,Farmland +String4=0x03,Landbouw / Glastuinbouw +String7=0x15,Tereny rolnicze +String8=0x10,Lavoura +String9=0x05,Terreno coltivato +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #EEF0D5" +[end] +[_polygon] +type=0x4f +;GRMN_TYPE: Surface Cover Areas/SCRUB/Scrub brush area/Non NT +String1=0x01,Broussailles +String2=0x02,Gebüsch +String3=0x04,Scrub +String4=0x03,Kreupelbos +String7=0x15,Zarośla +String8=0x10,Arbusto +String9=0x05,Terreno incolto +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #B0BE93" +"# c #C8D7AB" +"################################" +"#######################!########" +"#######################!########" +"#####################!#!#!######" +"#####################!!!!!######" +"######################!!!#######" +"####################!!!!!!!#####" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"##############!#################" +"##############!#################" +"############!#!#!###############" +"############!!!!!###############" +"#############!!!################" +"###########!!!!!!!##############" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x50 +;GRMN_TYPE: Surface Cover Areas/WOODS/Wooded or forested area/Non NT +String1=0x01,Forêt +String2=0x02,Wald +String3=0x04,Forest +String4=0x03,Bos +String7=0x15,Las +String8=0x10,Floresta +String9=0x05,Selva +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #8AB379" +"# c #ADD19E" +"################################" +"################################" +"################################" +"#####!!!###!####################" +"####!###!##!####################" +"####!###!#!#!###################" +"####!###!#!#!###################" +"#####!!!##!#!###################" +"######!##!###!##################" +"######!##!!!!!##################" +"######!####!####################" +"######!####!####################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"################################" +"###############!!!###!##########" +"##############!###!##!##########" +"##############!###!#!#!#########" +"##############!###!#!#!#########" +"###############!!!##!#!#########" +"################!##!###!########" +"################!##!!!!!########" +"################!####!##########" +"################!####!##########" +"################################" +"################################" +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x51 +;GRMN_TYPE: Surface Cover Areas/WETLAND/Wetland or swamp area/Non NT +String1=0x01,Marais +String2=0x02,Sumpf +String3=0x04,Marsh +String4=0x03,Nat gebied +String7=0x15,Mokradła +String8=0x10,Pântano +String9=0x05,Palude +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="32 32 2 1" +"! c #49A6FA" +" c none" +" " +" " +" !!!!!! !!!!!! " +" " +" " +" !!!!!! !!!!!! " +" " +"!!!!! !!!!! " +" " +" " +" !!!!!! !!!!!! " +" " +" " +" !!!!!! !!!!! " +"!!!!!! " +" " +" " +" " +" !!!!!! !!!!!! " +" " +" !!!!!! " +"!!!!!! " +" !!!!!" +" !!!!!! " +" " +" " +" !!!!!! !!!!!! " +" " +" " +" !!!!!! !!!!!! " +"!!!!!! " +" !!!!! " +;12345678901234567890123456789012 +[end] +[_polygon] +type=0x52 +;GRMN_TYPE: Surface Cover Areas/TUNDRA/Area of nearly always freezing soil/Non NT +String3=0x04,Tundra +String4=0x03,Toendra +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #D6D99F" +[end] +[_polygon] +type=0x53 +;GRMN_TYPE: Surface Cover Areas/FLAT/Sand, tidal, mud, etc. flat area/Non NT +String1=0x01,Terre +String2=0x02,Land +String3=0x04,Land +String4=0x03,Land +String7=0x15,Land +String8=0x10,Terra +String9=0x05,Terra +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #F2EFE9" +[end] +[_polygon] +type=0x6a +;GRMN_TYPE: // +String1=0x01,Gare +String2=0x02,Bahnhof +String3=0x04,Station +String4=0x03,Station +String7=0x15,Stacja +String8=0x10,Estação +String9=0x05,Stazione +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #AF9C8D" +[end] +[_polygon] +type=0x6b +;GRMN_TYPE: // +String1=0x01,Zone piétonne +String2=0x02,Fußgängerzone +String3=0x04,Pedestrians +String4=0x03,Voetgangersgebied +String7=0x15,Pieszy +String8=0x10,Pedestre +String9=0x05,Area pedonale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #DDDDE8" +[end] +[_polygon] +type=0x6c +;GRMN_TYPE: // +String1=0x01,Quai +String2=0x02,Bahnsteig +String3=0x04,Platform +String4=0x03,Perron +String7=0x15,Platform +String8=0x10,Plataforma +String9=0x05,Banchina +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +Xpm="0 0 1 0" +"1 c #BBBBBB" +[end] +[_line] +type=0x01 +;GRMN_TYPE: Roads/INTERSTATE, MAJOR_HWY/Primary, divided, limited-access highway, akin to an interstate in the US/Non NT, NT +UseOrientation=N +LineWidth=8 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #E892A2" +"2 c #FF008B" +String1=0x01,Autoroute +String2=0x02,Autobahn +String3=0x04,Motorway +String4=0x03,Snelweg +String7=0x15,Autostrada +String8=0x10,Autoestrada +String9=0x05,Autostrada +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x02 +;GRMN_TYPE: Roads/PRINCIPAL_HWY/Principal highway, usually divided and limited access, akin to US highways/Non NT, NT +UseOrientation=N +LineWidth=8 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F9B29C" +"2 c #D87559" +String1=0x01,Voie rapide +String2=0x02,Schnellstraße +String3=0x04,Trunk +String4=0x03,Autoweg +String7=0x15,Droga szybkiego ruchu +String8=0x10,Via expressa +String9=0x05,Superstrada +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x03 +;GRMN_TYPE: Roads/OTHER_HWY/Other highway road, akin to a state highway in the US/Non NT, NT +UseOrientation=N +LineWidth=8 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FCD6A4" +"2 c #C5A363" +String1=0x01,Route primaire +String2=0x02,Bundesstraße +String3=0x04,Primary +String4=0x03,Primair +String7=0x15,Droga krajowa +String8=0x10,Via primária +String9=0x05,Strada principale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x04 +;GRMN_TYPE: Roads/ARTERIAL/Main city thoroughfare/Non NT, NT +UseOrientation=N +LineWidth=8 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F7FABF" +"2 c #BBC279" +String1=0x01,Route secondaire +String2=0x02,Bundesstraße +String3=0x04,Secondary +String4=0x03,Secundair +String7=0x15,Droga wojewódzka +String8=0x10,Via secundária +String9=0x05,Strada principale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x05 +;GRMN_TYPE: Roads/COLLECTOR/Secondary city thoroughfare/Non NT, NT +UseOrientation=N +LineWidth=8 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FFFFFF" +"2 c #C5C5C5" +String1=0x01,Route tertiaire +String2=0x02,Straße +String3=0x04,Tertiary +String4=0x03,Tertiair +String7=0x15,Droga powiatowa +String8=0x10,Via terciária +String9=0x05,Strada +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x06 +;GRMN_TYPE: Roads/RESIDENTIAL/Residential street, these are typically the most numerous roads in a detailed city map and do not show up until the user is zoomed in quite far/Non NT, NT +UseOrientation=N +LineWidth=7 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FFFFFF" +"2 c #C5C5C5" +String1=0x01,Rue +String2=0x02,Straße +String3=0x04,Residential +String4=0x03,Weg +String7=0x15,Droga +String8=0x10,Residencial +String9=0x05,Strada residenziale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x07 +;GRMN_TYPE: Roads/ALLEY, DRIVEWAY/Alleyway, private driveway to residence/Non NT, NT +UseOrientation=N +LineWidth=3 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FFFFFF" +"2 c #C5C5C5" +String1=0x01,Rue +String2=0x02,Straße +String3=0x04,Street +String4=0x03,Straat +String7=0x15,Ulica +String8=0x10,Rua +String9=0x05,Strada +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#626262 +[end] +[_line] +type=0x08 +;GRMN_TYPE: Roads/LOW_SPEED_RAMP/Highway ramp which is to be driven at low speeds/Non NT, NT +UseOrientation=Y +LineWidth=5 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F7FABF" +"2 c #BBC279" +String1=0x01,Voie d’accès +String2=0x02,Bundesstraße (Verbindung) +String3=0x04,Secondary (Link) +String4=0x03,Secundair (Verbinding) +String7=0x15,Droga wojewódzka (łącznik) +String8=0x10,Ligação de via secundária +String9=0x05,Strada principale (collegamento) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x09 +;GRMN_TYPE: Roads/HIGH_SPEED_RAMP, RAMP/Highway ramp which is to be driven at high speeds/Non NT, NT +UseOrientation=N +LineWidth=5 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F9B29C" +"2 c #D87559" +String1=0x01,Voie d’accès +String2=0x02,Schnellstraße (verbindung) +String3=0x04,Trunk (Link) +String4=0x03,Autoweg (Verbinding) +String7=0x15,Droga szybkiego ruchu (łącznik) +String8=0x10,Ligação de via expressa +String9=0x05,Superstrada (collegamento) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x0a +;GRMN_TYPE: Roads/UNPAVED_ROAD/Gravel or dirt road/Non NT, NT +UseOrientation=Y +Xpm="32 2 2 1" +"! c #BD6500" +" c none" +"!!!!! !! !!!!! !! !!!!! !! " +"!!!!! !! !!!!! !! !!!!! !! " +;12345678901234567890123456789012 +String1=0x01,Sans revêtement +String2=0x02,Unbefestigt +String3=0x04,Track (Grade unknown) +String4=0x03,Veldweg (Onbekend) +String7=0x15,Droga gruntowa (klasa nieznana) +String8=0x10,Estrada agrícola +String9=0x05,Sentiero carrabile +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x0b +;GRMN_TYPE: Roads/MAJOR_CONNECTOR/Major highway connector/Non NT, NT +UseOrientation=N +LineWidth=5 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F9B29C" +"2 c #D87559" +String1=0x01,Voie d’accès +String2=0x02,Schnellstraße (verbindung) +String3=0x04,Trunk (Link) +String4=0x03,Autoweg (Verbinding) +String7=0x15,Droga szybkiego ruchu (łącznik) +String8=0x10,Ligação de via expressa +String9=0x05,Superstrada (collegamento) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x0c +;GRMN_TYPE: Roads/ROUNDABOUT/Roundabout/Non NT, NT +UseOrientation=N +LineWidth=7 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FFFFFF" +"2 c #C5C5C5" +String1=0x01,Rond point +String2=0x02,Kreisverkehr +String3=0x04,Roundabout +String4=0x03,Rotonde +String7=0x15,Rondo +String8=0x10,Rotatória +String9=0x05,Rotatoria +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x11 +;GRMN_TYPE: Customizable Line Types/CUSTOMIZABLE_ROUTE_LINE_5/Routable customizable line/Non NT, NT +UseOrientation=N +Xpm="32 2 2 1" +"! c #0030FF" +" c none" +" !!!! !!!! !!!! !!!! " +" !!!! !!!! !!!! !!!! " +;12345678901234567890123456789012 +String1=0x01,Piste cyclable +String2=0x02,Radweg +String3=0x04,Cycleway +String4=0x03,Fietspad +String7=0x15,Ścieżka rowerowa +String8=0x10,Ciclovia +String9=0x05,Pista ciclabile +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=Day +DaycustomColor:#0030FF +[end] +[_line] +type=0x14 +;GRMN_TYPE: Misc. Line Types/RAILROAD/Railroad/Non NT +UseOrientation=Y +Xpm="32 3 2 1" +"! c #737373" +"# c #FFFFFF" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +"!!!!!!!!########!!!!!!!!########" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +;12345678901234567890123456789012 +String1=0x01,Voie ferrée +String2=0x02,Gleis +String3=0x04,Railway +String4=0x03,Spoorweg +String7=0x15,Kolej +String8=0x10,Ferrovia +String9=0x05,Ferrovia +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x15 +;GRMN_TYPE: Misc. Line Types/SHORELINE/Shoreline of water body (typically only used when a water area is not available)/Non NT +UseOrientation=N +Xpm="32 1 2 1" +"! c #000000" +" c none" +" " +;12345678901234567890123456789012 +String1=0x01,Littoral +String2=0x02,Küstelinien +String3=0x04,Coastline +String4=0x03,Kustlijn +String7=0x15,Wybrzeże +String8=0x10,Litoral / Costal +String9=0x05,Costa +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#838383 +[end] +[_line] +type=0x16 +;GRMN_TYPE: Roads/TRAIL/Walkway or trail/Non NT, NT +UseOrientation=N +Xpm="32 1 2 1" +"! c #FF0000" +" c none" +" !!!!! !!!!! !!!!! !!!!! " +;12345678901234567890123456789012 +String1=0x01,Sentier +String2=0x02,Fußweg +String3=0x04,Path +String4=0x03,Wandelpad +String7=0x15,Ścieżka +String8=0x10,Trilha +String9=0x05,Sentiero +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x17 +;GRMN_TYPE: // +UseOrientation=N +LineWidth=1 +Xpm="0 0 1 0" +"1 c #AAAAAA" +String3=0x04,Barrier +String4=0x03,Barrière +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x18 +;GRMN_TYPE: Water Features/STREAM/Small stream or creek/Non NT +UseOrientation=Y +LineWidth=2 +Xpm="0 0 1 0" +"1 c #AAD3DF" +String1=0x01,Ruisseau +String2=0x02,Bach +String3=0x04,Stream +String4=0x03,Beek +String7=0x15,Strumień +String8=0x10,Córrego +String9=0x05,Ruscello +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +[end] +[_line] +type=0x1a +;GRMN_TYPE: Roads/INTERNATIONAL_FERRY/Major water or rail ferry/Non NT, NT +UseOrientation=N +Xpm="32 1 2 1" +"! c #0030FF" +" c none" +" !!!! !!!! !!!! !!!! " +;12345678901234567890123456789012 +String1=0x01,Ferry +String2=0x02,Fähre +String3=0x04,Ferry +String4=0x03,Ferry +String7=0x15,Prom +String8=0x10,Balsa +String9=0x05,Traghetto +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#0065D5 +[end] +[_line] +type=0x1b +;GRMN_TYPE: Roads/FERRY/Water or rail ferry/Non NT, NT +UseOrientation=N +Xpm="32 1 2 1" +"! c #0030FF" +" c none" +" !!!! !!!! !!!! !!!! " +;12345678901234567890123456789012 +String1=0x01,Ferry +String2=0x02,Fähre +String3=0x04,Ferry +String4=0x03,Ferry +String7=0x15,Prom +String8=0x10,Balsa +String9=0x05,Traghetto +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#0065D5 +[end] +[_line] +type=0x1c +;GRMN_TYPE: Political Borders/MJR_PLTCL_BDRY/Major political boundary (typically used for state, provincial borders)/Non NT +UseOrientation=N +Xpm="32 9 2 1" +"! c #BD95D5" +" c none" +" !!!! !!!! !!!! !!!! " +" !!!! !!!! !!!! !!!! " +" " +" " +" " +" " +" " +" " +" " +;12345678901234567890123456789012 +String1=0x01,Limite communale +String2=0x02,Grenze +String3=0x04,Boundary +String4=0x03,Gemeentegrens +String7=0x15,Granica +String8=0x10,Limite Municipal +String9=0x05,Confine +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x1d +;GRMN_TYPE: Political Borders/MNR_PLTCL_BDRY/Minor political boundary (typically used for county/parish borders) +UseOrientation=N +Xpm="32 9 2 1" +"! c #BD95D5" +" c none" +"!!!!!!!!!!!!!! !! " +"!!!!!!!!!!!!!! !! " +" " +" " +" " +" " +" " +" " +" " +;12345678901234567890123456789012 +String1=0x01,Limite départementale +String2=0x02,Landesgrenze +String3=0x04,State Boundary +String4=0x03,Provinciegrens 3 +String7=0x15,Granica województwa +String8=0x10,Limite Estadual +String9=0x05,Confine regionale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BD30B4 +[end] +[_line] +type=0x1e +;GRMN_TYPE: Political Borders/INTRN_PLTCL_BDRY/International boundary/Non NT +UseOrientation=N +Xpm="32 9 2 1" +"! c #BD95D5" +"# c #FFFFFF" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +"#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!" +"!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#" +"#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!" +"!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#" +"#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!" +"!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#" +"#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!" +;12345678901234567890123456789012 +String1=0x01,Frontière nationale +String2=0x02,Staatsgrenze +String3=0x04,National boundary +String4=0x03,Landsgrens +String7=0x15,Granica państwa +String8=0x10,Limite Nacional +String9=0x05,Confine nazionale +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#BD30B4 +[end] +[_line] +type=0x1f +;GRMN_TYPE: Water Features/RIVER/Large stream or river/Non NT +UseOrientation=Y +LineWidth=7 +Xpm="0 0 1 0" +"1 c #AAD3DF" +String1=0x01,Rivière, Canal +String2=0x02,Fluß, Kanal +String3=0x04,River, Canal +String4=0x03,Rivier, Kanaal +String7=0x15,Rzeka, Kanał +String8=0x10,Rio / Canal +String9=0x05,Fiume/Canale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +[end] +[_line] +type=0x20 +;GRMN_TYPE: Contour Lines/MINOR_CONTOUR/Minor land-based contour line/Non NT +UseOrientation=N +Xpm="32 1 2 1" +"! c #DBB793" +" c none" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +;12345678901234567890123456789012 +String1=0x01,Courbe de niveau (20 m) +String2=0x02,Höhenlinie (20 m) +String3=0x04,Contour (20 m) +String4=0x03,Contour (20 m) +String7=0x15,Warstwica (20 m) +String8=0x10,Contorno (20 m) +String9=0x05,Curva di livello (20 m) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x21 +;GRMN_TYPE: Contour Lines/INT_CONTOUR/Intermediate contour (should be used for about every 5th contour line)/Non NT +UseOrientation=N +LineWidth=1 +Xpm="0 0 1 0" +"1 c #D3A87C" +String1=0x01,Courbe de niveau (100 m) +String2=0x02,Höhenlinie(100 m) +String3=0x04,Contour (100 m) +String4=0x03,Contour (100 m) +String7=0x15,Warstwica (100 m) +String8=0x10,Contorno (100 m) +String9=0x05,Curva di livello (100 m) +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#996633 +[end] +[_line] +type=0x22 +;GRMN_TYPE: Contour Lines/MAJOR_CONTOUR/Major contour (should be used for about every 10th contour line)/Non NT +UseOrientation=N +LineWidth=1 +Xpm="0 0 1 0" +"1 c #CC9966" +String1=0x01,Courbe de niveau (500 m) +String2=0x02,Höhenlinie (500 m) +String3=0x04,Contour (500 m) +String4=0x03,Contour (500 m) +String7=0x15,Warstwica (500 m) +String8=0x10,Contorno (500 m) +String9=0x05,Curva di livello (500 m) +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#996633 +[end] +[_line] +type=0x26 +;GRMN_TYPE: Water Features/INTERMITTENT_STREAM/Intermittent or dry stream, creek, or ditch/Non NT +UseOrientation=Y +Xpm="32 3 2 1" +"! c #AAD3DF" +" c none" +" !!!!! !!!!! !!!!! !!!!! " +" !!!!! !!!!! !!!!! !!!!! " +" !!!!! !!!!! !!!!! !!!!! " +;12345678901234567890123456789012 +String1=0x01,Ruisseau intermittent +String2=0x02,Wasserlauf (periodisch) +String3=0x04,Stream (Intermittent) +String4=0x03,Beek (Periodiek) +String7=0x15,Strumień +String8=0x10,Córrego intermitente +String9=0x05,Torrente (discontinuo) +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +[end] +[_line] +type=0x27 +;GRMN_TYPE: Misc. Line Types/AIRPORT_RUNWAY/Airport runway centerline/Non NT +UseOrientation=Y +LineWidth=12 +Xpm="0 0 1 0" +"1 c #BBBBCC" +String1=0x01,Piste d’aviation +String2=0x02,Startbahn +String3=0x04,Runway +String4=0x03,Landingsbaan +String7=0x15,Pas startowy +String8=0x10,Pista +String9=0x05,Pista di aviazione +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x28 +;GRMN_TYPE: Misc. Line Types/PIPELINE/Oil or water pipeline/Non NT +UseOrientation=Y +Xpm="32 5 2 1" +"! c #626262" +" c none" +" ! ! ! ! " +" ! ! ! ! " +" !!!!!!!!!!!!!! !!!!!!!!!!!!!! " +" ! ! ! ! " +" ! ! ! ! " +;12345678901234567890123456789012 +String1=0x01,Pipeline +String2=0x02,Rohr +String3=0x04,Pipe +String4=0x03,Pijpleiding +String7=0x15,Rurociąg +String8=0x10,Tubulação +String9=0x05,Conduttura +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x29 +;GRMN_TYPE: Misc. Line Types/POWERLINE/Powerline/Non NT +UseOrientation=Y +LineWidth=1 +Xpm="0 0 2 0" +"1 c #737373" +"2 c #FFFFFF" +String1=0x01,Ligne électrique +String2=0x02,Hochspannungskabel +String3=0x04,Power cable +String4=0x03,Hoogspanningskabel +String7=0x15,Kabel wysokiego napięcia +String8=0x10,Cabo de força +String9=0x05,Linea elettrica +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x30 +;GRMN_TYPE: // +UseOrientation=Y +LineWidth=7 +Xpm="0 0 1 0" +"1 c #FFC0CB" +String1=0x01,Circuit +String2=0x02,Rennstrecke +String3=0x04,Raceway +String4=0x03,Circuit +String7=0x15,Tor wyścigowy +String8=0x10,Pista de corrida +String9=0x05,Pista +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x10801 +;GRMN_TYPE: // +UseOrientation=N +LineWidth=5 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F9B29C" +"2 c #D87559" +String1=0x01,Voie d’accès +String2=0x02,Schnellstraße (verbindung) +String3=0x04,Trunk (Link) +String4=0x03,Autoweg (Verbinding) +String7=0x15,Droga szybkiego ruchu (łącznik) +String8=0x10,Ligação de via expressa +String9=0x05,Superstrada (collegamento) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x10802 +;GRMN_TYPE: // +UseOrientation=N +LineWidth=5 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FCD6A4" +"2 c #C5A363" +String1=0x01,Voie d’accès +String2=0x02,Bundesstraße (Verbindung) +String3=0x04,Primary (Link) +String4=0x03,Primair (Verbinding) +String7=0x15,Droga krajowa (łącznik) +String8=0x10,Ligação de via primária +String9=0x05,Strada principale (collegamento) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x10803 +;GRMN_TYPE: // +UseOrientation=Y +LineWidth=5 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #F7FABF" +"2 c #BBC279" +String1=0x01,Voie d’accès +String2=0x02,Bundesstraße (Verbindung) +String3=0x04,Secondary (Link) +String4=0x03,Secundair (Verbinding) +String7=0x15,Droga wojewódzka (łącznik) +String8=0x10,Ligação de via secundária +String9=0x05,Strada principale (collegamento) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x10804 +;GRMN_TYPE: // +UseOrientation=N +LineWidth=8 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #FFFFFF" +"2 c #C5C5C5" +String1=0x01,Route tertiaire +String2=0x02,Straße +String3=0x04,Tertiary +String4=0x03,Tertiair +String7=0x15,Droga powiatowa +String8=0x10,Via terciária +String9=0x05,Strada +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=No +[end] +[_line] +type=0x10a02 +;GRMN_TYPE: Water Features/INTERMITTENT_STREAM(NT)/Intermittent or dry stream, creek, or ditch/NT +UseOrientation=Y +Xpm="32 6 2 1" +"! c #AAD3DF" +" c none" +" !! !! !! !! !! !! !! !! " +" !! !! !! !! !! !! !! !! " +" !! !! !! !! !! !! !! !! " +" !! !! !! !! !! !! !! !! " +" !! !! !! !! !! !! !! !! " +" !! !! !! !! !! !! !! !! " +;12345678901234567890123456789012 +String1=0x01,Cours d’eau (intermittent) +String2=0x02,Fluß (Periodisch) +String3=0x04,River, Wadi (Intermittent) +String4=0x03,Rivier (Periodiek) +String7=0x15,Rzeka +String8=0x10,Rio intermitente +String9=0x05,Fiume (discontinuo) +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x10a06 +;GRMN_TYPE: // +UseOrientation=N +LineWidth=4 +BorderWidth=1 +Xpm="0 0 2 0" +"1 c #BBBBBB" +"2 c #949494" +String1=0x01,Quai +String2=0x02,Plattform +String3=0x04,Platform +String4=0x03,Perron +String7=0x15,Peron +String8=0x10,Plataforma +String9=0x05,Piattaforma +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_line] +type=0x10c06 +;GRMN_TYPE: // +UseOrientation=Y +Xpm="32 7 2 1" +"! c #99CCCC" +"# c #FFFFFF" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +"!!####!!!!####!!!!####!!!!####!!" +"!!####!!!!####!!!!####!!!!####!!" +"!!####!!!!####!!!!####!!!!####!!" +"!!####!!!!####!!!!####!!!!####!!" +"!!####!!!!####!!!!####!!!!####!!" +"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +;12345678901234567890123456789012 +String1=0x01,Route (en travaux) +String2=0x02,Weg (Im bau) +String3=0x04,Road (Construction) +String4=0x03,Weg (In aanleg) +String7=0x15,Droga (w budowie) +String8=0x10,Rodovia em construção +String9=0x05,Strada in costruzione +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +[end] +[_point] +type=0x001 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_10M/Large city with >10 million inhabitants/Non NT +String1=0x01,Capitale +String2=0x02,Hauptstadt +String3=0x04,Capital +String4=0x03,Hoofdstad +String7=0x15,Stolica +String8=0x10,Capital +String9=0x05,Metropoli +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="11 11 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!! " +" ! ! " +" ! ! " +"! !!! !" +"! !!!!! !" +"! !!!!! !" +"! !!!!! !" +"! !!! !" +" ! ! " +" ! ! " +" !!!!! " +;12345678901 +[end] +[_point] +type=0x002 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_5M, LARGE_CITY/Large city center, typically 1M+ inhabitants/Non NT +String1=0x01,Capitale +String2=0x02,Hauptstadt +String3=0x04,Capital +String4=0x03,Hoofdstad +String7=0x15,Stolica +String8=0x10,Capital +String9=0x05,Metropoli +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="11 11 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!! " +" ! ! " +" ! ! " +"! !!! !" +"! !!!!! !" +"! !!!!! !" +"! !!!!! !" +"! !!! !" +" ! ! " +" ! ! " +" !!!!! " +;12345678901 +[end] +[_point] +type=0x003 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_2M/Large city with a range of (2, 5] million inhabitants/Non NT +String1=0x01,Ville (>200k) +String2=0x02,Stadt (200t) +String3=0x04,City (>200k) +String4=0x03,Stad (>200k) +String7=0x15,Miasto (>200 tys.) +String8=0x10,Cidade (>200k) +String9=0x05,Città +ExtendedLabels=Y +FontStyle=LargeFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="9 9 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!! " +" ! ! " +"! !" +"! !" +"! !" +"! !" +"! !" +" ! ! " +" !!!!! " +;123456789 +[end] +[_point] +type=0x004 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_1M/Large city with a range of (1, 2] million inhabitants/Non NT +String1=0x01,Ville (>200k) +String2=0x02,Stadt (200t) +String3=0x04,City (>200k) +String4=0x03,Stad (>200k) +String7=0x15,Miasto (>200 tys.) +String8=0x10,Cidade (>200k) +String9=0x05,Città +ExtendedLabels=Y +FontStyle=LargeFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="9 9 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!! " +" ! ! " +"! !" +"! !" +"! !" +"! !" +"! !" +" ! ! " +" !!!!! " +;123456789 +[end] +[_point] +type=0x005 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_500K/City with the range of (0.5, 1] million inhabitants/Non NT +String1=0x01,Ville (>200k) +String2=0x02,Stadt (200t) +String3=0x04,City (>200k) +String4=0x03,Stad (>200k) +String7=0x15,Miasto (>200 tys.) +String8=0x10,Cidade (>200k) +String9=0x05,Città +ExtendedLabels=Y +FontStyle=LargeFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="9 9 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!! " +" ! ! " +"! !" +"! !" +"! !" +"! !" +"! !" +" ! ! " +" !!!!! " +;123456789 +[end] +[_point] +type=0x006 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_200K/City with the range of (200, 500] thousand inhabitants/Non NT +String1=0x01,Ville (>50k) +String2=0x02,Stadt (50t) +String3=0x04,City (>50k) +String4=0x03,Woonplaats (>50k) +String7=0x15,Miasto (>50 tys.) +String8=0x10,Cidade (>50k) +String9=0x05,Città +ExtendedLabels=Y +FontStyle=LargeFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="9 9 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!! " +" ! ! " +"! !" +"! !" +"! !" +"! !" +"! !" +" ! ! " +" !!!!! " +;123456789 +[end] +[_point] +type=0x007 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_100K/City with the range of (100, 200] thousand inhabitants/Non NT +String1=0x01,Ville (>10k) +String2=0x02,Stadt (10t) +String3=0x04,City (>10k) +String4=0x03,Woonplaats (>10k) +String7=0x15,Miasto (>10 tys.) +String8=0x10,Cidade (>10k) +String9=0x05,Città +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#414141 +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x008 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_50K, MEDIUM_CITY/Medium city center, typically 50K-1M inhabitants/Non NT +String1=0x01,Village (>5k) +String2=0x02,Dorf (>5t) +String3=0x04,Village (>5k) +String4=0x03,Dorp (>5k) +String7=0x15,Wieś (>5 tys.) +String8=0x10,Povoado (>5k) +String9=0x05,Cittadina +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#414141 +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x009 +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_20K/City with the range of (20, 50] thousand inhabitants/Non NT +String1=0x01,Village (>5k) +String2=0x02,Dorf (>5t) +String3=0x04,Village (>5k) +String4=0x03,Dorp (>5k) +String7=0x15,Wieś (>5 tys.) +String8=0x10,Povoado (>5k) +String9=0x05,Cittadina +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#414141 +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x00a +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_10K/City with the range of (10, 20] thousand inhabitants/Non NT +String1=0x01,Hameau +String2=0x02,Ort, Weiler +String3=0x04,Hamlet +String4=0x03,Gehucht +String7=0x15,Wioska +String8=0x10,Lugarejo +String9=0x05,Paese +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#737373 +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x00b +subtype=0x00 +;GRMN_TYPE: Political Entities/CITY_5K/City with the range of (5, 10] thousand inhabitants/Non NT +String1=0x01,Nom +String2=0x02,Name +String3=0x04,Name +String4=0x03,Naam +String7=0x15,Nazwa +String8=0x10,Nome +String9=0x05,Borgo +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#737373 +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x021 +subtype=0x00 +;GRMN_TYPE: // +String1=0x01,Sortie +String2=0x02,Ausfahrt +String3=0x04,Ext +String4=0x03,Afrit +String7=0x15,Zjazd +String8=0x10,Saída +String9=0x05,Uscita +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BD3000 +DayXpm="8 4 2 1" Colormode=16 +"! c #BD3000" +" c none" +" !! " +"!!!! " +"!!!! " +" !! " +;12345678 +[end] +[_point] +type=0x021 +subtype=0x0f +;GRMN_TYPE: // +String1=0x01,Sortie +String2=0x02,Ausfahrt +String3=0x04,Ext +String4=0x03,Afrit +String7=0x15,Zjazd +String8=0x10,Saída +String9=0x05,Uscita +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BD3000 +DayXpm="8 4 2 1" Colormode=16 +"! c #BD3000" +" c none" +" !! " +"!!!! " +"!!!! " +" !! " +;12345678 +[end] +[_point] +type=0x02a +subtype=0x00 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT/General restaurant of unknown genre/Non NT +String1=0x01,Restaurant +String2=0x02,Restaurant +String3=0x04,Restaurant +String4=0x03,Restaurant +String7=0x15,Restauracja +String8=0x10,Restaurante +String9=0x05,Ristorante +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x01 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_AMERICAN/American cuisine/Non NT +String1=0x01,Restaurant (Américain) +String2=0x02,Restaurant +String3=0x04,Restaurant (American) +String4=0x03,Restaurant (Amerikaans) +String7=0x15,Restauracja (Amerykańska) +String8=0x10,Restaurante americano +String9=0x05,Ristorante americano +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x02 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_ASIAN/Asian cuisine/Non NT +String1=0x01,Restaurant (Asiatique) +String2=0x02,Restaurant (Asiatisch) +String3=0x04,Restaurant (Asia) +String4=0x03,Restaurant (Aziatisch) +String7=0x15,Restauracja (Azjatycka) +String8=0x10,Restaurante asiático +String9=0x05,Ristorante asiatico +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x03 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_BARBECUE/Barbecue cuisine/Non NT +String1=0x01,Restaurant (Grill) +String2=0x02,Restaurant +String3=0x04,Restaurant (BBQ) +String4=0x03,Restaurant (BBQ) +String7=0x15,Restauracja (Grill) +String8=0x10,Churrascaria +String9=0x05,Ristorante di grigliate +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x04 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_CHINESE/Chinese cuisine/Non NT +String1=0x01,Restaurant (Chinois) +String2=0x02,Restaurant (Chinesisch) +String3=0x04,Restaurant (Chinese) +String4=0x03,Restaurant (Chinees) +String7=0x15,Restauracja (Chińska) +String8=0x10,Restaurante chinês +String9=0x05,Ristorante cinese +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x05 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_DELI/Deli or sandwich shop/Non NT +String1=0x01,Restaurant (Grec) +String2=0x02,Restaurant (Griechisch) +String3=0x04,Restaurant (Greek) +String4=0x03,Restaurant (Delicatesse) +String7=0x15,Restauracja (Grecka) +String8=0x10,Restaurante grego +String9=0x05,Gastronomia +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x06 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_INTRNTNL/International cuisine/Non NT +String1=0x01,Restaurant (International) +String2=0x02,Restaurant +String3=0x04,Restaurant (International) +String4=0x03,Restaurant (Internationaal) +String7=0x15,Restauracja (Międzynarodowa) +String8=0x10,Restaurante Internacional +String9=0x05,Ristorante internazionale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x07 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_FAST_FOOD/Fast food establishment, i.e. McDonalds/Non NT +String1=0x01,Restauration rapide +String2=0x02,Restaurant (Fastfood) +String3=0x04,Restaurant (Fastfood) +String4=0x03,Restaurant (Fastfood) +String7=0x15,Restauracja (Fastfood) +String8=0x10,Lanchonete +String9=0x05,Fast food +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 9 3 1" Colormode=16 +"! c #C77400" +"# c #CD9649" +" c none" +" #!!!!!!!!# " +" #!!!!!!!!!!# " +" !!!!!!!!!!!! " +" !!!!!!!!!!!! " +" " +"#!!!!!!!!!!!!#" +" " +" !!!!!!!!!!!! " +" #!!!!!!!!!!# " +;12345678901234 +[end] +[_point] +type=0x02a +subtype=0x08 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_ITALIAN/Italian cuisine/Non NT +String1=0x01,Restaurant (Italien) +String2=0x02,Restaurant (Italienisch) +String3=0x04,Restaurant (Italian) +String4=0x03,Restaurant (Italiaans) +String7=0x15,Restauracja (Włoska) +String8=0x10,Restaurante italiano +String9=0x05,Ristorante italiano +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x09 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_MEXICAN/Mexican cuisine/Non NT +String1=0x01,Restaurant (Mexicain) +String2=0x02,Restaurant (Mexikanisch) +String3=0x04,Restaurant (Mexican) +String4=0x03,Restaurant (Mexicaans) +String7=0x15,Restauracja (Meksykańska) +String8=0x10,Restaurante mexicano +String9=0x05,Ristorante messicano +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x0a +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_PIZZA/Pizzeria/Non NT +String1=0x01,Pizzeria +String2=0x02,Restaurant (Pizza) +String3=0x04,Restaurant (Pizza) +String4=0x03,Restaurant (Pizza) +String7=0x15,Restauracja (Pizza) +String8=0x10,Pizzaria +String9=0x05,Pizzeria +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x0b +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_SEAFOOD/Seafood restaurant/Non NT +String1=0x01,Restaurant (Poissons/Fruits de mer) +String2=0x02,Restaurant (Fisch) +String3=0x04,Restaurant (Fisch) +String4=0x03,Restaurant (Vis) +String7=0x15,Restauracja (Ryby) +String8=0x10,Restaurante Frutos do Mar +String9=0x05,Ristorante di pesce +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x0c +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_STEAK/Grill and steak brew pub restaurant/Non NT +String1=0x01,Restaurant (Steak) +String2=0x02,Restaurant (Steak) +String3=0x04,Restaurant (Steak) +String4=0x03,Restaurant (Steak) +String7=0x15,Restauracja (Steki) +String8=0x10,Restaurante de carnes +String9=0x05,Ristorante di carne +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x0d +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_DONUTS/Donut shop/Non NT +String1=0x01,Restaurant (Beignets) +String2=0x02,Restaurant (Bagel) +String3=0x04,Restaurant (Donuts) +String4=0x03,Restaurant (Donuts) +String7=0x15,Restauracja (Pączki) +String8=0x10,Doceria +String9=0x05,Panetteria +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="13 12 3 1" Colormode=16 +"! c #CD9344" +"# c #C77400" +" c none" +" ######### " +" ########### " +" ######### #" +" ######### #" +" ######### #" +" ########### " +" ######### " +" ####### " +" ##### " +" " +"########### " +" !#######! " +;1234567890123 +[end] +[_point] +type=0x02a +subtype=0x0e +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_CAFES/Cafe or coffee shop/Non NT +String1=0x01,Café +String2=0x02,Cafe +String3=0x04,Cafe +String4=0x03,Cafe +String7=0x15,Kawiarnia +String8=0x10,Café +String9=0x05,Caffetteria +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="13 12 3 1" Colormode=16 +"! c #CD9344" +"# c #C77400" +" c none" +" ######### " +" ########### " +" ######### #" +" ######### #" +" ######### #" +" ########### " +" ######### " +" ####### " +" ##### " +" " +"########### " +" !#######! " +;1234567890123 +[end] +[_point] +type=0x02a +subtype=0x0f +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_FRENCH/French cuisine/Non NT +String1=0x01,Restaurant (Français) +String2=0x02,Restaurant +String3=0x04,Restaurant (French) +String4=0x03,Restaurant (Frans) +String7=0x15,Restauracja (Francuska) +String8=0x10,Restaurante francês +String9=0x05,Ristorante francese +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x10 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_GERMAN/German cuisine/Non NT +String1=0x01,Restaurant (Allemand) +String2=0x02,Restaurant (Deutsch) +String3=0x04,Restaurant (German) +String4=0x03,Restaurant (Duits) +String7=0x15,Restauracja (Niemiecka) +String8=0x10,Restaurante alemão +String9=0x05,Ristorante tedesco +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x11 +;GRMN_TYPE: Business - Food and Drink/RESTAURANT_BRITISH/British cuisine/Non NT +String1=0x01,Restaurant (Anglais) +String2=0x02,Restaurant +String3=0x04,Restaurant (British) +String4=0x03,Restaurant (Brits) +String7=0x15,Restauracja (Angielska) +String8=0x10,Restaurante inglês +String9=0x05,Ristorante britannico +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x12 +;GRMN_TYPE: // +String1=0x01,Restaurant (Spécialités) +String2=0x02,Restaurant +String3=0x04,Restaurant (Special) +String4=0x03,Restaurant (Speciaal) +String7=0x15,Restauracja (Specjalna) +String8=0x10,Restaurante especial +String9=0x05,Ristorante tipico +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x13 +;GRMN_TYPE: // +String1=0x01,Restaurant (Végétarien) +String2=0x02,Restaurant (Vegetarisch) +String3=0x04,Restaurant (Vegetarian) +String4=0x03,Restaurant (Vegetarisch) +String7=0x15,Restauracja (Wegetariańska) +String8=0x10,Restaurante vegetariano +String9=0x05,Ristorante vegetariano +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02a +subtype=0x14 +;GRMN_TYPE: // +String1=0x01,Restaurant (Régional) +String2=0x02,Restaurant +String3=0x04,Restaurant (Regional) +String4=0x03,Restaurant (Regionaal) +String7=0x15,Restauracja (Regionalna) +String8=0x10,Restaurante regional +String9=0x05,Ristorante regionale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 16 3 1" Colormode=16 +"! c #C77400" +"# c #BD956A" +" c none" +" ! !# ! !! " +" ! !# ! !!!! " +" ! !# ! #!!!!#" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" ! !# ! !!!!!!" +" !!!!!! !!!!!!" +" !!!!!! !!!! " +" !!!! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +" !! !! " +;123456789012345 +[end] +[_point] +type=0x02b +subtype=0x01 +;GRMN_TYPE: Business - Lodging/HOTEL/Hotel/Non NT +String1=0x01,Hôtel +String2=0x02,Hotel +String3=0x04,Hotel +String4=0x03,Hotel +String7=0x15,Hotel +String8=0x10,Hotel +String9=0x05,Albergo +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#0065D5 +DayXpm="21 10 2 1" Colormode=16 +"! c #0095FF" +" c none" +"!! " +"!! " +"!! !! !!!!! " +"!! !! !!!!!! !! " +"!! !! " +"!!!!!!!!!!!!!!!! " +"!!!!!!!!!!!!!!!! " +"!! !! " +"!! !! " +"!! !! " +;123456789012345678901 +[end] +[_point] +type=0x02b +subtype=0x02 +;GRMN_TYPE: Business - Lodging/BED_AND_BREAKFAST/Bed and breakfast inn/Non NT +String1=0x01,Chambre d'hôtes +String2=0x02,Pension +String3=0x04,Guesthouse +String4=0x03,Bed en breakfast +String7=0x15,Pensjonat +String8=0x10,Pensão +String9=0x05,Pensione/Bed and breakfast +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#0065D5 +DayXpm="17 16 3 1" Colormode=16 +"! c #0095FF" +"# c #7BCAFF" +" c none" +" # " +" ### " +" ####### " +" ########### " +" ############# " +" ######### " +"!! ######### " +"!! ######### " +"!! !!!####### " +"!! !!!#!!!!!! !!" +"!! !!!#!!!!!!! !!" +"!! ### ### !!" +"!!!!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!!!!" +"!! ### ### !!" +"!! !!" +;12345678901234567 +[end] +[_point] +type=0x02b +subtype=0x05 +;GRMN_TYPE: // +String1=0x01,Camping +String2=0x02,Campingplatz +String3=0x04,Campsite +String4=0x03,Camping +String7=0x15,Kemping +String8=0x10,Camping +String9=0x05,Campeggio +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#0095FF +DayXpm="22 13 2 1" Colormode=16 +"! c #0095FF" +" c none" +" !!!!!!!!!!!!!! " +"!!!!!!!!!!!!!!!! " +"!! !!!! !!! " +"!! !!!! !! " +"!! !!!! !! " +"!!!!!!!!!!!!!!!!! " +"!!!!!!!!!!!!!!!!! " +"!!!!!!!!!!!!!!!!! " +"!!!!!! !!!!!!! " +"!!!!! !! !!!!!!!!! !" +" !!!! !!!! !!!!!!!!! !" +" !!!! " +" !! " +;1234567890123456789012 +[end] +[_point] +type=0x02b +subtype=0x06 +;GRMN_TYPE: // +String1=0x01,Abri +String2=0x02,Schutzhütte +String3=0x04,Shelter +String4=0x03,Schuilplek +String7=0x15,Schronienie +String8=0x10,Cabana +String9=0x05,Rifugio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="16 16 2 1" Colormode=16 +"! c #666666" +" c none" +" " +" ! ! " +" ! ! " +" ! ! " +" ! ! " +" ! ! " +" ! !! ! " +" !!!! " +" !!! !!! " +" !!! !!! " +" ! ! " +" ! ! " +" ! ! " +" ! ! " +" ! ! " +" ! ! " +;1234567890123456 +[end] +[_point] +type=0x02b +subtype=0x07 +;GRMN_TYPE: // +String1=0x01,Refuge +String2=0x02,Bergschutzhütte +String3=0x04,Shelter +String4=0x03,Bergschuilhut +String7=0x15,Schronisko +String8=0x10,Cabana +String9=0x05,Bivacco +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#0065D5 +DayXpm="14 14 2 1" Colormode=16 +"! c #0095FF" +" c none" +" !! " +" !! " +" !!!!! " +" !!!!!!!! " +" !!!!! !!!!! " +"!!! !! !!!" +"!!! !! !!!" +" !! !! !! " +" !! !! !! " +" !! !!!!!! !! " +" !! !!!!!! !! " +" !! !! !! !! " +" !! !! !! !! " +" !! !!!!!! !! " +;12345678901234 +[end] +[_point] +type=0x02c +subtype=0x01 +;GRMN_TYPE: Business - Attractions/THEME_PARK/Amusement or theme park, i.e. Disneyland/Non NT +String1=0x01,Parc d'attractions +String2=0x02,Freizeitpark +String3=0x04,Themepark +String4=0x03,Attractiepark +String7=0x15,Park rozrywki +String8=0x10,Parque temático / diversão +String9=0x05,Parco a tema +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#006500 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x02c +subtype=0x02 +;GRMN_TYPE: Business - Attractions/MUSEUM/Museum or historical society/Non NT +String1=0x01,Musée +String2=0x02,Museum +String3=0x04,Museum +String4=0x03,Museum +String7=0x15,Muzeum +String8=0x10,Museu +String9=0x05,Museo +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="20 13 2 1" Colormode=16 +"! c #734A08" +" c none" +" !! " +" !!!!!! " +" !!!!!!!!!! " +" !!!!!!!!!!!!!! " +" " +" !!!!!!!!!!!!!! " +" !!! !!!! !!! " +" !! !! !! " +" !! !! !! " +" !! !! !! " +" !! !! !! " +" !!!!!!!!!!!!!! " +"!!!!!!!!!!!!!!!! " +;12345678901234567890 +[end] +[_point] +type=0x02c +subtype=0x03 +;GRMN_TYPE: Business - Attractions/LIBRARY/Library/Non NT +String1=0x01,Bibliothèque +String2=0x02,Bibliothek +String3=0x04,Library +String4=0x03,Bibliotheek +String7=0x15,Biblioteka +String8=0x10,Biblioteca +String9=0x05,Biblioteca +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="20 12 2 1" Colormode=16 +"! c #734A08" +" c none" +"! ! " +"! !!! !!! ! " +"! !!!!! !!!!! ! " +"! !!!!! !!!!! ! " +"! !!!!! !!!!! ! " +"! !!!!! !!!!! ! " +"! !!!!! !!!!! ! " +"! !!!! !!!! ! " +"! !! !! ! " +"!!! !!! " +" !! !! " +" !!!!! " +;12345678901234567890 +[end] +[_point] +type=0x02c +subtype=0x04 +;GRMN_TYPE: Business - Attractions/LANDMARK/Landmark/Non NT +String1=0x01,Point de vue, œuvre d'art, attraction +String2=0x02,Aussichtspunkt, Kunstwerk, Attraktion +String3=0x04,Artwork,Attraction, Viewpoint +String4=0x03,Bezienswaardigheid +String7=0x15,Sztuka, Atrakcja, Punkt widokowy +String8=0x10,Atração +String9=0x05,Opera/Veduta +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="12 17 2 1" Colormode=16 +"! c #734A08" +" c none" +" !! " +" !!!! " +" !!!! " +" !! " +" " +" !!!!!! " +" !!!!!! " +" !!!!!! " +" !!!! " +" !! " +" !! " +" !! " +" " +" !!!!!! " +" " +"!!!!!!!! " +"!!!!!!!! " +;123456789012 +[end] +[_point] +type=0x02c +subtype=0x05 +;GRMN_TYPE: Business - Attractions/SCHOOL/School/Non NT +String1=0x01,École +String2=0x02,Schule +String3=0x04,Education +String4=0x03,Educatie +String7=0x15,Edukacja +String8=0x10,Escola +String9=0x05,Scuola +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#4D4D00 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x02c +subtype=0x06 +;GRMN_TYPE: Business - Attractions/GARDEN, PARK/Garden, park/Non NT +String1=0x01,Aire de jeu +String2=0x02,Spielplatz +String3=0x04,Playground +String4=0x03,Speelplaats +String7=0x15,Plac zabaw +String8=0x10,Parque infantil +String9=0x05,Parco giochi +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="18 17 2 1" Colormode=16 +"! c #009500" +" c none" +" !! " +" !!!! " +" !!!! !! " +" !! !!!! " +" !!!! " +" !! !! " +" !! " +" !!!! !! " +" !!!! !! " +"!!!!!!! !!!! " +"!!!!!!!!!! !!!! " +" !!!!!!!!!!!!!" +" !! !!!!!!" +" !! " +" !!!! " +" !!!!!! " +" !!!!!!!! " +;123456789012345678 +[end] +[_point] +type=0x02c +subtype=0x07 +;GRMN_TYPE: Business - Attractions/ZOO/Zoo/Non NT +String1=0x01,Zoo +String2=0x02,Zoo +String3=0x04,Zoo +String4=0x03,Dierentuin +String7=0x15,Zoo +String8=0x10,Zoológico +String9=0x05,Zoo/Acquario +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#660033 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x02c +subtype=0x08 +;GRMN_TYPE: Business - Attractions/ARENA/Arena or track/Non NT +String1=0x01,Sport +String2=0x02,Sport +String3=0x04,Sport +String4=0x03,Sport +String7=0x15,Sport +String8=0x10,Esporte +String9=0x05,Sport +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#3D9171 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x02c +subtype=0x09 +;GRMN_TYPE: Business - Attractions/HALL/Auditorium or hall/Non NT +String1=0x01,Auditorium +String2=0x02,Konferenzzentrum +String3=0x04,Conference +String4=0x03,Conferentiecentrum +String7=0x15,Centrum konferencyjne +String8=0x10,Conferência +String9=0x05,Fiera +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x02c +subtype=0x0a +;GRMN_TYPE: Business - Attractions/WINERY/Winery/Non NT +String1=0x01,Cave à vin +String2=0x02,Weinkeller +String3=0x04,Winery +String4=0x03,Wijnmaker +String7=0x15,Winiarnia +String8=0x10,Adega +String9=0x05,Cantina +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x02c +subtype=0x0b +;GRMN_TYPE: Business - Attractions/PLACE_OF_WORSHIP/Religious place of worship/Non NT +String1=0x01,Lieu de culte +String2=0x02,Religiöses Gebäude +String3=0x04,Religious building +String4=0x03,Gebedshuis +String7=0x15,Budynek religijny +String8=0x10,Edifício religioso +String9=0x05,Luogo di culto +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#000000 +DayXpm="12 15 2 1" Colormode=16 +"! c #000000" +" c none" +" !! " +" !!!! " +" !!!! " +" !! " +" ! " +"!! !!!! " +" !!!!!!!! " +" !! !!!! " +" !!!! " +" !!!! " +" !!!! " +" !!! " +" !!! " +" !!!!!!! " +" !!!!!!!!" +;123456789012 +[end] +[_point] +type=0x02c +subtype=0x0c +;GRMN_TYPE: // +String1=0x01,Volcan +String2=0x02,Vulkan +String3=0x04,Vulcano +String4=0x03,Vulkaan +String7=0x15,Wulkan +String8=0x10,Vulcão +String9=0x05,Vulcano +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BD3000 +DayXpm="14 10 2 1" Colormode=16 +"! c #D40000" +" c none" +" ! " +" ! " +" !!! " +" !!! " +" !!!!! " +" !!!!! " +" !!!!!!! " +" !!!!!!! " +"!!!!!!!!! " +"!!!!!!!!! " +;12345678901234 +[end] +[_point] +type=0x02c +subtype=0x0d +;GRMN_TYPE: Business - Attractions/POW_MOSQUE/Religious place of worship/Non NT +String1=0x01,Attraction touristique +String2=0x02,Touristenattraktion +String3=0x04,Tourist attraction +String4=0x03,Touristische attractie +String7=0x15,Atrakcja turystyczna +String8=0x10,Atração turística +String9=0x05,Attrazione turistica +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#660033 +DayXpm="10 5 2 1" Colormode=16 +"! c #660033" +" c none" +" !!! " +"!!!!! " +"!!!!! " +"!!!!! " +" !!! " +;1234567890 +[end] +[_point] +type=0x02d +subtype=0x01 +;GRMN_TYPE: Business - Entertainment/LIVE_THEATER/Live theater/Non NT +String1=0x01,Théâtre +String2=0x02,Theater +String3=0x04,Theatre +String4=0x03,Theater +String7=0x15,Teatr +String8=0x10,Teatro +String9=0x05,Teatro +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="18 13 2 1" Colormode=16 +"! c #734A08" +" c none" +"!!!!!!!! " +"! ! " +"! ! ! ! " +"! !!!!!!!! " +"! ! !!!!!!!!! " +"! ! !!! !! !! " +"! !! !!!!!!!! " +"! !!!!!!!! " +" ! !!! !!! " +" !!!!!! !! !! " +" !! !! !! " +" !!!!!! " +" !!!! " +;123456789012345678 +[end] +[_point] +type=0x02d +subtype=0x02 +;GRMN_TYPE: Business - Entertainment/BAR/Bar or club/Non NT +String1=0x01,Bar +String2=0x02,Bar +String3=0x04,Bar +String4=0x03,Bar +String7=0x15,Bar +String8=0x10,Bar +String9=0x05,Bar +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="13 12 2 1" Colormode=16 +"! c #C77400" +" c none" +"!!!!!!!!!!!!!" +" !!!!!!!!!!! " +" !!!!!!!!! " +" !!!!!!! " +" !!!!! " +" !!! " +" ! " +" ! " +" ! " +" ! " +" !!! " +" !!!!!!! " +;1234567890123 +[end] +[_point] +type=0x02d +subtype=0x03 +;GRMN_TYPE: Business - Entertainment/MOVIE_THEATER/Movie theater or cinema/Non NT +String1=0x01,Cinéma +String2=0x02,Kino +String3=0x04,Cinema +String4=0x03,Bioscoop +String7=0x15,Kino +String8=0x10,Cinema +String9=0x05,Cinema +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="16 13 2 1" Colormode=16 +"! c #734A08" +" c none" +"!!!!!!!!!!!! " +"! ! ! ! " +"!!! !!! " +"! ! ! ! " +"!!! !!! " +"! ! ! ! " +"!!!!!!!!!!!! " +"! ! ! ! " +"!!! !!! " +"! ! ! ! " +"!!! !!! " +"! ! ! ! " +"!!!!!!!!!!!! " +;1234567890123456 +[end] +[_point] +type=0x02d +subtype=0x04 +;GRMN_TYPE: Business - Entertainment/CASINO/Casino/Non NT +String1=0x01,Casino +String2=0x02,Casino +String3=0x04,Casino +String4=0x03,Casino +String7=0x15,Kasyno +String8=0x10,Cassino +String9=0x05,Sala da gioco +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="16 15 2 1" Colormode=16 +"! c #734A08" +" c none" +" !!! " +" !!!!! " +" !!! !! " +" !!!! !!! " +"!!!!!!!!!!! " +"!!!!!!! " +"!! !! !!!!!!! " +"!! !! !!!!!!!!!" +" !!!!! !!!!! !!" +" !!!! !!!!! !!" +" !!! !!!!!!!!!" +" !! !! !!!!!" +" !! !!!!!" +" !!!!!!!!!" +" !!!!!!! " +;1234567890123456 +[end] +[_point] +type=0x02d +subtype=0x05 +;GRMN_TYPE: Business - Entertainment/GOLF_COURSE/Golf course/Non NT +String1=0x01,Golf +String2=0x02,Golfplatz +String3=0x04,Golf course +String4=0x03,Golfbaan +String7=0x15,Golf +String8=0x10,Campo de golfe +String9=0x05,Campo da golf +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#009500 +DayXpm="19 18 2 1" Colormode=16 +"! c #009500" +" c none" +" !! " +"! !! " +"!! !! " +"!!! !! !! " +"!!!! !!!! !! " +"!!!!! !!!! !! " +"!!!! !! !! " +"!!! !! " +"!! !!!! " +"! !!!! " +"! !!!! " +"! !!!! " +"! !!!! " +"! !! !! " +"! !! !! " +"! !! !! " +"! !! !! " +"! !! !! " +;1234567890123456789 +[end] +[_point] +type=0x02d +subtype=0x06 +;GRMN_TYPE: Business - Entertainment/SKI_CENTER/Ski area or resort/Non NT +String1=0x01,Ski +String2=0x02,Ski +String3=0x04,Ski +String4=0x03,Ski +String7=0x15,Narty +String8=0x10,Esqui +String9=0x05,Centro sciistico +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#009500 +DayXpm="16 13 2 1" Colormode=16 +"! c #009500" +" c none" +" ! " +" !!! " +" !!!!! ! " +" !!!!!! !!!" +" !! !!! !!!" +" !! !! ! " +"!! !!! ! " +"!!!! !! !! " +" !!!! " +" !!!! " +" !!!! " +" !!!! ! " +" !!!!! " +;1234567890123456 +[end] +[_point] +type=0x02d +subtype=0x07 +;GRMN_TYPE: Business - Entertainment/BOWLING/Bowling alley/Non NT +String1=0x01,Bowling +String2=0x02,Bowling +String3=0x04,Bowling alley +String4=0x03,Bowlingbaan +String7=0x15,Kręgle +String8=0x10,Boliche +String9=0x05,Sala da bowling +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#009500 +DayXpm="18 16 2 1" Colormode=16 +"! c #009500" +" c none" +" !! " +" !!!! " +" !!!! " +" !! " +" " +" !! " +" " +" !!! !! " +" !!! !!! !!!! " +" !!! !! !!!!!! " +"!!!!!!!!! !!!!! " +"!!!! ! ! !!!!! " +"!!! ! !! !!!!! " +" !!!!!!! !!!!!! " +" !!!!!!! !!!!!! " +" !!! !!!! " +;123456789012345678 +[end] +[_point] +type=0x02d +subtype=0x08 +;GRMN_TYPE: Business - Entertainment/ICE_SKATING/Ice skating rink/Non NT +String1=0x01,Patinoire +String2=0x02,Eisbahn +String3=0x04,Ice skating +String4=0x03,Schaatsbaan +String7=0x15,Lodowisko +String8=0x10,Patinação no gelo +String9=0x05,Pista di pattinaggio +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#009500 +DayXpm="15 12 3 1" Colormode=16 +"! c #009500" +"# c #009500" +" c none" +" ##### " +" #!!!! " +" #!!!! " +" #!!!!! " +" #!!!!!!! " +" #!!!!!!!!! " +" #!!!!!!!!!! " +" !!!!!!!!!! " +" ! ! " +" ! ! #" +"###!!!!!!!!!###" +"############## " +;123456789012345 +[end] +[_point] +type=0x02d +subtype=0x09 +;GRMN_TYPE: Business - Entertainment/SWIMMING_POOL/Swimming pool/Non NT +String1=0x01,Piscine +String2=0x02,Schwimmbad +String3=0x04,Swimmingpool +String4=0x03,Zwembad +String7=0x15,Basen +String8=0x10,Piscina +String9=0x05,Piscina +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#009500 +DayXpm="19 13 2 1" Colormode=16 +"! c #009500" +" c none" +" !! " +" !!!! " +" !!! " +" !! !! " +" !!! !!!! " +" !!! !!!! " +" !!!!!! !! " +" !!!!!!!!! " +" ! !!! ! " +" " +" !!! !!! !!! " +"!!!!!!!!!!!!!!! " +"! !! !! ! " +;1234567890123456789 +[end] +[_point] +type=0x02d +subtype=0x0a +;GRMN_TYPE: Business - Entertainment/FITNESS_CENTER/Fitness center or gym/Non NT +String1=0x01,Centre sportif +String2=0x02,Sportzentrum +String3=0x04,Sports centre +String4=0x03,Sportcentrum +String7=0x15,Centrum sportowe +String8=0x10,Centro esportivo +String9=0x05,Centro sportivo +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#006500 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x02d +subtype=0x0b +;GRMN_TYPE: Business - Entertainment/SPORT_AIRPORT/Sport or R-C airport/Non NT +String1=0x01,Aéroport +String2=0x02,Flughafen +String3=0x04,Airport +String4=0x03,Luchthaven +String7=0x15,Lotnisko +String8=0x10,Aeroporto +String9=0x05,Aerodromo +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#8461C4 +DayXpm="16 16 2 1" Colormode=16 +"! c #8461C4" +" c none" +" !! " +" !! " +" !! " +" !! " +" !! " +" !!!! " +" !!!!!!!! " +" !!!!!!!!!!!! " +" !!!!!!!!!!!!!! " +"!!!! !! !!!!" +" !! " +" !! " +" !! " +" !!!! " +" !!!!!!!! " +" !!!! !!!! " +;1234567890123456 +[end] +[_point] +type=0x02e +subtype=0x01 +;GRMN_TYPE: Business - Shopping/DEPARTMENT_STORE/Department store/Non NT +String1=0x01,Hypermarché +String2=0x02,Kaufhaus +String3=0x04,Mall +String4=0x03,Warenhuis +String7=0x15,Centrum handlowe +String8=0x10,Shopping Center +String9=0x05,Grande magazzino +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 17 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!!!!! " +" !!!!!! " +" ! ! " +" ! ! " +" ! ! " +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +"!!!!!! !!!!!!" +"!!!!! !! !!!!!" +"!!!!! ! !!!!!" +"!!!!! !!!!!!" +"!!!! !! !!!!" +"!!!! !!! !!!!" +"!!!! !!!" +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +;12345678901234 +[end] +[_point] +type=0x02e +subtype=0x02 +;GRMN_TYPE: Business - Shopping/GROCERY_STORE/Grocery store/Non NT +String1=0x01,Boulangerie +String2=0x02,Bäckerei +String3=0x04,Backery +String4=0x03,Bakker +String7=0x15,Piekarnia +String8=0x10,Padaria +String9=0x05,Alimentari/Drogheria +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 8 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" ! ! ! !! " +" !! !! !! ! " +" !! ! ! !! " +"!!! !! !! !!!" +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +" !!!!!!!!!!!! " +;12345678901234 +[end] +[_point] +type=0x02e +subtype=0x03 +;GRMN_TYPE: Business - Shopping/GENERAL_STORE/General store/Non NT +String1=0x01,Supermarché +String2=0x02,Supermarkt +String3=0x04,Generic store +String4=0x03,Algemeen +String7=0x15,Supermarket +String8=0x10,Loja +String9=0x05,Supermercato +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="13 11 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" ! " +" !! " +" !! " +" ! " +"!!!!!!!!!!!!!" +"!! ! ! !!" +" !!!!!!!!!!! " +" ! ! ! ! " +" !!!!!!!!!!! " +" !! ! ! " +" !!!!!!!!! " +;1234567890123 +[end] +[_point] +type=0x02e +subtype=0x04 +;GRMN_TYPE: Business - Shopping/SHOPPING_CENTER/Shopping center or mall/Non NT +String1=0x01,Centre commercial +String2=0x02,Einkaufszentrum +String3=0x04,Shopping center +String4=0x03,Winkelcentrum +String7=0x15,Centrum handlowe +String8=0x10,Shopping Center +String9=0x05,Centro commerciale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 17 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!!!!! " +" !!!!!! " +" ! ! " +" ! ! " +" ! ! " +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +"!!!!!! !!!!!!" +"!!!!! !! !!!!!" +"!!!!! ! !!!!!" +"!!!!! !!!!!!" +"!!!! !! !!!!" +"!!!! !!! !!!!" +"!!!! !!!" +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +;12345678901234 +[end] +[_point] +type=0x02e +subtype=0x05 +;GRMN_TYPE: Business - Shopping/PHARMACY/Pharmacy/Non NT +String1=0x01,Pharmacie +String2=0x02,Apotheke +String3=0x04,Pharmacy +String4=0x03,Apotheek +String7=0x15,Apteka +String8=0x10,Farmácia +String9=0x05,Farmacia +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="10 14 2 1" Colormode=16 +"! c #BF0000" +" c none" +" !!!!!! " +" !!!!!! " +" !!!!!! " +" " +" !!!!!!!! " +"!!!!!!!!!!" +"!!!! !!!!" +"!!!! !!!!" +"!! !!" +"!! !!" +"!!!! !!!!" +"!!!! !!!!" +"!!!!!!!!!!" +"!!!!!!!!!!" +;1234567890 +[end] +[_point] +type=0x02e +subtype=0x06 +;GRMN_TYPE: Business - Shopping/CONVENIENCE_STORE/Convenience store/Non NT +String1=0x01,Station-service +String2=0x02,Tankstellenshop +String3=0x04,Gasstation shop +String4=0x03,Tankstation shop +String7=0x15,Sklep na stacji paliw +String8=0x10,Loja de conveniência +String9=0x05,Negozio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="5 5 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! " +"!!!!!" +"!!!!!" +"!!!!!" +" !!! " +;12345 +[end] +[_point] +type=0x02e +subtype=0x07 +;GRMN_TYPE: Business - Shopping/CLOTHING_RETAIL/Clothing store/Non NT +String1=0x01,Magasin de vêtements +String2=0x02,Bekleidungsgeschäft +String3=0x04,Clothing store +String4=0x03,Kleding +String7=0x15,Sklep odzieżowy +String8=0x10,Loja de roupas +String9=0x05,Negozio di abbigliamento +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 11 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! !!! " +" !!!!! !!!!! " +" !!!!!!!!!!!! " +"!!!!!!!!!!!!!!" +"!!!!!!!!!!!!!!" +" !!!!!!!! " +" !!!!!!!! " +" !!!!!!!! " +" !!!!!!!! " +" !!!!!!!! " +" !!!!!!!! " +;12345678901234 +[end] +[_point] +type=0x02e +subtype=0x08 +;GRMN_TYPE: Business - Shopping/HOME_AND_GARDEN_STORE/House and garden store/Non NT +String1=0x01,Jardinerie +String2=0x02,Gartencentrum +String3=0x04,Garden center +String4=0x03,Huis- en tuin +String7=0x15,Centrum ogrodnicze +String8=0x10,Loja de jardinagem +String9=0x05,Negozio di giardinaggio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="16 15 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! " +" !!!!! " +" !! !! " +" !! !!" +" !! !! !!" +" !!! !! !!" +"!!!! !!!!!!!!!" +"!!!!! !!!!!!!!!" +" !!! !!!!!!!!!" +" !!!!!!!!!!!!" +" !!!!!!!!!! " +" !!!!!!!!! " +" !!!!!!!! " +" !!!!!! " +" !!!!! " +;1234567890123456 +[end] +[_point] +type=0x02e +subtype=0x09 +;GRMN_TYPE: Business - Shopping/HOME_FURNISHINGS_STORE/Furniture store/Non NT +String1=0x01,Magasin d’ameublement +String2=0x02,Möbelgeschäft +String3=0x04,Furniture +String4=0x03,Meubels +String7=0x15,Meble +String8=0x10,Loja de móveis +String9=0x05,Negozio di arredamento +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 10 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!!!!!!!!! " +" !!!!!!!!!!!! " +" !!!!!!!! " +"!! !!!!!!!! !!" +"!!! !!!!!! !!!" +" !! !! " +" !!!!!!!!!!!! " +" !!!!!!!!!!!! " +" !!!!!!!!!!!! " +" !!! !!! " +;12345678901234 +[end] +[_point] +type=0x02e +subtype=0x0a +;GRMN_TYPE: Business - Shopping/SPECIALTY_RETAIL/Specialty store/Non NT +String1=0x01,Magasin +String2=0x02,Geschäft +String3=0x04,Shop +String4=0x03,Winkel +String7=0x15,Sklep +String8=0x10,Loja +String9=0x05,Negozio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="5 5 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! " +"!!!!!" +"!!!!!" +"!!!!!" +" !!! " +;12345 +[end] +[_point] +type=0x02e +subtype=0x0b +;GRMN_TYPE: Business - Shopping/SOFTWARE_RETAIL/Computer software store/Non NT +String1=0x01,Magasin d’électronique +String2=0x02,Computerhandel +String3=0x04,Electronics +String4=0x03,Elektronica +String7=0x15,Elektronika +String8=0x10,Loja de eletrônicos +String9=0x05,Negozio di elettronica +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 14 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !! !! " +" !! !! " +" !!!! " +" !! " +" !!!!!!!!!!!! " +"!!!!!!!!!!!!!!" +"!!! !! !" +"!! ! !" +"!! !!!!" +"!! ! !" +"!! ! !" +"!!! !!!!!" +"!!!!!!!!!!!!!!" +" !!!!!!!!!!!! " +;12345678901234 +[end] +[_point] +type=0x02e +subtype=0x0c +;GRMN_TYPE: // +String1=0x01,Magasin +String2=0x02,Geschäft +String3=0x04,Shop +String4=0x03,Winkel +String7=0x15,Sklep +String8=0x10,Loja +String9=0x05,Negozio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="5 5 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! " +"!!!!!" +"!!!!!" +"!!!!!" +" !!! " +;12345 +[end] +[_point] +type=0x02f +subtype=0x01 +;GRMN_TYPE: Business - Services/GAS_STATION/Gas, fuel station/Non NT +String1=0x01,Station-essence +String2=0x02,Tankstelle +String3=0x04,Gas station +String4=0x03,Tankstation +String7=0x15,Stacja paliw +String8=0x10,Posto de combustível +String9=0x05,Stazione di servizio +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#3E8BFD +DayXpm="12 14 3 1" Colormode=16 +"! c #0095FF" +"# c #F2EFE9" +" c none" +" !! " +" !!!!!! !!!" +"!!!!!!!! !!" +"! ! !" +"! !! #!" +"! ! !#!" +"!!!!!!!! !#!" +"!!!!!!!!#!#!" +"!!!!!!!!#!#!" +"!!!!!!!!#!#!" +"!!!!!!!!#! !" +"!!!!!!!!# ! " +"!!!!!!!!####" +"!!!!!!!! " +;123456789012 +[end] +[_point] +type=0x02f +subtype=0x02 +;GRMN_TYPE: Business - Services/AUTO_RENTAL/Car rental location/Non NT +String1=0x01,Location de voitures +String2=0x02,Autoverleih +String3=0x04,Car rental +String4=0x03,Autoverhuur +String7=0x15,Wypożyczalnia samochodów +String8=0x10,Aluguel de carro +String9=0x05,Noleggio veicoli +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="10 14 2 1" Colormode=16 +"! c #0095FF" +" c none" +" !!! " +"! !!!!!! " +"!! !!!!!!!" +" !! " +" " +" " +" !!!!!! " +" ! ! " +" !! !! " +"!!!!!!!!!!" +"! !!!! !" +"! !!!! !" +"!!!!!!!!!!" +" !! !! " +;1234567890 +[end] +[_point] +type=0x02f +subtype=0x03 +;GRMN_TYPE: Business - Services/AUTO_REPAIR/Car repair, mechanic/Non NT +String1=0x01,Réparation automobile +String2=0x02,Autowerkstatt +String3=0x04,Garage +String4=0x03,Autogarage +String7=0x15,Garaż +String8=0x10,Garagem +String9=0x05,Riparazione veicoli +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="10 14 2 1" Colormode=16 +"! c #734A08" +" c none" +" !!! " +" !!!!!! " +" !!!!!! " +" !!! " +" " +" " +" !!!!!! " +" ! ! " +" !! !! " +" !!!!!!!! " +"! !!!! !" +"! !!!! !" +"!!!!!!!!!!" +" !! !! " +;1234567890 +[end] +[_point] +type=0x02f +subtype=0x04 +;GRMN_TYPE: Business - Services/AIRPORT/Airport/Non NT +String1=0x01,Aéroport +String2=0x02,Flughafen +String3=0x04,Airport +String4=0x03,Luchthaven +String7=0x15,Lotnisko +String8=0x10,Aeroporto +String9=0x05,Aerodromo +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#8461C4 +DayXpm="16 16 2 1" Colormode=16 +"! c #8461C4" +" c none" +" !! " +" !! " +" !! " +" !! " +" !! " +" !!!! " +" !!!!!!!! " +" !!!!!!!!!!!! " +" !!!!!!!!!!!!!! " +"!!!! !! !!!!" +" !! " +" !! " +" !! " +" !!!! " +" !!!!!!!! " +" !!!! !!!! " +;1234567890123456 +[end] +[_point] +type=0x02f +subtype=0x05 +;GRMN_TYPE: Business - Services/POST_OFFICE/Post office/Non NT +String1=0x01,Poste +String2=0x02,Postamt +String3=0x04,Postoffice +String4=0x03,Postkantoor +String7=0x15,Poczta +String8=0x10,Correio +String9=0x05,Poste +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 10 2 1" Colormode=16 +"! c #734A08" +" c none" +"!!!!!!!!!!!!!!" +" !!!!!!!!!! " +"! !!!!!!!! !" +"!! !!!!!! !!" +"!!! !!!! !!!" +"!!!! !! !!!!" +"!!! ! ! !!!" +"!! !!! !!! !!" +"! !!!!!!!!!! !" +" !!!!!!!!!!!! " +;12345678901234 +[end] +[_point] +type=0x02f +subtype=0x06 +;GRMN_TYPE: Business - Services/BANK/Bank/Non NT +String1=0x01,Banque +String2=0x02,Bank +String3=0x04,Bank +String4=0x03,Bank +String7=0x15,Bank +String8=0x10,Banco +String9=0x05,Banca/Sportello +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="16 11 2 1" Colormode=16 +"! c #734A08" +" c none" +"!!!!!!!!!!!!!! " +"!!! !!! " +"!! !! !! " +"! !!!! ! " +"! !!!! ! " +"! !!!! ! " +"!! !! !! " +"!!! !!! " +"!!!!!!!!!!!!!! " +" " +"!!!!!!!!!!!!!! " +;1234567890123456 +[end] +[_point] +type=0x02f +subtype=0x07 +;GRMN_TYPE: Business - Services/DEALER/Dealer of manufactured goods/Non NT +String1=0x01,Concessionnaire automobile +String2=0x02,Autohändler +String3=0x04,Car dealer +String4=0x03,Autodealer +String7=0x15,Sprzedaż samochodów +String8=0x10,Revenda de carros +String9=0x05,Vendita veicoli +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BD30B4 +DayXpm="13 10 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!!!!!! " +" !! !! " +" ! ! " +" ! ! " +" !!!!!!!!!!! " +"!! !!!!! !!" +"!! !!!!! !!" +"!!!!!!!!!!!!!" +" !!! !!! " +" !!! !!! " +;1234567890123 +[end] +[_point] +type=0x02f +subtype=0x08 +;GRMN_TYPE: Business - Services/GND_TRANSPORT/Ground transportation status, i.e. a bus station/Non NT +String1=0x01,Gare routière +String2=0x02,Busbahnhof +String3=0x04,Busstation +String4=0x03,Busstation +String7=0x15,Przystanek autobusowy +String8=0x10,Estação de ônibus +String9=0x05,Stazione +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#0065FF +DayXpm="21 16 3 1" Colormode=16 +"! c #0095FF" +"# c #F2EFE9" +" c none" +"!!!!!!!!!!!!!!!! " +"!!!!########!!!! " +"!!!###!!!!###!!! " +"!!############!! " +"!!##!!!!!!!!##!! " +"!!##!!!!!!!!##!! " +"!!##!!!!!!!!##!! " +"!!##!!!!!!!!##!! " +"!!############!! " +"!!############!! " +"!!#!!######!!#!! " +"!!#!!######!!#!! " +"!!############!! " +"!!!##!!!!!!##!!! " +"!!!##!!!!!!##!!! " +"!!!!!!!!!!!!!!!! " +;123456789012345678901 +[end] +[_point] +type=0x02f +subtype=0x09 +;GRMN_TYPE: Business - Services/MARINA/Marina/Non NT +String1=0x01,Magasin accastillage et bateaux +String2=0x02,Bootsgeschäft +String3=0x04,Yacht shop +String4=0x03,Botenwinkel +String7=0x15,Sklep żeglarski +String8=0x10,Loja de iates +String9=0x05,Assistenza imbarcazioni +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="5 5 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! " +"!!!!!" +"!!!!!" +"!!!!!" +" !!! " +;12345 +[end] +[_point] +type=0x02f +subtype=0x0a +;GRMN_TYPE: Business - Services/WRECKER_SERVICE/Wrecker service/Non NT +String1=0x01,Dépanneuse +String2=0x02,Schleppdienst +String3=0x04,Wrecker service +String4=0x03,Sleepdienst +String7=0x15,Holowanie +String8=0x10,Guincho +String9=0x05,Soccorso stradale +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="5 5 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !!! " +"!!!!!" +"!!!!!" +"!!!!!" +" !!! " +;12345 +[end] +[_point] +type=0x02f +subtype=0x0b +;GRMN_TYPE: Business - Services/PARKING/Parking/Non NT +String1=0x01,Parking +String2=0x02,Parking +String3=0x04,Parking +String4=0x03,Parkeerplaats +String7=0x15,Parking +String8=0x10,Estacionamento +String9=0x05,Parcheggio +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#3E8BFD +DayXpm="15 13 2 1" Colormode=16 +"! c #0095FF" +" c none" +"!!!!!!! " +"!!!!!!!! " +"!! !!! " +"!! !! " +"!! !! " +"!! !!! " +"!!!!!!!! " +"!!!!!!! " +"!! " +"!! " +"!! " +"!! " +"!! " +;123456789012345 +[end] +[_point] +type=0x02f +subtype=0x0c +;GRMN_TYPE: Business - Services/REST_AREA_TOURIST_INFO/Rest area, Tourist information/Non NT +String1=0x01,WC +String2=0x02,WC +String3=0x04,WC +String4=0x03,Toilet +String7=0x15,Toaleta +String8=0x10,Banheiro +String9=0x05,Servizi igienici +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 14 2 1" Colormode=16 +"! c #734A08" +" c none" +" ! " +" !! ! !! " +" !!!! ! !!!! " +" !! ! !! " +" ! " +" !!!! ! !!!!!!" +" !!!! ! !!!!!!" +" !! ! !!!! " +" !! ! !!!! " +" !!!! ! !! " +"!!!!!! ! !! " +" !! ! !! " +" !! ! !! " +" !! ! !! " +;123456789012345 +[end] +[_point] +type=0x02f +subtype=0x0d +;GRMN_TYPE: Business - Services/AUTO_CLUB/Automobile club/Non NT +String1=0x01,Club automobile +String2=0x02,Automobilclub +String3=0x04,Automobile club +String4=0x03,Auto club +String7=0x15,Klub samochodowy +String8=0x10,Clube automobilístico +String9=0x05,Amministrazione veicoli +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x02f +subtype=0x0e +;GRMN_TYPE: Business - Services/CAR_WASH/Car wash/Non NT +String1=0x01,Lavage-auto +String2=0x02,Autowaschanlage +String3=0x04,Carwash +String4=0x03,Autowasstraat +String7=0x15,Myjnia +String8=0x10,Lava jato +String9=0x05,Lavaggio veicoli +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="10 14 2 1" Colormode=16 +"! c #734A08" +" c none" +" !!!!!! " +" " +" ! !! ! " +" ! !! ! " +"! !! !" +" " +" !!!!!! " +" ! ! " +" !! !! " +"!!!!!!!!!!" +"! !!!! !" +"! !!!! !" +"!!!!!!!!!!" +" !! !! " +;1234567890 +[end] +[_point] +type=0x02f +subtype=0x10 +;GRMN_TYPE: Business - Services/SERVICES_PERSONAL/Personal services/Non NT +String1=0x01,Coiffeur +String2=0x02,Friseur +String3=0x04,Hairdresser +String4=0x03,Kapper +String7=0x15,Fryzjer +String8=0x10,Cabeleireiro +String9=0x05,Servizi per la persona +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="14 14 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" !! !!!!! " +" !! !!!!!!" +" !! !!" +" !! !!!!!!" +" !! !!" +" !! !!!!!!" +" !! !!" +" !! !!!!!!" +" !! !!" +" !!!!!!!! !!" +"!! !!!! !! !!" +"! !! ! !!" +"!! !!!! !! !!" +" !!! !!! !!" +;12345678901234 +[end] +[_point] +type=0x02f +subtype=0x12 +;GRMN_TYPE: Business - Services/COMMUNICATION_SERVICES/Communication services/Non NT +String1=0x01,Wifi +String2=0x02,Wifi +String3=0x04,Wifi +String4=0x03,Wifi +String7=0x15,Wifi +String8=0x10,Wifi +String9=0x05,Servizi di comunicazione +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="11 11 2 1" Colormode=16 +"! c #737373" +" c none" +" !!!!!!" +" !! " +" ! !!!!!" +" ! !! " +" ! ! !!!" +"! ! !! " +"! ! ! " +"! ! ! !!!" +"! ! ! !!!!" +"! ! ! !!!!" +"! ! ! !!!!" +;12345678901 +[end] +[_point] +type=0x02f +subtype=0x13 +;GRMN_TYPE: Business - Services/REPAIR_SERVICE/Repair service/Non NT +String1=0x01,Vélo +String2=0x02,Rad +String3=0x04,Bicycle +String4=0x03,Fietsen +String7=0x15,Rower +String8=0x10,Bicicleta +String9=0x05,Servizi di riparazione +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BD30B4 +DayXpm="16 11 2 1" Colormode=16 +"! c #BD30B4" +" c none" +" ! " +" !!!! !! " +" !! ! " +" !!!!!! " +" !! !! " +" !!!! !!!!!! " +"!! !!! !! !! " +"! ! ! ! " +"! ! ! ! " +"!! !! !! !! " +" !!!! !!!! " +;1234567890123456 +[end] +[_point] +type=0x02f +subtype=0x14 +;GRMN_TYPE: Business - Services/SOCIAL_SERVICES/Social services/Non NT +String1=0x01,Services sociaux +String2=0x02,Soziale Einrichtung +String3=0x04,Social +String4=0x03,Sociale instelling +String7=0x15,Social +String8=0x10,Social +String9=0x05,Istituzione sociale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="19 16 2 1" Colormode=16 +"! c #734A08" +" c none" +" !! " +" !!!! " +"!! !!!! !! " +"!! !! !! " +"!! !! " +"!! !! !! " +"!! !!!! !! " +"!! !!!! !! " +"!! !!!! !! " +"!! !!!! !! " +" !! !! " +" !! !! " +" !!!! !!!! " +" !!!! !!!! " +" !!!! !!!! " +" !!!! !!!! " +;1234567890123456789 +[end] +[_point] +type=0x02f +subtype=0x15 +;GRMN_TYPE: Business - Services/UTILITY/Utility (gas, electric, water) services/Non NT +String1=0x01,Déchetterie +String2=0x02,Recycling +String3=0x04,Recycling +String4=0x03,Glasbak +String7=0x15,Recykling +String8=0x10,Reciclagem +String9=0x05,Punto di raccolta +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="17 17 2 1" Colormode=16 +"! c #734A08" +" c none" +" !! !! " +" !! !!! " +" !!! !! ! " +" !!! !!!! " +" !!! !!! " +" !! !!!! " +" " +" !!!! !!! " +" !!! !!! " +" !!!! !!!" +"!!! ! !!!" +"!!! !!" +"!!! ! " +" ! !!! !!!!! " +" !!!!! !!!!!! " +" !!!!! !!!! " +" ! " +;12345678901234567 +[end] +[_point] +type=0x02f +subtype=0x16 +;GRMN_TYPE: Business - Services/TRUCK_STOP/Truck stop/Non NT +String1=0x01,Station-essence +String2=0x02,Tankstelle +String3=0x04,Gas station +String4=0x03,Tankstation +String7=0x15,Stacja paliw +String8=0x10,Posto de combustível +String9=0x05,Stazione di servizio +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#3E8BFD +DayXpm="12 14 3 1" Colormode=16 +"! c #0095FF" +"# c #F2EFE9" +" c none" +" !! " +" !!!!!! !!!" +"!!!!!!!! !!" +"! ! !" +"! !! #!" +"! ! !#!" +"!!!!!!!! !#!" +"!!!!!!!!#!#!" +"!!!!!!!!#!#!" +"!!!!!!!!#!#!" +"!!!!!!!!#! !" +"!!!!!!!!# ! " +"!!!!!!!!####" +"!!!!!!!! " +;123456789012 +[end] +[_point] +type=0x02f +subtype=0x17 +;GRMN_TYPE: Business - Services/TRANSIT_SERVICES/Mass transit services/Non NT +String1=0x01,Arrêt de bus +String2=0x02,Haltestelle +String3=0x04,Busstop +String4=0x03,Bushalte +String7=0x15,Przystanek +String8=0x10,Parada de ônibus +String9=0x05,Fermata mezzi pubblici +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="9 11 3 1" Colormode=16 +"! c #0095FF" +"# c #F2EFE9" +" c none" +" !!!!!!!#" +"!! ### !!" +"!!!!!!!!!" +"!#######!" +"!#######!" +"!#######!" +"!!!!!!!!!" +"!##!!! #!" +"!##!!! #!" +"!!!!!!!!!" +"#!!###!!#" +;123456789 +[end] +[_point] +type=0x030 +subtype=0x01 +;GRMN_TYPE: Business - Emergency and Government/POLICE_STATION/Police station/Non NT +String1=0x01,Police +String2=0x02,Polizei +String3=0x04,Police +String4=0x03,Politiekantoor +String7=0x15,Policja +String8=0x10,Polícia +String9=0x05,Forze dell’Ordine +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="15 15 2 1" Colormode=16 +"! c #734A08" +" c none" +" !!!!!! " +" !!!!!! " +" " +" !!!!!! " +" !!!!!! " +" !!!! " +" " +" !!!!!!!! " +"!!!!!!! !! " +"!!!!!! !!! " +"!!!!! !!!! " +"!!!! !!!!! " +"!!! !!!!!! " +"!! !!!!!!! " +"! !!!!!!!! " +;123456789012345 +[end] +[_point] +type=0x030 +subtype=0x02 +;GRMN_TYPE: Business - Emergency and Government/HOSPITAL/Hospital/Non NT +String1=0x01,Hôpital +String2=0x02,Krankenhaus +String3=0x04,Hospital +String4=0x03,Ziekenhuis +String7=0x15,Szpital +String8=0x10,Hospital +String9=0x05,Ospedale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#BF0000 +DayXpm="19 14 2 1" Colormode=16 +"! c #BF0000" +" c none" +" !!!!!! " +" !!!!!!!!!! " +" !!!!! !!!!! " +" !!!!! !!!!! " +"!!!!!! !!!!!! " +"!!!!!! !!!!!! " +"!! !! " +"!! !! " +"!!!!!! !!!!!! " +"!!!!!! !!!!!! " +" !!!!! !!!!! " +" !!!!! !!!!! " +" !!!!!!!!!! " +" !!!!!! " +;1234567890123456789 +[end] +[_point] +type=0x030 +subtype=0x03 +;GRMN_TYPE: Business - Emergency and Government/CITY_HALL/City hall/Non NT +String1=0x01,Mairie +String2=0x02,Rathaus +String3=0x04,Townhall +String4=0x03,Stadhuis +String7=0x15,Ratusz +String8=0x10,Prefeitura +String9=0x05,Municipio/Ambasciata +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="16 14 2 1" Colormode=16 +"! c #734A08" +" c none" +" !!!!!! " +" !!!! " +" !!!!!! " +" !! " +" !! " +" !!!!!! " +" !!!!!!!!!! " +" " +"!!!!!!!!!!!! " +" !!!! !!!! " +" !!! !!! " +" !!! !!! " +" !!! !!! " +"!!!!!!!!!!!! " +;1234567890123456 +[end] +[_point] +type=0x030 +subtype=0x04 +;GRMN_TYPE: Business - Emergency and Government/COURTHOUSE/Courthouse/Non NT +String1=0x01,Tribunal +String2=0x02,Gericht +String3=0x04,Courthouse +String4=0x03,Gerechtsgebouw +String7=0x15,Sąd +String8=0x10,Tribunal +String9=0x05,Tribunale +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="18 15 2 1" Colormode=16 +"! c #734A08" +" c none" +" !! " +" !!!!!!!! " +" !!!!!!!!!! " +" !! !! !! " +" ! !! ! " +" !!! !! !!! " +" !!!!! !! !!!!! " +" ! ! !! ! ! " +"!!!!!!! !! !!!!!!!" +" !!!!! !! !!!!! " +" !! " +" !! " +" !! " +" !!!!!!!!! " +" !!!!!!!!!!! " +;123456789012345678 +[end] +[_point] +type=0x030 +subtype=0x05 +;GRMN_TYPE: Business - Emergency and Government/COMMUNITY_CENTER/Community center/Non NT +String1=0x01,Associations culturelles +String2=0x02,Gemeindezentrum +String3=0x04,Community centre +String4=0x03,Gemeenschapscentrum +String7=0x15,Ośrodek kultury +String8=0x10,Centro comunitário +String9=0x05,Centro comunitario +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="18 15 2 1" Colormode=16 +"! c #734A08" +" c none" +" !! !! " +" !!!! !!!! " +" !!!! !!!! " +" !! !! " +" " +"!!!!!!!!!!!!!!! " +" " +" !! !! " +" !!!! !!!! " +" ! ! ! ! " +" !! !! !! !! " +" !! !! !! !! " +"!! ! ! !! " +"!! !!! !! " +"! ! ! " +;123456789012345678 +[end] +[_point] +type=0x030 +subtype=0x06 +;GRMN_TYPE: Business - Emergency and Government/BORDER_CROSSING/Border crossing/Non NT +String1=0x01,Douane +String2=0x02,Zoll +String3=0x04,Bordercontrol +String4=0x03,Douane +String7=0x15,Kontrola graniczna +String8=0x10,Alfândega +String9=0x05,Dogana +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x030 +subtype=0x07 +;GRMN_TYPE: Business - Emergency and Government/GOV_OFFICE/Government Office/Non NT +String1=0x01,Prison +String2=0x02,Gefängnis +String3=0x04,Prison +String4=0x03,Gevangenis +String7=0x15,Więzienie +String8=0x10,Prisão +String9=0x05,Istituto governativo +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="16 14 2 1" Colormode=16 +"! c #734A08" +" c none" +"!!!!!!!!!!!!!! " +"! ! ! ! " +"! ! ! ! " +"! ! !! ! ! " +"! !!!!!! ! " +"! !!!!!! ! " +"! ! !! ! ! " +"! ! ! ! " +"! !!!!!! ! " +"! !!!!!! ! " +"! !!!!!!!! ! " +"! !!!!!!!! ! " +"! !!!!!!!! ! " +"!!!!!!!!!!!!!! " +;1234567890123456 +[end] +[_point] +type=0x030 +subtype=0x08 +;GRMN_TYPE: Business - Emergency and Government/FIRE_DEPT/Fire department/Non NT +String1=0x01,Pompiers +String2=0x02,Feuerwehr +String3=0x04,Firestation +String4=0x03,Brandweer +String7=0x15,Straż pożarna +String8=0x10,Bombeiros +String9=0x05,Vigili del fuoco +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="13 17 2 1" Colormode=16 +"! c #734A08" +" c none" +" ! " +" !! " +" !!! ! " +" !!!! !! " +" !!!! !!! " +" !!!!! !!!! " +" !!!!!!!!!! " +"!!!! !!!!!!! " +"!!!! !!!!!! " +"!!!! !!!!!!!" +"!!!! !! !!!" +" !!! ! !!!" +" !!!! !! " +" !!! !! " +" !!! !! " +" !!! !! " +" !! ! " +;1234567890123 +[end] +[_point] +type=0x032 +subtype=0x00 +;GRMN_TYPE: // +String1=0x01,Barrière +String2=0x02,Poller +String3=0x04,Bollard +String4=0x03,Paaltje +String7=0x15,Słupek +String8=0x10,Barreira +String9=0x05,Ostacolo +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="5 5 2 1" Colormode=16 +"! c #3F3F3F" +" c none" +" !!! " +"!!!!!" +"!!!!!" +"!!!!!" +" !!! " +;12345 +[end] +[_point] +type=0x043 +subtype=0x00 +;GRMN_TYPE: // +String1=0x01,Marina +String2=0x02,Bootshafen +String3=0x04,Marina +String4=0x03,Jachthaven +String7=0x15,Przystań +String8=0x10,Marina +String9=0x05,Porto +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#697EE2 +DayXpm="1 1 2 1" Colormode=16 +"! c #FFFFFF" +" c none" +" " +;1 +[end] +[_point] +type=0x043 +subtype=0x01 +;GRMN_TYPE: // +String1=0x01,Gare +String2=0x02,Bahnhof +String3=0x04,Trainstation +String4=0x03,Treinstation +String7=0x15,Dworzec kolejowy +String8=0x10,Estação de trem +String9=0x05,Stazione +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#4A58AD +DayXpm="12 7 2 1" Colormode=16 +"! c #7981B0" +" c none" +"!!!!!!! " +"!!!!!!! " +"!!!!!!! " +"!!!!!!! " +"!!!!!!! " +"!!!!!!! " +"!!!!!!! " +;123456789012 +[end] +[_point] +type=0x043 +subtype=0x02 +;GRMN_TYPE: // +String1=0x01,Tramway +String2=0x02,Straßenbahn +String3=0x04,Tram / Metro +String4=0x03,Tram / Metro +String7=0x15,Tramwaj / Metro +String8=0x10,Metrô +String9=0x05,Stazione +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4A58AD +DayXpm="8 4 2 1" Colormode=16 +"! c #7981B0" +" c none" +"!!!! " +"!!!! " +"!!!! " +"!!!! " +;12345678 +[end] +[_point] +type=0x043 +subtype=0x03 +;GRMN_TYPE: // +String1=0x01,Terminal Ferry +String2=0x02,Fährhafen +String3=0x04,Ferry terminal +String4=0x03,Ferry terminal +String7=0x15,Terminal promowy +String8=0x10,Terminal de balsa +String9=0x05,Terminal +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#8461C4 +DayXpm="11 15 2 1" Colormode=16 +"! c #8A68C6" +" c none" +" !! " +" !!!! " +" !!!! " +" !! " +" !! " +" !!!!!!!! " +" !!!!!!!! " +" !! " +" !! " +"! !! ! " +"!! !! !! " +"!!! !! !!! " +" !!!!!!!! " +" !!!!!! " +" !! " +;12345678901 +[end] +[_point] +type=0x04a +subtype=0x00 +;GRMN_TYPE: Misc. Points of Interest/PICNIC_AREA/Picnic area/Non NT, NT +String1=0x01,Pique-nique +String2=0x02,Picknick +String3=0x04,Picnic area +String4=0x03,Picknick plaats +String7=0x15,Miejsce piknikowe +String8=0x10,Piquenique +String9=0x05,Area picnic +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="13 10 2 1" Colormode=16 +"! c #009500" +" c none" +" !!!!!!!!!!! " +" !!!!!!!!!!! " +" !! !! " +" !! !! " +" !! !! " +"!!!!!!!!!!!!!" +" !! !! " +" !!! !!! " +" !!! !!! " +" !! !! " +;1234567890123 +[end] +[_point] +type=0x04c +subtype=0x00 +;GRMN_TYPE: Misc. Points of Interest/INFORMATION/Information/Non NT, NT +String1=0x01,Office du Tourisme +String2=0x02,Touristeninformation +String3=0x04,Tourist information +String4=0x03,Touristen informatie +String7=0x15,Informacja turystyczna +String8=0x10,Informações turísticas +String9=0x05,Informazioni turistiche +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="6 13 2 1" Colormode=16 +"! c #666666" +" c none" +" !! " +" !! " +" " +" " +" !!! " +" !!! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +"!!!!!!" +;123456 +[end] +[_point] +type=0x04e +subtype=0x00 +;GRMN_TYPE: Misc. Points of Interest/RESTROOMS/Restrooms/Non NT, NT +String1=0x01,WC +String2=0x02,WC +String3=0x04,WC +String4=0x03,Toilet +String7=0x15,Toaleta +String8=0x10,Banheiro +String9=0x05,Servizi igienici +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="15 14 2 1" Colormode=16 +"! c #734A08" +" c none" +" ! " +" !! ! !! " +" !!!! ! !!!! " +" !! ! !! " +" ! " +" !!!! ! !!!!!!" +" !!!! ! !!!!!!" +" !! ! !!!! " +" !! ! !!!! " +" !!!! ! !! " +"!!!!!! ! !! " +" !! ! !! " +" !! ! !! " +" !! ! !! " +;123456789012345 +[end] +[_point] +type=0x050 +subtype=0x00 +;GRMN_TYPE: Misc. Points of Interest/DRINKING_WATER/Drinking water/Non NT, NT +String1=0x01,Eau potable +String2=0x02,Trinkwasser +String3=0x04,Drinking water +String4=0x03,Drinkwater +String7=0x15,Woda pitna +String8=0x10,Bebedouro +String9=0x05,Acqua potabile +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="13 16 2 1" Colormode=16 +"! c #734A08" +" c none" +" " +" !!!!!! " +" !! " +" !! " +" !!!!!!!!!" +" !!!!!!!!!!" +" !! " +" " +"!!!!!!!! " +"!! !! " +"!! !! " +" !!!!!! " +" !!!!!! " +" !!!!!! " +" !!!! " +" !!!! " +;1234567890123 +[end] +[_point] +type=0x059 +subtype=0x04 +;GRMN_TYPE: // +String1=0x01,Héliport +String2=0x02,Hubschrauberlandeplatz +String3=0x04,Heli +String4=0x03,Helihaven +String7=0x15,Lądowisko helikopterowe +String8=0x10,Heliponto +String9=0x05,Eliporto +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="18 17 2 1" Colormode=16 +"! c #8461C4" +" c none" +" !!!!!! " +" !! !! " +" ! ! " +" ! ! " +" ! !! !! ! " +" ! !! !! ! " +"! !! !! !" +"! !!!!!!!! !" +"! !!!!!!!! !" +"! !! !! !" +"! !! !! !" +" ! !! !! ! " +" ! !! !! ! " +" ! ! " +" ! ! " +" !! !! " +" !!!!!! " +;123456789012345678 +[end] +[_point] +type=0x064 +subtype=0x03 +;GRMN_TYPE: Geographical Named Points of Interest/CEMETERY/Cemetery/Non NT, NT +String1=0x01,Cimetière +String2=0x02,Friedhof +String3=0x04,Cemetry +String4=0x03,Begraafplaats +String7=0x15,Cmentarz +String8=0x10,Cemitério +String9=0x05,Cimitero +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#495E48 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x064 +subtype=0x0b +;GRMN_TYPE: Geographical Named Points of Interest/MILITARY/Military point of interest/Non NT, NT +String1=0x01,Militaire +String2=0x02,Militär +String3=0x04,Military +String4=0x03,Militair +String7=0x15,Wojsko +String8=0x10,Militar +String9=0x05,Elemento militare +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#8B0606 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x064 +subtype=0x11 +;GRMN_TYPE: Geographical Named Points of Interest/TOWER/Tower/Non NT, NT +String1=0x01,Tour/Pylône +String2=0x02,Turm +String3=0x04,Tower +String4=0x03,Toren +String7=0x15,Wieża +String8=0x10,Torre +String9=0x05,Torre/Pilone +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="10 15 2 1" Colormode=16 +"! c #666666" +" c none" +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !! " +" !!!! " +"!!!!!!!! " +"!!!!!!!! " +;1234567890 +[end] +[_point] +type=0x065 +subtype=0x05 +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/CANAL/Canal/Non NT, NT +String1=0x01,Barrage +String2=0x02,Wehr +String3=0x04,Weir +String4=0x03,Stuw +String7=0x15,Tama +String8=0x10,Açude +String9=0x05,Diga +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#5E5E5D +DayXpm="9 9 2 1" Colormode=16 +"! c #A4A4A4" +" c none" +" !!!!! " +" !!!!!!! " +"!!! !!!" +"!! !!" +"!! !!" +"!! !!" +"!!! !!!" +" !!!!!!! " +" !!!!! " +;123456789 +[end] +[_point] +type=0x065 +subtype=0x08 +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/WATERFALL/Waterfall/Non NT, NT +String1=0x01,Chute d'eau +String2=0x02,Wasserfall +String3=0x04,Waterfall +String4=0x03,Waterval +String7=0x15,Wodospad +String8=0x10,Cachoeira +String9=0x05,Cascata +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4863A0 +DayXpm="15 14 2 1" Colormode=16 +"! c #4D80B3" +" c none" +" !!!!!!!!!! " +" !!!!!!!!!!! " +" !! !! !!! " +" !! !! !! " +" !! !! !! " +" !! !! !! " +" !! !! !! " +" !! !! !! " +" ! ! !! " +" ! " +" " +" !!! !!! !!! " +"!!!!!!!!!!!!!!!" +"! !! !! !" +;123456789012345 +[end] +[_point] +type=0x065 +subtype=0x0a +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/GLACIER/Glacier/Non NT, NT +String1=0x01,Glacier +String2=0x02,Gletscher +String3=0x04,Gletcher +String4=0x03,Gletsjer +String7=0x15,Lodowiec +String8=0x10,Geleira +String9=0x05,Ghiacciaio +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x065 +subtype=0x0c +;GRMN_TYPE: // +String1=0x01,Île +String2=0x02,Insel +String3=0x04,Island +String4=0x03,Eiland +String7=0x15,Wyspa +String8=0x10,Ilha +String9=0x05,Isola +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#414141 +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x065 +subtype=0x0f +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/RESERVOIR/Reservoir/Non NT, NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x065 +subtype=0x11 +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/SPRING/Spring/Non NT, NT +String1=0x01,Source +String2=0x02,Quelle +String3=0x04,Well +String4=0x03,Waterbron +String7=0x15,Źródło +String8=0x10,Poço +String9=0x05,Sorgente +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +DayXpm="9 9 2 1" Colormode=16 +"! c #7BBCEC" +" c none" +" !!!!! " +" !!!!!!! " +"!!! !!!" +"!! !!" +"!! !!" +"!! !!" +"!!! !!!" +" !!!!!!! " +" !!!!! " +;123456789 +[end] +[_point] +type=0x065 +subtype=0x12 +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/STREAM/Stream/Non NT, NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x065 +subtype=0x13 +;GRMN_TYPE: Geographical Named Points of Interest - Water Related/SWAMP/Swamp/Non NT, NT +String1=0x01,Marais +String2=0x02,Sumpf +String3=0x04,Swamp +String4=0x03,Moeras +String7=0x15,Bagno +String8=0x10,Pântano +String9=0x05,Palude +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x01 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/ARCH/Arch/Non NT, NT +String1=0x01,Grotte +String2=0x02,Höhle +String3=0x04,Cave +String4=0x03,Grot +String7=0x15,Jaskinia +String8=0x10,Caverna +String9=0x05,Grotta +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="11 6 2 1" Colormode=16 +"! c #000000" +" c none" +" !!!!! " +" ! ! " +" ! !!! ! " +"! !!!!! !" +"! !!!!!!! !" +"! !!!!!!! !" +;12345678901 +[end] +[_point] +type=0x066 +subtype=0x03 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/BASIN/Basin/Non NT, NT +String1=0x01,Eau +String2=0x02,Wasser +String3=0x04,Water +String4=0x03,Water +String7=0x15,Woda +String8=0x10,Água +String9=0x05,Acqua +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#4D80B3 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x04 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/BEACH/Beach/Non NT, NT +String1=0x01,Plage +String2=0x02,Strand +String3=0x04,Beach +String4=0x03,Strand +String7=0x15,Plaża +String8=0x10,Praia +String9=0x05,Spiaggia +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#907712 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x06 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/CAPE/Cape/Non NT, NT +String1=0x01,Cap +String2=0x02,Kap +String3=0x04,Cape +String4=0x03,Kaap +String7=0x15,Przylądek +String8=0x10,Cabo +String9=0x05,Capo +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x07 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/CLIFF/Cliff/Non NT, NT +String1=0x01,Falaise +String2=0x02,Klippe +String3=0x04,Cliff +String4=0x03,Klif +String7=0x15,Klif +String8=0x10,Penhasco +String9=0x05,Dirupo +ExtendedLabels=Y +FontStyle=NoLabel (invisible) +CustomColor=No +DayXpm="1 1 2 1" Colormode=16 +"! c #00FF4B" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x12 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/RESERVE/Reserve/Non NT, NT +String1=0x01,Réserve naturelle +String2=0x02,Naturschutzgebiet +String3=0x04,Nature reserve +String4=0x03,Natuurgebied +String7=0x15,Rezerwat przyrody +String8=0x10,Reserva natural +String9=0x05,Riserva +ExtendedLabels=Y +FontStyle=Default +CustomColor=Day +DaycustomColor:#006500 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x14 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/ROCK/Rock/Non NT, NT +String1=0x01,Roche +String2=0x02,Felsen +String3=0x04,Rock +String4=0x03,Rotsen +String7=0x15,Skała +String8=0x10,Rochedo +String9=0x05,Roccia +ExtendedLabels=Y +FontStyle=NormalFont +CustomColor=Day +DaycustomColor:#967757 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] +[_point] +type=0x066 +subtype=0x16 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/SUMMIT/Summit, top of a hill or mountain/Non NT, NT +String1=0x01,Sommet +String2=0x02,Gipfel +String3=0x04,Summit +String4=0x03,Bergtop +String7=0x15,Szczyt +String8=0x10,Pico +String9=0x05,Picco +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#734A08 +DayXpm="14 10 2 1" Colormode=16 +"! c #D08F55" +" c none" +" ! " +" ! " +" !!! " +" !!! " +" !!!!! " +" !!!!! " +" !!!!!!! " +" !!!!!!! " +"!!!!!!!!! " +"!!!!!!!!! " +;12345678901234 +[end] +[_point] +type=0x066 +subtype=0x18 +;GRMN_TYPE: Geographical Named Points of Interest - Land Related/WOODS/Wooded area/Non NT, NT +String1=0x01,Forêt +String2=0x02,Wald +String3=0x04,Woods +String4=0x03,Bos +String7=0x15,Drzewa +String8=0x10,Mata / Bosque +String9=0x05,Selva +ExtendedLabels=Y +FontStyle=SmallFont +CustomColor=Day +DaycustomColor:#527247 +DayXpm="1 1 2 1" Colormode=16 +"! c #009500" +" c none" +" " +;1 +[end] diff --git a/resources/typ-files/sameOrder.txt b/resources/typ-files/sameOrder.txt new file mode 100644 index 0000000..2222249 --- /dev/null +++ b/resources/typ-files/sameOrder.txt @@ -0,0 +1,119 @@ +;------------------------------------------------------------------------------- +; This is an example TYP file. +; A TYP file controls how the Garmin device renders polygons, lines and points. +; See https://wiki.openstreetmap.org/wiki/Mkgmap/help/typ_compile +; for more information. +; +; This example sets most polygons to have the same drawOrder +; See https://wiki.openstreetmap.org/wiki/Editing_OSM_Map_On_Garmin/Area_Types +; so that mkgmap option --order-by-decreasing-area works in an optimum manner. +; It exposes all the known non-extended Garmin polygon representations, eg +; 0x01-0x03=City and provides some hidden polygons for naming large areas such +; as Counties, Islands... +;------------------------------------------------------------------------------- +; +[_drawOrder] +; nothing shows, even with: Type=0x00,2 +Type=0x01,2 +Type=0x02,2 +Type=0x03,2 +Type=0x04,2 +Type=0x05,2 +Type=0x06,2 +; 0x07/Airport default drawOrder is lower that most other polygons on some Garmin devices; make it the same. +Type=0x07,2 +Type=0x08,2 +Type=0x09,2 +Type=0x0a,2 +Type=0x0b,2 +Type=0x0c,2 +Type=0x0d,2 +Type=0x0e,2 +Type=0x0f,2 +Type=0x10,2 +Type=0x11,2 +Type=0x12,2 +Type=0x13,2 +; the following Greens default drawOrder is lower than most on some Garmin devices; make them the same. +Type=0x14,2 +Type=0x15,2 +Type=0x16,2 +Type=0x17,2 +Type=0x18,2 +Type=0x19,2 +Type=0x1a,2 +Type=0x1b,2 +Type=0x1c,2 +Type=0x1d,2 +Type=0x1e,2 +Type=0x1f,2 +Type=0x20,2 +; to here +Type=0x21,2 +Type=0x22,2 +Type=0x23,2 +Type=0x24,2 +Type=0x25,2 +Type=0x26,2 +Type=0x27,2 +Type=0x28,2 +Type=0x29,2 +Type=0x2a,2 +Type=0x2b,2 +Type=0x2c,2 +Type=0x2d,2 +Type=0x2e,2 +Type=0x2f,2 +Type=0x30,2 +Type=0x31,2 +Type=0x32,2 +Type=0x33,2 +Type=0x34,2 +Type=0x35,2 +Type=0x36,2 +Type=0x37,2 +Type=0x38,2 +Type=0x39,2 +Type=0x3a,2 +Type=0x3b,2 +Type=0x3c,2 +Type=0x3d,2 +Type=0x3e,2 +Type=0x3f,2 +Type=0x40,2 +Type=0x41,2 +Type=0x42,2 +Type=0x43,2 +Type=0x44,2 +Type=0x45,2 +Type=0x46,2 +Type=0x47,2 +Type=0x48,2 +Type=0x49,2 +; The following two are overview/main background. Give them a lower drawOrder. +Type=0x4a,1 +Type=0x4b,1 +Type=0x4c,2 +Type=0x4d,2 +Type=0x4e,2 +Type=0x4f,2 +Type=0x50,2 +Type=0x51,2 +Type=0x52,2 +Type=0x53,2 +Type=0x54,2 +Type=0x55,2 +; The following don't seem to have any known pre-defined meaning to Garmin +; devices and can be used to give a 'hover' or 'select' name and details without +; other representation, being hidden with a lower drawOrder than the background. +Type=0x56,0 +Type=0x57,0 +Type=0x58,0 +Type=0x59,0 +Type=0x5a,0 +Type=0x5b,0 +Type=0x5c,0 +Type=0x5d,0 +Type=0x5e,0 +Type=0x5f,0 +[end] diff --git a/src/uk/me/parabola/imgfmt/app/Label.java b/src/uk/me/parabola/imgfmt/app/Label.java index 1064970..f90b5fa 100644 --- a/src/uk/me/parabola/imgfmt/app/Label.java +++ b/src/uk/me/parabola/imgfmt/app/Label.java @@ -95,6 +95,11 @@ return s.trim(); } + /** + * two or more whitespace characters are replaced by a single space + * @param s string to trim + * @return the trimmed string or null if s is null or empty + */ public static String squashSpaces(String s) { if(s == null || s.isEmpty()) return null; diff --git a/src/uk/me/parabola/imgfmt/app/net/AccessTagsAndBits.java b/src/uk/me/parabola/imgfmt/app/net/AccessTagsAndBits.java index 8c8ade7..8a7747f 100644 --- a/src/uk/me/parabola/imgfmt/app/net/AccessTagsAndBits.java +++ b/src/uk/me/parabola/imgfmt/app/net/AccessTagsAndBits.java @@ -14,6 +14,7 @@ import java.util.LinkedHashMap; import java.util.Map; + import uk.me.parabola.mkgmap.reader.osm.Element; import uk.me.parabola.mkgmap.reader.osm.TagDict; @@ -23,6 +24,11 @@ * */ public final class AccessTagsAndBits { + + private AccessTagsAndBits() { + // private constructor to hide the implicit public one + } + // constants for vehicle class public static final byte FOOT = 0x01; public static final byte BIKE = 0x02; @@ -60,8 +66,9 @@ for (Map.Entry entry : ACCESS_TAGS.entrySet()) { ACCESS_TAGS_COMPILED.put(TagDict.getInstance().xlate(entry.getKey()), entry.getValue()); } - }; + } + public static final Map ROUTE_TAGS; static { ROUTE_TAGS = new LinkedHashMap<>(); diff --git a/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java b/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java index fe7432f..b1782d6 100644 --- a/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java +++ b/src/uk/me/parabola/imgfmt/app/net/AngleChecker.java @@ -46,10 +46,9 @@ private boolean ignoreSharpAngles; private boolean cycleMap; -// private final Coord test = new Coord(48.074815,16.272771); - - private final int MIN_ANGLE = 0x10; - private final int MIN_LOW_SPEED_ANGLE = 0x20; + + private static final int MIN_ANGLE = 0x10; + private static final int MIN_LOW_SPEED_ANGLE = 0x20; private int mask; private int mrnd; @@ -76,55 +75,49 @@ orAccessMask |= arc.getRoadDef().getAccess(); roadDefs.add(arc.getRoadDef()); } + public float getInitialHeading() { return initialHeading; } + public boolean isOneway() { return isOneWayTrueCount == arcs.size(); } + public boolean isForward() { return isForwardTrueCount == arcs.size(); } + /** * @return */ public void setInitialHeading(float modIH) { while (modIH > 180) - modIH -= 360; + modIH -= 360; while (modIH < -180) - modIH += 360; + modIH += 360; initialHeading = modIH; - imgHeading = calcEncodedBearing(initialHeading); - - for (RouteArc arc : arcs){ + imgHeading = calcEncodedBearing(initialHeading); + + for (RouteArc arc : arcs) { arc.setInitialHeading(modIH); } } - - public String toString(){ + + public String toString() { return arcs.get(0).toString(); } } - private byte calcEncodedBearing (float b) { + private byte calcEncodedBearing(float b) { return (byte) ((RouteArc.directionFromDegrees(b) + mrnd) & mask); } - + public void config(EnhancedProperties props) { // undocumented option - usually used for debugging only ignoreSharpAngles = props.getProperty("ignore-sharp-angles", false); cycleMap = props.getProperty("cycle-map", false); -// float a = 0; -// for (int i = 0; i <= 1440; i++){ -// int ar = (int) Math.round(a * 256.0 / 360); -// int am = ar & 0xf0; -// log.error(a,ar,"0x" + Integer.toHexString(am)); -// a +=0.25; -// if (a >= 180) -// a -= 360; -// } - return; } public void check(Map nodes) { @@ -183,25 +176,17 @@ if (angleAttr.maskedAngle < 0) angleAttr.maskedAngle += 256; - if (ag1.isOneway() && ag1.isForward()){ - // the "incoming" arc is a wrong direction oneway + if (ag1.isOneway() && ag1.isForward() + || ag2.isOneway() && !ag2.isForward()) { + // the "incoming" arc or the "outgoing" is a wrong direction oneway angleAttr.noAccess = true; - } else if (ag2.isOneway() && ag2.isForward() == false){ - // the "outgoing" arc is a wrong direction oneway - angleAttr.noAccess = true; } -// if (node.getCoord().distance(test) < 2){ -// if (angleAttr.angle == 20){ -// angleAttr.maskedMinAngle = 0x30; -// continue; -// } -// } int sumSpeeds = ag1.maxRoadSpeed + ag2.maxRoadSpeed; if (sumSpeeds <= 1) continue; byte pathAccessMask = (byte) (ag1.orAccessMask & ag2.orAccessMask); - if (pathAccessMask == 0){ + if (pathAccessMask == 0) { // no common vehicle allowed on both arcs angleAttr.noAccess = true; } @@ -290,7 +275,7 @@ int modIH = ag2.imgHeading + usedIncrement; if (modIH > 128) modIH -= 256; - ag2.setInitialHeading(modIH * 360/256); + ag2.setInitialHeading(modIH * 360 / 256f); int modAngle = Math.round(ag2.getInitialHeading() - ag1.getInitialHeading()); if (modAngle < 0) modAngle += 360; @@ -309,7 +294,7 @@ int modIH = ag1.imgHeading - usedIncrement; if (modIH < -128) modIH += 256; - ag1.setInitialHeading(modIH * 360/256); + ag1.setInitialHeading(modIH * 360 / 256f); int modAngle = Math.round(ag2.getInitialHeading() - ag1.getInitialHeading()); if (modAngle < 0) modAngle += 360; @@ -329,7 +314,6 @@ log.info(sharpAngle, "don't know how to enlarge it further"); } } - return; } @@ -380,7 +364,7 @@ ag.addArc(arc2); } else{ arc1 = arc2; - if (iter.hasNext() == false) + if (!iter.hasNext()) addArc1 = true; break; } diff --git a/src/uk/me/parabola/imgfmt/app/net/NETFileReader.java b/src/uk/me/parabola/imgfmt/app/net/NETFileReader.java index 20f58fb..260ca77 100644 --- a/src/uk/me/parabola/imgfmt/app/net/NETFileReader.java +++ b/src/uk/me/parabola/imgfmt/app/net/NETFileReader.java @@ -37,7 +37,7 @@ private final NETHeader netHeader = new NETHeader(); // To begin with we only need LBL offsets. - private final Map offsetLabelMap = new HashMap(); + private final Map offsetLabelMap = new HashMap<>(); private List offsets; private List cities; @@ -79,9 +79,9 @@ */ public List getRoads() { ImgFileReader reader = getReader(); - int start = netHeader.getRoadDefinitionsStart(); - - List roads = new ArrayList(); + long start = netHeader.getRoadDefinitionsStart(); + + List roads = new ArrayList<>(); int record = 0; for (int off : offsets) { reader.position(start + off); @@ -182,7 +182,8 @@ initFlag >>= 5; } node += skip + 1; - int right = 0, left = 0; + int right = 0; + int left = 0; if (initFlag == 0) { right = left = getCityOrZip(reader, size, endPos); } else if ((initFlag & 0x4) != 0) { @@ -208,8 +209,7 @@ assert false : "ERRROR overflow"; return 0; } - int cnum = reader.getNu(size); - return cnum; + return reader.getNu(size); } /** @@ -252,11 +252,10 @@ private void readLabelOffsets() { ImgFileReader reader = getReader(); offsets = readOffsets(); - int start = netHeader.getRoadDefinitionsStart(); + long start = netHeader.getRoadDefinitionsStart(); for (int off : offsets) { reader.position(start + off); int labelOffset = reader.get3u(); - // TODO what if top bit is not set?, there can be more than one name and we will miss them offsetLabelMap.put(off, labelOffset & 0x7fffff); } } @@ -283,10 +282,9 @@ } // Sort in address order in the hope of speeding up reading. - List offsets = new ArrayList<>(allOffsets); - allOffsets = null; - offsets.sort(null); - return offsets; + List sortedOffsets = new ArrayList<>(allOffsets); + sortedOffsets.sort(null); + return sortedOffsets; } public void setCities(List cities) { diff --git a/src/uk/me/parabola/imgfmt/app/net/NETHeader.java b/src/uk/me/parabola/imgfmt/app/net/NETHeader.java index ae18e43..6dfc6a5 100644 --- a/src/uk/me/parabola/imgfmt/app/net/NETHeader.java +++ b/src/uk/me/parabola/imgfmt/app/net/NETHeader.java @@ -16,7 +16,6 @@ */ package uk.me.parabola.imgfmt.app.net; -import uk.me.parabola.imgfmt.ReadFailedException; import uk.me.parabola.imgfmt.app.CommonHeader; import uk.me.parabola.imgfmt.app.ImgFileReader; import uk.me.parabola.imgfmt.app.ImgFileWriter; @@ -51,7 +50,7 @@ * * @param reader The header is read from here. */ - protected void readFileHeader(ImgFileReader reader) throws ReadFailedException { + protected void readFileHeader(ImgFileReader reader) { roadDefinitions.readSectionInfo(reader, false); roadShift = reader.get1u(); diff --git a/src/uk/me/parabola/imgfmt/app/net/NOD1Part.java b/src/uk/me/parabola/imgfmt/app/net/NOD1Part.java index a40a34f..7345c64 100644 --- a/src/uk/me/parabola/imgfmt/app/net/NOD1Part.java +++ b/src/uk/me/parabola/imgfmt/app/net/NOD1Part.java @@ -67,17 +67,14 @@ // maximal width and height of the bounding box, since // NOD 1 coordinate offsets are at most 16 bit wide. private static final int MAX_SIZE_UNSAFE = 1 << 16; -// private static final int MAX_SIZE = MAX_SIZE_UNSAFE / 2; private static final int MAX_SIZE = MAX_SIZE_UNSAFE - 0x800; // Table A has at most 0x100 entries private static final int MAX_TABA_UNSAFE = 0x100; -// private static final int MAX_TABA = MAX_TABA_UNSAFE / 2; private static final int MAX_TABA = MAX_TABA_UNSAFE - 0x8; // Table B has at most 0x100 entries private static final int MAX_TABB_UNSAFE = 0x100; -// private static final int MAX_TABB = MAX_TABB_UNSAFE / 2; private static final int MAX_TABB = MAX_TABB_UNSAFE - 0x2; // Nodes size is max 0x2000 to cope with signed 14 bit node offsets @@ -183,9 +180,9 @@ // The area that actually has nodes. private final BBox bboxActual = new BBox(); - private List nodes = new ArrayList(); + private List nodes = new ArrayList<>(); private TableA tabA = new TableA(); - private Map destNodes = new LinkedHashMap(); + private Map destNodes = new LinkedHashMap<>(); /** * Create an unbounded NOD1Part. @@ -218,18 +215,16 @@ * external arc at a deeper level of recursion. */ public void addNode(RouteNode node) { - assert bbox == null || bbox.contains(node.getCoord()) - : "trying to add out-of-bounds node: " + node; + assert bbox == null || bbox.contains(node.getCoord()) : "trying to add out-of-bounds node: " + node; bboxActual.extend(node.getCoord()); nodes.add(node); for (RouteArc arc : node.arcsIteration()) { tabA.addArc(arc); RouteNode dest = arc.getDest(); - if (arc.isInternal() == false){ + if (!arc.isInternal()) { destNodes.put(dest, dest); - } - else if (bbox != null && !bbox.contains(dest.getCoord()) || dest.getGroup() != node.getGroup()) { + } else if (bbox != null && !bbox.contains(dest.getCoord()) || dest.getGroup() != node.getGroup()) { arc.setInternal(false); destNodes.put(dest, dest); } @@ -243,7 +238,7 @@ if (arc.getSource() != node){ tabA.addArc(arc); RouteNode dest = arc.getDest(); - if (arc.isInternal() == false) + if (!arc.isInternal()) destNodes.put(dest, dest); else if (bbox != null && !bbox.contains(dest.getCoord()) || dest.getGroup() != node.getGroup()) { arc.setInternal(false); @@ -268,7 +263,7 @@ * Subdivide this part recursively until it satisfies the constraints. */ protected List subdivideHelper(int depth) { - List centers = new LinkedList(); + List centers = new LinkedList<>(); if (satisfiesConstraints()) { centers.add(this.toRouteCenter()); diff --git a/src/uk/me/parabola/imgfmt/app/net/NODHeader.java b/src/uk/me/parabola/imgfmt/app/net/NODHeader.java index dc3a120..3be4a8a 100644 --- a/src/uk/me/parabola/imgfmt/app/net/NODHeader.java +++ b/src/uk/me/parabola/imgfmt/app/net/NODHeader.java @@ -18,7 +18,6 @@ import java.util.Arrays; -import uk.me.parabola.imgfmt.ReadFailedException; import uk.me.parabola.imgfmt.app.CommonHeader; import uk.me.parabola.imgfmt.app.ImgFileReader; import uk.me.parabola.imgfmt.app.ImgFileWriter; @@ -61,7 +60,7 @@ * * @param reader The header is read from here. */ - protected void readFileHeader(ImgFileReader reader) throws ReadFailedException { + protected void readFileHeader(ImgFileReader reader) { nodes.readSectionInfo(reader, false); flags = reader.get2u(); reader.get2u(); @@ -92,8 +91,8 @@ // multiplier shift for road + arc length values, the smaller the shift the higher the precision and NOD size // as it has an influence on the number of bits needed to encode a length - final static int DISTANCE_MULT_SHIFT = 1; // 0..7 1 seems to be a good compromise - final static int DISTANCE_MULT = 1 << DISTANCE_MULT_SHIFT; + static final int DISTANCE_MULT_SHIFT = 1; // 0..7 1 seems to be a good compromise + static final int DISTANCE_MULT = 1 << DISTANCE_MULT_SHIFT; protected void writeFileHeader(ImgFileWriter writer) { nodes.setPosition(HEADER_LEN); nodes.writeSectionInfo(writer); diff --git a/src/uk/me/parabola/imgfmt/app/net/NumberPreparer.java b/src/uk/me/parabola/imgfmt/app/net/NumberPreparer.java index 7beed56..597f579 100644 --- a/src/uk/me/parabola/imgfmt/app/net/NumberPreparer.java +++ b/src/uk/me/parabola/imgfmt/app/net/NumberPreparer.java @@ -238,7 +238,7 @@ /** * The current state of the writing process. */ - static abstract class State { + abstract static class State { protected final Side left = new Side(true); protected final Side right = new Side(false); diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java index 7c61c61..1478de5 100644 --- a/src/uk/me/parabola/imgfmt/app/net/RouteNode.java +++ b/src/uk/me/parabola/imgfmt/app/net/RouteNode.java @@ -326,7 +326,7 @@ } public Iterable arcsIteration() { - return () -> arcs.iterator(); + return arcs::iterator; } public List getRestrictions() { @@ -362,7 +362,7 @@ } } } - if (roundaboutArcs.size() > 0) { + if (!roundaboutArcs.isEmpty()) { // get the coordinates of a box bounding the junctions of the roundabout int minRoundaboutLat = coord.getHighPrecLat(); int maxRoundaboutLat = minRoundaboutLat; @@ -484,10 +484,9 @@ log.warn("Roundabout " + rd + " overlaps " + fb.getRoadDef() + " at " + coord.toOSMURL()); } } - else if(fb.isForward()) { - if(!rd.messagePreviouslyIssued("roundabout forks/overlaps")) { - log.warn("Roundabout " + rd + " forks at " + coord.toOSMURL()); - } + else if (fb.isForward() + && !rd.messagePreviouslyIssued("roundabout forks/overlaps")) { + log.warn("Roundabout " + rd + " forks at " + coord.toOSMURL()); } } } @@ -557,14 +556,15 @@ boolean connectsToNonRoundaboutSegment = false; RouteArc nextRoundaboutArc = null; for (RouteArc nba : nb.arcs) { - if (nba.isDirect() == false) + if (!nba.isDirect()) continue; if (!nba.getRoadDef().isSynthesised()) { if (nba.getRoadDef().isRoundabout()) { if (nba.isForward()) nextRoundaboutArc = nba; - } else + } else { connectsToNonRoundaboutSegment = true; + } } } @@ -723,7 +723,6 @@ if (nodes.size() < 3) return; -// System.out.println(road + " " + nodes.size() + " " + forwardArcs.size()); ArrayList newArcs = new ArrayList<>(); IntArrayList arcPositions = forwardArcPositions; List roadArcs = forwardArcs; @@ -772,15 +771,14 @@ newArc.setIndirect(); newArcs.add(newArc); arcToStepNode = newArc; - stepNode = destNode; partialArcLength = 0; if (cl >= finalClass) break; } } - if (newArcs.isEmpty() == false){ - int directArcPos = arcPositions.getInt(i); + if (!newArcs.isEmpty()) { + int directArcPos = arcPositions.getInt(i); assert nodes.get(i).arcs.get(directArcPos).isDirect(); assert nodes.get(i).arcs.get(directArcPos).getRoadDef() == newArcs.get(0).getRoadDef(); assert nodes.get(i).arcs.get(directArcPos).isForward() == newArcs.get(0).isForward(); @@ -790,7 +788,6 @@ int reverseArcPos = reverseArcPositions.get(i-1); // i-1 because first node doesn't have reverse arc if (directArcPos < reverseArcPos) reverseArcPositions.set(i - 1, reverseArcPos + newArcs.size()); - } } } diff --git a/src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java b/src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java index 6dbf694..3c718a7 100644 --- a/src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java +++ b/src/uk/me/parabola/imgfmt/app/net/RouteRestriction.java @@ -38,8 +38,6 @@ * @author Robert Vollmert */ public class RouteRestriction { - //private static final Logger log = Logger.getLogger(RouteRestriction.class); - // first three bytes of the header -- might specify the type of restriction // and when it is active private static final byte RESTRICTION_TYPE = 0x05; // 0x07 spotted, meaning? @@ -63,16 +61,16 @@ private final byte exceptMask; private final byte flags; // meaning of bits 0x01 and 0x10 are not clear - private final static byte F_EXCEPT_FOOT = 0x02; - private final static byte F_EXCEPT_EMERGENCY = 0x04; - private final static byte F_MORE_EXCEPTIONS = 0x08; - - private final static byte EXCEPT_CAR = 0x01; - private final static byte EXCEPT_BUS = 0x02; - private final static byte EXCEPT_TAXI = 0x04; - private final static byte EXCEPT_DELIVERY = 0x10; - private final static byte EXCEPT_BICYCLE = 0x20; - private final static byte EXCEPT_TRUCK = 0x40; + private static final byte F_EXCEPT_FOOT = 0x02; + private static final byte F_EXCEPT_EMERGENCY = 0x04; + private static final byte F_MORE_EXCEPTIONS = 0x08; + + private static final byte EXCEPT_CAR = 0x01; + private static final byte EXCEPT_BUS = 0x02; + private static final byte EXCEPT_TAXI = 0x04; + private static final byte EXCEPT_DELIVERY = 0x10; + private static final byte EXCEPT_BICYCLE = 0x20; + private static final byte EXCEPT_TRUCK = 0x40; /** * @@ -87,21 +85,21 @@ RouteArc arc = arcs.get(i); assert arc.getDest() != viaNode; } - byte flags = 0; + byte tmpFlags = 0; if ((mkgmapExceptMask & FOOT) != 0) - flags |= F_EXCEPT_FOOT; + tmpFlags |= F_EXCEPT_FOOT; if ((mkgmapExceptMask & EMERGENCY) != 0) - flags |= F_EXCEPT_EMERGENCY; + tmpFlags |= F_EXCEPT_EMERGENCY; exceptMask = translateExceptMask(mkgmapExceptMask); if(exceptMask != 0) - flags |= F_MORE_EXCEPTIONS; + tmpFlags |= F_MORE_EXCEPTIONS; int numArcs = arcs.size(); assert numArcs < 8; - flags |= ((numArcs) << 5); - this.flags = flags; + tmpFlags |= ((numArcs) << 5); + this.flags = tmpFlags; } @@ -170,12 +168,10 @@ offsets[pos++] = calcOffset(arc.getDest(), tableOffset); else offsets[pos++] = arc.getIndexB(); - if (arc.getSource() == viaNode){ + if (!viaWritten && arc.getSource() == viaNode) { // there will be two nodes with source node = viaNode, but we write the source only once - if (!viaWritten){ - offsets[pos++] = calcOffset(viaNode, tableOffset); - viaWritten = true; - } + offsets[pos++] = calcOffset(viaNode, tableOffset); + viaWritten = true; } } diff --git a/src/uk/me/parabola/imgfmt/app/net/TableA.java b/src/uk/me/parabola/imgfmt/app/net/TableA.java index 55f0380..ddf0c5f 100644 --- a/src/uk/me/parabola/imgfmt/app/net/TableA.java +++ b/src/uk/me/parabola/imgfmt/app/net/TableA.java @@ -39,13 +39,13 @@ private int offset; // arcs for roundabouts - private final HashMap roundaboutArcs = new LinkedHashMap(); + private final HashMap roundaboutArcs = new LinkedHashMap<>(); // arcs for unpaved ways - private final HashMap unpavedArcs = new LinkedHashMap(); + private final HashMap unpavedArcs = new LinkedHashMap<>(); // arcs for ferry ways - private final HashMap ferryArcs = new LinkedHashMap(); + private final HashMap ferryArcs = new LinkedHashMap<>(); // arcs for paved ways - private final HashMap pavedArcs = new LinkedHashMap(); + private final HashMap pavedArcs = new LinkedHashMap<>(); private static int count; diff --git a/src/uk/me/parabola/imgfmt/app/net/TableB.java b/src/uk/me/parabola/imgfmt/app/net/TableB.java index 1e173d9..dc7acf7 100644 --- a/src/uk/me/parabola/imgfmt/app/net/TableB.java +++ b/src/uk/me/parabola/imgfmt/app/net/TableB.java @@ -25,9 +25,9 @@ */ public class TableB { - private final ArrayList nodes = new ArrayList(); + private final ArrayList nodes = new ArrayList<>(); - private final static int ITEM_SIZE = 3; + private static final int ITEM_SIZE = 3; private int offset; @@ -64,7 +64,6 @@ public void addNode(RouteNode node) { int i = nodes.indexOf(node); if (i < 0) { - //i = nodes.size(); nodes.add(node); } } diff --git a/src/uk/me/parabola/imgfmt/app/net/TableC.java b/src/uk/me/parabola/imgfmt/app/net/TableC.java index 3a3b4e4..e6cf243 100644 --- a/src/uk/me/parabola/imgfmt/app/net/TableC.java +++ b/src/uk/me/parabola/imgfmt/app/net/TableC.java @@ -31,7 +31,7 @@ private final TableA tabA; - private final List restrictions = new ArrayList(); + private final List restrictions = new ArrayList<>(); public TableC(TableA tabA) { this.tabA = tabA; diff --git a/src/uk/me/parabola/mkgmap/build/MapArea.java b/src/uk/me/parabola/mkgmap/build/MapArea.java index 6c14793..31c1bdb 100644 --- a/src/uk/me/parabola/mkgmap/build/MapArea.java +++ b/src/uk/me/parabola/mkgmap/build/MapArea.java @@ -130,12 +130,7 @@ * @param resolution The current resolution of the layer. */ private void addPolygons(MapDataSource src, final int resolution) { - MapFilterChain chain = new MapFilterChain() { - public void doFilter(MapElement element) { - MapShape shape = (MapShape) element; - addShape(shape); - } - }; + MapFilterChain chain = element -> addShape((MapShape) element); PolygonSubdivSizeSplitterFilter filter = new PolygonSubdivSizeSplitterFilter(); FilterConfig config = new FilterConfig(); @@ -166,12 +161,7 @@ private void addLines(MapDataSource src, final int resolution) { // Split lines for size, such that it is appropriate for the // resolution that it is at. - MapFilterChain chain = new MapFilterChain() { - public void doFilter(MapElement element) { - MapLine line = (MapLine) element; - addLine(line); - } - }; + MapFilterChain chain = element -> addLine((MapLine) element); LineSizeSplitterFilter filter = new LineSizeSplitterFilter(); FilterConfig config = new FilterConfig(); @@ -309,7 +299,7 @@ } else { for (MapLine l : this.lines) { // Drop any zero sized lines. - if (l instanceof MapRoad == false && l.getRect().height <= 0 && l.getRect().width <= 0) + if (!(l instanceof MapRoad) && l.getRect().height <= 0 && l.getRect().width <= 0) continue; Area lineBounds = l.getBounds(); int areaIndex = pickArea(mapAreas, l, xbaseHp, ybaseHp, nx, ny, dxHp, dyHp); @@ -891,9 +881,7 @@ * @return true if this area contains any data */ public boolean hasData() { - if (points.isEmpty() && lines.isEmpty() && shapes.isEmpty()) - return false; - return true; + return !points.isEmpty() || !lines.isEmpty() || !shapes.isEmpty(); } @Override diff --git a/src/uk/me/parabola/mkgmap/main/TypCompiler.java b/src/uk/me/parabola/mkgmap/main/TypCompiler.java index cf7371e..8fd0835 100644 --- a/src/uk/me/parabola/mkgmap/main/TypCompiler.java +++ b/src/uk/me/parabola/mkgmap/main/TypCompiler.java @@ -85,7 +85,7 @@ param.setFamilyId(family); if (product != -1) param.setProductId(product); - if (cp != -1) + if (cp != -1 && param.getCodePage() == 0) param.setCodePage(cp); File outFile = new File(filename); diff --git a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java index ade334d..d7020e1 100644 --- a/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java +++ b/src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java @@ -158,6 +158,7 @@ private final Tags styleOptionTags; private static final String STYLE_OPTION_PREF = "mkgmap:option:"; private final PrefixSuffixFilter prefixSuffixFilter; + private final boolean keepBlanks; private LineAdder lineAdder; @@ -225,6 +226,7 @@ // control calculation of extra nodes in NOD3 / NOD4 admLevelNod3 = props.getProperty("add-boundary-nodes-at-admin-boundaries", 2); addBoundaryNodesAtAdminBoundaries = routable && admLevelNod3 > 0; + keepBlanks = props.containsKey("keep-blanks"); } /** @@ -322,9 +324,9 @@ roads.add(cw); numRoads++; if (!cw.isFerry()) { - String country = way.getTag(countryTagKey); - if (country != null) { - boolean drivingSideIsLeft = LocatorConfig.get().getDriveOnLeftFlag(country); + String countryIso = LocatorConfig.get().getCountryISOCode(way.getTag(countryTagKey)); + if (countryIso != null) { + boolean drivingSideIsLeft = LocatorConfig.get().getDriveOnLeftFlag(countryIso); if (drivingSideIsLeft) numDriveOnLeftRoads++; else @@ -1263,15 +1265,15 @@ private static final short phoneTagKey = TagDict.getInstance().xlate("mkgmap:phone"); private static final short is_inTagKey = TagDict.getInstance().xlate("mkgmap:is_in"); - private static void elementSetup(MapElement ms, GType gt, Element element) { + private void elementSetup(MapElement ms, GType gt, Element element) { String[] labels = new String[4]; - int noLabels = 0; + int numLabels = 0; for (int labelNo = 0; labelNo < 4; labelNo++) { String label1 = element.getTag(labelTagKeys[labelNo]); - String label = Label.squashSpaces(label1); + String label = keepBlanks ? label1 : Label.squashSpaces(label1); if (label != null) { - labels[noLabels] = label; - noLabels++; + labels[numLabels] = label; + numLabels++; } } diff --git a/src/uk/me/parabola/mkgmap/osmstyle/function/LengthFunction.java b/src/uk/me/parabola/mkgmap/osmstyle/function/LengthFunction.java index d28098e..9fef048 100644 --- a/src/uk/me/parabola/mkgmap/osmstyle/function/LengthFunction.java +++ b/src/uk/me/parabola/mkgmap/osmstyle/function/LengthFunction.java @@ -18,7 +18,6 @@ import java.util.Locale; import java.util.Map.Entry; -import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.log.Logger; import uk.me.parabola.mkgmap.reader.osm.Element; import uk.me.parabola.mkgmap.reader.osm.Relation; @@ -45,18 +44,9 @@ return nf.format(length); } - private double calcLength(Element el) { + private static double calcLength(Element el) { if (el instanceof Way) { - Way w = (Way)el; - double length = 0; - Coord prevC = null; - for (Coord c : w.getPoints()) { - if (prevC != null) { - length += prevC.distance(c); - } - prevC = c; - } - return length; + return ((Way) el).calcLengthInMetres(); } else if (el instanceof Relation) { Relation rel = (Relation)el; double length = 0; @@ -76,14 +66,17 @@ } } + @Override public String getName() { return "length"; } + @Override public boolean supportsWay() { return true; } + @Override public boolean supportsRelation() { return true; } diff --git a/src/uk/me/parabola/mkgmap/reader/osm/CoastlineFileLoader.java b/src/uk/me/parabola/mkgmap/reader/osm/CoastlineFileLoader.java index a28955e..635fc02 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/CoastlineFileLoader.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/CoastlineFileLoader.java @@ -94,9 +94,8 @@ Collection loadedCoastlines = loadFile(coastlineFile); log.info(loadedCoastlines.size(), "coastline ways from", coastlineFile, "loaded."); - ArrayList ways = SeaGenerator.joinWays(loadedCoastlines); + List ways = SeaGenerator.joinWays(loadedCoastlines); ListIterator wayIter = ways.listIterator(); - ways = null; while (wayIter.hasNext()) { Way way = wayIter.next(); wayIter.remove(); diff --git a/src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java b/src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java index d522553..bafb788 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/LinkDestinationHook.java @@ -13,33 +13,29 @@ package uk.me.parabola.mkgmap.reader.osm; -import java.util.AbstractMap; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.IdentityHashMap; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.log.Logger; import uk.me.parabola.mkgmap.osmstyle.NameFinder; -import uk.me.parabola.mkgmap.osmstyle.function.LengthFunction; +import uk.me.parabola.mkgmap.osmstyle.housenumber.ExtNumbers; import uk.me.parabola.util.EnhancedProperties; import uk.me.parabola.util.MultiHashMap; /** - * Copies the destination tag from motorway_link and trunk_link ways to the - * first adjacent non link way so that the Garmin is able to display a valid - * destination. - * @author WanMil + * Cuts link ways into parts and adds destination/exit hints to a new part so + * that the Garmin is able to display a valid destination. + * @author Gerd Petermann + * @author WanMil (initial versions) */ public class LinkDestinationHook implements OsmReadingHooks { private static final Logger log = Logger.getLogger(LinkDestinationHook.class); @@ -49,14 +45,13 @@ /** Maps which ways can be driven from a given Coord */ private IdentityHashMap> adjacentWays = new IdentityHashMap<>(); /** Contains all _link ways that have to be processed */ - private Map destinationLinkWays = new LinkedHashMap<>(); + private LinkedHashSet destinationLinkWays = new LinkedHashSet<>(); private static final Set highwayTypes = new LinkedHashSet<>(Arrays.asList( "motorway", "trunk", "primary", "secondary", "tertiary", "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link")); - private HashSet linkTypes = new HashSet<>(Arrays.asList( + private static final Set linkTypes = new LinkedHashSet<>(Arrays.asList( "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link")); - /** Map way ids to its restriction relations so that the relations can easily be updated when the way is split. */ private MultiHashMap restrictions = new MultiHashMap<>(); @@ -68,6 +63,11 @@ private boolean processDestinations; private boolean processExits; + + private static final short TAG_KEY_HIGHWAY = TagDict.getInstance().xlate("highway"); + private static final short TAG_KEY_ONEWAY = TagDict.getInstance().xlate("oneway"); + private static final short TAG_KEY_EXIT_TO = TagDict.getInstance().xlate("exit_to"); + private static final short TAG_KEY_DEST_HINT_WORK = TagDict.getInstance().xlate("mkgmap:dest_hint_work"); @Override public boolean init(ElementSaver saver, EnhancedProperties props) { @@ -81,95 +81,16 @@ /** * Fills the internal lists */ - private void retrieveWays() { + private void retrieveWaysAndRelations() { // collect all ways tagged with highway for (Way w : saver.getWays().values()) { if (w.getPoints().size() < 2) { // ignore one-node or zero-node ways continue; } - String highwayTag = w.getTag("highway"); + String highwayTag = w.getTag(TAG_KEY_HIGHWAY); if (highwayTag != null && highwayTypes.contains(highwayTag)) { - // the points of the way are kept so that it is easy to get - // the adjacent ways for a given _link way - String directedDestination = null; - String directedDestinationLanes = null; - String directionSuffix = null; - List points; - - if (isOnewayInDirection(w)) { - // oneway => don't need the last point because the - // way cannot be driven standing at the last point - points = w.getPoints().subList(0, w.getPoints().size() - 1); - directedDestination = w.getTag("destination:forward"); - directedDestinationLanes = w.getTag("destination:lanes:forward"); - directionSuffix = "forward"; - } else if (isOnewayOppositeDirection(w)) { - // reverse oneway => don't need the first point because the - // way cannot be driven standing at the first point - points = w.getPoints().subList(1, w.getPoints().size()); - directedDestination = w.getTag("destination:backward"); - directedDestinationLanes = w.getTag("destination:lanes:backward"); - directionSuffix = "backward"; - } else { - points = w.getPoints(); - } - for (Coord c : points) { - Set ways = adjacentWays.get(c); - if (ways == null) { - ways = new HashSet<>(4); - adjacentWays.put(c, ways); - } - ways.add(w); - } - registerPointsOfWay(w); - - // if the way is a link way and has a destination tag - // put it the list of ways that have to be processed - if (linkTypes.contains(highwayTag)) { - String destSourceTagKey = "destination"; - String destinationTag = w.getTag("destination"); - if (destinationTag == null) { - // destination is not set - // => check if destination:lanes is without any lane specific information (no |) - destSourceTagKey = "destination:lanes"; - String destLanesTag = w.getTag(destSourceTagKey); - if (destLanesTag == null && directedDestinationLanes != null){ - destLanesTag = directedDestinationLanes; - destSourceTagKey += ":" + directionSuffix; - } - if (destLanesTag != null && !destLanesTag.contains("|")) { - // the destination:lanes tag contains no | => no lane specific information - // use this tag as destination tag - destinationTag = destLanesTag; - } - if (destinationTag == null && directedDestination != null) { - // use the destination:forward or :backward value - destinationTag = directedDestination; - destSourceTagKey = "destination:" + directionSuffix; - } - if (destinationTag == null){ - // try to use the destination:street value - destSourceTagKey = "destination:street"; - destinationTag = w.getTag(destSourceTagKey); - } - - } - if (destinationTag != null) { - w.addTag("mkgmap:dest_hint_work", destinationTag); - if (!"destination".equals(destSourceTagKey) && log.isDebugEnabled()) { - if (destSourceTagKey.startsWith("destination:lanes")) { - log.debug("Use", destSourceTagKey, - "as destination tag because there is one lane information only. Way ", - w.getId(), w.toTagString()); - } else { - log.debug("Use", destSourceTagKey, "as destination tag. Way ", w.getId(), - w.toTagString()); - } - } - destinationLinkWays.put(w.getId(), w); - } - } + processHighWay(w, highwayTag); } } @@ -184,19 +105,89 @@ } } - + private void processHighWay(Way w, String highwayTag) { + // the points of the way are kept so that it is easy to get + // the adjacent ways for a given _link way + List points; + String directionSuffix = null; + + if (isOnewayInDirection(w)) { + // oneway => don't need the last point because the + // way cannot be driven standing at the last point + points = w.getPoints().subList(0, w.getPoints().size() - 1); + directionSuffix = "forward"; + } else if (isOnewayOppositeDirection(w)) { + // reverse oneway => don't need the first point because the + // way cannot be driven standing at the first point + points = w.getPoints().subList(1, w.getPoints().size()); + directionSuffix = "backward"; + } else { + points = w.getPoints(); + } + for (Coord c : points) { + adjacentWays.computeIfAbsent(c, k -> new HashSet<>(4)).add(w); + } + registerPointsOfWay(w); + checkIfUsableLink(w, highwayTag, directionSuffix); + } + + private void checkIfUsableLink(Way w, String highwayTag, String directionSuffix) { + // if the way is a link way and has a destination tag + // put it the list of ways that have to be processed + if (!linkTypes.contains(highwayTag)) + return; + + final String destinationLanes = "destination:lanes"; + final String standardTagKey = "destination"; + String destSourceTagKey = standardTagKey; // for log messages + String destHint = w.getTag(standardTagKey); + if (destHint == null) { + // destination is not set + // => check if destination:lanes is without any lane specific information (no |) + destSourceTagKey = destinationLanes; + String destLanesTag = w.getTag(destSourceTagKey); + if (destLanesTag == null && directionSuffix != null) { + destSourceTagKey += ":" + directionSuffix; + destLanesTag = w.getTag(destSourceTagKey); + } + if (destLanesTag != null && !destLanesTag.contains("|")) { + // the destination:lanes tag contains no | => no lane specific information + // use this tag as destination tag + destHint = destLanesTag; + } + + if (destHint == null && directionSuffix != null) { + // use the destination:forward or :backward value + destSourceTagKey = "destination:" + directionSuffix; + destHint = w.getTag(destSourceTagKey); + + } + if (destHint == null) { + // try to use the destination:street value + destSourceTagKey = "destination:street"; + destHint = w.getTag(destSourceTagKey); + } + } + + if (destHint != null) { + w.addTag(TAG_KEY_DEST_HINT_WORK, destHint); + destinationLinkWays.add(w); + + if (log.isDebugEnabled() && !standardTagKey.equals(destSourceTagKey)) { + String msg = destSourceTagKey.startsWith(destinationLanes) + ? "as destination tag because there is one lane information only." : "as destination tag."; + log.debug("Use", destSourceTagKey, msg, "Way ", w.getId(), w.toTagString()); + } + } + } + /** * Registers the points of the given way for the internal data structures. * @param w a new way */ private void registerPointsOfWay(Way w) { for (Coord c : w.getPoints()) { - Set ways = wayNodes.get(c); - if (ways == null) { - ways = new HashSet<>(4); - wayNodes.put(c, ways); - } - ways.add(w); + wayNodes.computeIfAbsent(c, k-> new HashSet<>()).add(w); } } @@ -235,7 +226,7 @@ for (RestrictionRelation rr : new ArrayList<>(wayRestrictions)) { Coord lastPointNewWay = newWay.getFirstPoint(); List viaCoords = rr.getViaCoords(); - for (Coord via : viaCoords){ + for (Coord via : viaCoords) { if (via == lastPointNewWay) { if (rr.isToWay(oldWay.getId())) { log.debug("Change to-way",oldWay.getId(),"to",newWay.getId(),"for relation",rr.getId(),"at",lastPointNewWay.toOSMURL()); @@ -243,7 +234,7 @@ restrictions.removeMapping(oldWay.getId(), rr); restrictions.add(newWay.getId(), rr); - } else if (rr.isFromWay(oldWay.getId())){ + } else if (rr.isFromWay(oldWay.getId())) { log.debug("Change from-way",oldWay.getId(),"to",newWay.getId(),"for relation",rr.getId(),"at",lastPointNewWay.toOSMURL()); rr.replaceWay(oldWay.getId(), newWay.getId()); restrictions.removeMapping(oldWay.getId(), rr); @@ -261,11 +252,11 @@ * @param maxLength the cut off way is no longer than this value * @return the cut off way or null if cutting not possible */ - private Way cutoffWay(Way w, double cutLength, double maxLength, Coord c1, Coord c2) { - if (w.getPoints().size()<2) { + private Way cutoffWay(Way w, double cutLength, double maxLength) { + if (w.getPoints().size() < 2) { return null; } - + if (w.getPoints().size() >= 3) { // try to use existing points - that does not deform the way Coord firstPoint = w.getPoints().get(0); @@ -288,14 +279,12 @@ // check and update relations so that they use the new way if appropriate changeWayIdInRelations(w, precedingWay); - log.debug("Cut way", w, "at existing point 1. New way:", - precedingWay); + log.debug("Cut way", w, "at existing point 1. New way:", precedingWay); // return the new way return precedingWay; } else { - log.debug("Cannot cut way", w, - "on existing nodes because the first distance is too big:", dist); + log.debug("Cannot cut way", w, "on existing nodes because the first distance is too big:", dist); } } @@ -308,39 +297,15 @@ if (startSegmentLength + segmentLength >= cutLength) { double frac = (cutLength - startSegmentLength) / segmentLength; - // insert a new point at the minimum distance + // insert a new point at the minimum distance Coord cConnection = lastC.makeBetweenPoint(c, frac); - - if (c1 != null && c2 != null && cConnection != null) { - // test if the way using the new point still uses the same - // orientation to the main motorway - double oldAngle = getAngle(c1, c2, c); - double newAngle = getAngle(c1, c2, cConnection); - if (Math.signum(oldAngle) != Math.signum(newAngle)) { - double bestAngleDiff = 180.0d; - Coord bestCoord = cConnection; - for (Coord cNeighbour : getDirectNeighbours(cConnection)) { - double neighbourAngle = getAngle(c1, c2, cNeighbour); - if (Math.signum(oldAngle) == Math.signum(neighbourAngle) && - Math.abs(oldAngle - neighbourAngle) < bestAngleDiff) { - bestAngleDiff = Math.abs(oldAngle - neighbourAngle); - bestCoord = cNeighbour; - } - } - if (log.isDebugEnabled()) { - log.debug("Changed orientation:", oldAngle, "to", - newAngle); - log.debug("on Link", w); - log.debug("Corrected coord ", cConnection, "to", - bestCoord); - } - cConnection = bestCoord; - } - } - + // check if we find a point which is closer to Garmin point in 24 resolution + Coord alternative = ExtNumbers.rasterLineNearPoint(c, lastC, cConnection, false); + if (alternative != null) + cConnection = alternative; // create the new way with identical tags - w.getPoints().add(i,cConnection); - Way precedingWay = new Way(w.getOriginalId(), new ArrayList(w.getPoints().subList(0, i+1))); + w.getPoints().add(i, cConnection); + Way precedingWay = new Way(w.getOriginalId(), new ArrayList(w.getPoints().subList(0, i + 1))); precedingWay.setFakeId(); precedingWay.copyTags(w); @@ -354,7 +319,7 @@ changeWayIdInRelations(w, precedingWay); // return the split way - return precedingWay; + return precedingWay; } lastC = c; } @@ -364,71 +329,18 @@ } /** - * TODO: move to Coord class? - * Retrieve a list of all Coords that are the direct neighbours of - * the given Coord. A neighbours latitude and longitude does not differ - * more than one Garmin unit from the given Coord. - * @param c the Coord for which the neighbours should be retrieved. - * @return all neighbours of c - */ - private List getDirectNeighbours(Coord c) { - List neighbours = new ArrayList<>(8); - for (int dLat = -1; dLat < 2; dLat++) { - for (int dLon = -1; dLon < 2; dLon++) { - if (dLat != 0 || dLon != 0) { - neighbours.add(new Coord(c.getLatitude() + dLat, c.getLongitude() + dLon)); - } - } - } - return neighbours; - } - - /** - * Retrieves if the given node is tagged as motorway exit. So it must contain at least the tags + * Retrieves if the given node is tagged as usable exit. So it must contain at least the tags * highway=motorway_junction and one of the tags ref, name or exit_to. * @param node the node to check - * @return true the node is a motorway exit, false the node is not a - * motorway exit + * @return true if the node is a usable exit, else false */ private boolean isTaggedAsExit(Node node) { - return "motorway_junction".equals(node.getTag("highway")) - && (node.getTag("ref") != null || (nameFinder.getName(node) != null) || node.getTag("exit_to") != null); - } - - /** - * Retrieve all nodes that are connected to the given node either in - * driving direction or reverse. - * @param node a coord - * @param drivingDirection true driving direction; false reverse direction - * @return a list of all coords an the connection ways - */ - private List> getNextNodes(Coord node, boolean drivingDirection) { - List> nextNodes = new ArrayList<>(); - - Set connectedWays = wayNodes.get(node); - for (Way w : connectedWays) { - // get the index of the node - int index = w.getPoints().indexOf(node); - if (index < 0) { - // this should not happen - log.error("Cannot find node "+node+" in way "+w); - continue; - } - - boolean oneWayDirection = isOnewayInDirection(w); - // calc the index of the next node - index += (drivingDirection ? 1 : -1) * (oneWayDirection ? 1 : -1); - - if (index >= 0 && index < w.getPoints().size()) { - nextNodes.add(new AbstractMap.SimpleEntry(w.getPoints().get(index), w)); - } - } - - return nextNodes; - } - - /** - * Cuts motorway_link and trunk_link ways into three parts to be able to get + return "motorway_junction".equals(node.getTag(TAG_KEY_HIGHWAY)) + && (node.getTag("ref") != null || (nameFinder.getName(node) != null) || node.getTag(TAG_KEY_EXIT_TO) != null); + } + + /** + * Cuts major roads into three parts to be able to get * a hint on Garmin GPS. This happens if the the option process-exits is set * and the way is connected to an exit node (highway=motorway_junction) * and/or the option process-destination is set and the destination tag is @@ -450,279 +362,212 @@ * mkgmap:exit_hint_ref, mkgmap:exit_hint_name and/or mkgmap:exit_hint_exit_to. */ private void processWays() { + cleanupLinkDestWays(); + createExitHints(); + createDestinationHints(); + } + + private void cleanupLinkDestWays() { // remove the adjacent links from the destinationLinkWays list // to avoid duplicate dest_hints - Queue linksWithDestination = new ArrayDeque<>(); - linksWithDestination.addAll(destinationLinkWays.values()); + Queue linksWithDestination = new ArrayDeque<>(destinationLinkWays); log.debug(destinationLinkWays.size(),"links with destination tag"); while (!linksWithDestination.isEmpty()) { Way linkWay = linksWithDestination.poll(); - String destination = linkWay.getTag("mkgmap:dest_hint_work"); + String destination = linkWay.getTag(TAG_KEY_DEST_HINT_WORK); if (log.isDebugEnabled()) - log.debug("Check way",linkWay.getId(),linkWay.toTagString()); + log.debug("Check way", linkWay.getId(), linkWay.toTagString()); // Retrieve all adjacent ways of the current link - Coord c = linkWay.getLastPoint(); - if (isOnewayOppositeDirection(linkWay)) { - c = linkWay.getFirstPoint(); - } + Coord c = isOnewayOppositeDirection(linkWay) ? linkWay.getFirstPoint() : linkWay.getLastPoint(); Set nextWays = adjacentWays.get(c); if (nextWays != null) { for (Way connectedWay : nextWays) { - String nextDest = connectedWay.getTag("mkgmap:dest_hint_work"); + String nextDest = connectedWay.getTag(TAG_KEY_DEST_HINT_WORK); if (log.isDebugEnabled()) log.debug("Followed by",connectedWay.getId(),connectedWay.toTagString()); - + // remove the way from destination handling only if both ways are connected with start/end points // otherwise it is a crossroads and therefore both ways need to be handled - boolean startEndConnection = c.equals(connectedWay.getFirstPoint()); - if (startEndConnection && !connectedWay.equals(linkWay) - && connectedWay.getTag("highway").endsWith("_link") + Coord c2 = isOnewayOppositeDirection(connectedWay) ? connectedWay.getLastPoint() + : connectedWay.getFirstPoint(); + + boolean startEndConnection = c == c2; + if (startEndConnection && !connectedWay.equals(linkWay) + && connectedWay.getTag(TAG_KEY_HIGHWAY).endsWith("_link") && destination.equals(nextDest)) { // do not use this way because there is another link before that with the same destination - destinationLinkWays.remove(connectedWay.getId()); - if (log.isDebugEnabled()) - log.debug("Removed",connectedWay.getId(),connectedWay.toTagString()); - } - } - } - } - log.debug(destinationLinkWays.size(),"links with destination tag after cleanup"); - - if (processExits) { - // collect all nodes of highway=motorway/trunk ways so that we can check if an exit node - // belongs to a motorway/trunk or is a "subexit" within a motorway/trunk junction - - Map> highwayCoords = new LinkedHashMap<>(); - for (String type : highwayTypes){ - highwayCoords.put(type, new HashSet()); - } - for (Way w : saver.getWays().values()) { - String highwayTag = w.getTag("highway"); - if (highwayTag == null) - continue; - if (highwayTypes.contains(highwayTag)){ - Set set = highwayCoords.get(highwayTag); - set.addAll(w.getPoints()); - } - } - - // get all nodes tagged with highway=motorway_junction - for (Node exitNode : saver.getNodes().values()) { - if (isTaggedAsExit(exitNode) && saver.getBoundingBox().contains(exitNode.getLocation())) { - String expectedHighwayTag = null; - for (Entry> entry : highwayCoords.entrySet()){ - if (entry.getValue().contains(exitNode.getLocation())){ - expectedHighwayTag = entry.getKey(); - break; - } - } - if (expectedHighwayTag == null){ - // use exits only if they are located on a motorway or trunk - if (log.isDebugEnabled()) - log.debug("Skip non highway exit:", exitNode.toBrowseURL(), exitNode.toTagString()); - continue; - } - // retrieve all ways with this exit node - Set exitWays = adjacentWays.get(exitNode.getLocation()); - if (exitWays==null) { - log.debug("Exit node", exitNode, "has no connected ways. Skip it."); - continue; - } - - // retrieve the next node on the highway to be able to check if - // the inserted node has the correct orientation - List> nextNodes = getNextNodes(exitNode.getLocation(), true); - Coord nextHighwayNode = null; - int countMatches = 0; - for (Entry nextNode : nextNodes) { - if (expectedHighwayTag.equals(nextNode.getValue().getTag("highway"))) { - nextHighwayNode = nextNode.getKey(); - countMatches++; - } - } - if (countMatches > 1){ - // may happen when the highway is a link which splits further into two or more links - // ignore the node - nextHighwayNode = null; - } - - // use link ways only - for (Way w : exitWays) { - destinationLinkWays.remove(w.getId()); - if (isNotOneway(w)) { - log.warn("Ignore way",w,"because it is not oneway"); - continue; - } - if (w.isViaWay()){ - log.warn("Ignore way",w,"because it is a via way in a restriction relation"); - continue; - } - - String highwayLinkTag = w.getTag("highway"); - if (highwayLinkTag.endsWith("_link")) { - log.debug("Try to cut",highwayLinkTag, w, "into three parts for giving hint to exit", exitNode); - // calc the way length to decide how to cut the way - double wayLength = getLength(w); - if (wayLength < 10 && w.getPoints().size() < 3) { - log.info("Way", w, "is too short (", wayLength," m) to cut it into several pieces. Cannot place exit hint."); - continue; - } - - - // now create three parts: - // wayPart1: original tags only - // hintWay: original tags plus the mkgmap:exit_hint* tags - // w: rest of the original way - - double cut1 = Math.min(wayLength/2,20.0); - double cut2 = Math.min(wayLength, 100); - Way wayPart1 = cutoffWay(w,cut1, cut2, exitNode.getLocation(), nextHighwayNode); - if (wayPart1 == null) { - log.info("Way", w, "is too short to cut at least ",cut1,"m from it. Cannot create exit hint."); - continue; - } else { - if (log.isDebugEnabled()) - log.debug("Cut off way", wayPart1, wayPart1.toTagString()); - } - - Way hintWay = w; - if (wayLength > 50) { - hintWay = cutoffWay(w, 10.0, 50.0, exitNode.getLocation(), nextHighwayNode); - } - if (hintWay == null) { - log.info("Way", w, "is too short to cut at least 20m from it. Cannot create exit hint."); - } else { - hintWay.addTag("mkgmap:exit_hint", "true"); - if (processDestinations) { - String hint = hintWay.getTag("mkgmap:dest_hint_work"); - if (hint != null){ - hintWay.deleteTag("mkgmap:dest_hint_work"); - hintWay.addTag("mkgmap:dest_hint", hint); - } - } - if (exitNode.getTag("ref") != null) - hintWay.addTag("mkgmap:exit_hint_ref", exitNode.getTag("ref")); - if (countMatches == 1 && exitNode.getTag("exit_to") != null) { - hintWay.addTag("mkgmap:exit_hint_exit_to", exitNode.getTag("exit_to")); - } - if (nameFinder.getName(exitNode) != null){ - hintWay.addTag("mkgmap:exit_hint_name", nameFinder.getName(exitNode)); - } - - if (log.isInfoEnabled()) - log.info("Cut off exit hint way", hintWay, hintWay.toTagString()); - } + boolean removed = destinationLinkWays.remove(connectedWay); + if (removed && log.isDebugEnabled()) { + log.debug("Removed", connectedWay.getId(), connectedWay.toTagString()); } } } } } - + log.debug(destinationLinkWays.size(),"links with destination tag after cleanup"); + } + + private void createExitHints() { + if(!processExits) + return; + + List hwSorted = new ArrayList<>(highwayTypes); + for (Node exitNode : saver.getNodes().values()) { + if (isTaggedAsExit(exitNode) && saver.getBoundingBox().contains(exitNode.getLocation())) { + // node is tagged with highway=motorway_junction and has usable exit info + processExitNode(exitNode, hwSorted); + } + } + } + + /** + * Check if we can add an exit hint for the given exit node. + * @param exitNode + * @param hwSorted + * @param highwayCoords + */ + private void processExitNode(Node exitNode, List hwSorted) { + // retrieve all ways with this exit node + Set exitWays = adjacentWays.get(exitNode.getLocation()); + if (exitWays == null) { + log.debug("Exit node", exitNode, "has no connected ways. Skip it."); + return; + } + String exitTo = exitNode.getTag(TAG_KEY_EXIT_TO); + if (exitTo != null) { + int countMatches = 0; + int preferred = Integer.MAX_VALUE; + for (Way w : exitWays) { + String hw = w.getTag(TAG_KEY_HIGHWAY); + int pos = hwSorted.indexOf(hw); + if (pos < preferred) { + preferred = pos; + countMatches = 1; + } else if (pos == preferred) { + countMatches++; + } + } + if (countMatches != 1) + exitTo = null; // don't use exit_to info, it typically contains info for all the ways + } + + // use link ways only + for (Way w : exitWays) { + destinationLinkWays.remove(w); + if(!canSplit(w)) + continue; + processExitWay(exitNode, w, exitTo); + } + } + + private void processExitWay(Node exitNode, Way w, String exitTo) { + String highwayLinkTag = w.getTag(TAG_KEY_HIGHWAY); + if (highwayLinkTag.endsWith("_link")) { + log.debug("Try to cut", highwayLinkTag, w, "into three parts for giving hint to exit", exitNode); + Way hintWay = splitWay(w, "exit"); + if (hintWay != null) { + fixDestHint(hintWay); + hintWay.addTag("mkgmap:exit_hint", "true"); + if (exitNode.getTag("ref") != null) + hintWay.addTag("mkgmap:exit_hint_ref", exitNode.getTag("ref")); + if (exitTo != null) { + hintWay.addTag("mkgmap:exit_hint_exit_to", exitTo); + } + if (nameFinder.getName(exitNode) != null) { + hintWay.addTag("mkgmap:exit_hint_name", nameFinder.getName(exitNode)); + } + + if (log.isInfoEnabled()) + log.info("Cut off exit hint way", hintWay, hintWay.toTagString()); + } + } + } + + private String fixDestHint(Way hintWay) { if (processDestinations) { - // use link ways only - while (!destinationLinkWays.isEmpty()) { - Way w = destinationLinkWays.values().iterator().next(); - destinationLinkWays.remove(w.getId()); - if (isNotOneway(w)) { - log.warn("Ignore way",w,"because it is not oneway"); - continue; + String hint = hintWay.getTag(TAG_KEY_DEST_HINT_WORK); + if (hint != null) { + hintWay.deleteTag(TAG_KEY_DEST_HINT_WORK); + hintWay.addTag("mkgmap:dest_hint", hint); + } + return hint; + } + return null; + } + + private void createDestinationHints() { + if (!processDestinations) + return; + // use link ways only + while (!destinationLinkWays.isEmpty()) { + Way w = destinationLinkWays.iterator().next(); + destinationLinkWays.remove(w); + String highwayLinkTag = w.getTag(TAG_KEY_HIGHWAY); + if (!canSplit(w) || !highwayLinkTag.endsWith("_link")) + continue; + + log.debug("Try to cut",highwayLinkTag, w, "into three parts for giving hint"); + + Way hintWay = splitWay(w, "destination"); + if (hintWay != null) { + String hint = fixDestHint(hintWay); + if (hint == null) { + log.error("Internal error in process_destination with way", hintWay); } - if (w.isViaWay()){ - log.warn("Ignore way",w,"because it is a via way in a restriction relation"); - continue; - } - - String highwayLinkTag = w.getTag("highway"); - if (highwayLinkTag.endsWith("_link")) { - log.debug("Try to cut",highwayLinkTag, w, "into three parts for giving hint"); - - Coord firstNode = w.getPoints().get(0); - Coord secondNode = w.getPoints().get(1); - // retrieve the next node on the highway to be able to check if - // the inserted node has the correct orientation - List> nextNodes = getNextNodes(firstNode, true); - Coord nextHighwayNode = null; - double angle = Double.MAX_VALUE; - for (Entry nextNode : nextNodes) { - if (nextNode.getValue().equals(w)) { - continue; - } - double thisAngle = getAngle(firstNode, secondNode, nextNode.getKey()); - if (Math.abs(thisAngle) < angle) { - angle = Math.abs(thisAngle); - nextHighwayNode = nextNode.getKey(); - } - } - - // calc the way length to decide how to cut the way - double wayLength = getLength(w); - if (wayLength < 10) { - log.info("Way", w, "is too short (", wayLength," m) to cut it into several pieces. Cannot place destination hint."); - continue; - } - - // now create three parts: - // wayPart1: original tags only - // hintWay: original tags plus the mkgmap:exit_hint* tags - // w: rest of the original way - - double cut1 = Math.min(wayLength/2, 20.0); - double cut2 = Math.min(wayLength, 100); - Way wayPart1 = cutoffWay(w, cut1, cut2, firstNode, nextHighwayNode); - if (wayPart1 == null) { - log.info("Way", w, "is too short to cut at least 10m from it. Cannot create destination hint."); - continue; - } else { - if (log.isDebugEnabled()) - log.debug("Cut off way", wayPart1, wayPart1.toTagString()); - } - - Way hintWay = w; - if (wayLength > 50) { - hintWay = cutoffWay(w, 10.0, 50.0, firstNode, nextHighwayNode); - } - if (hintWay == null) { - log.info("Way", w, "is too short to cut at least 20m from it. Cannot create destination hint."); - } else { - String hint = hintWay.getTag("mkgmap:dest_hint_work"); - if (hint != null){ - hintWay.deleteTag("mkgmap:dest_hint_work"); - hintWay.addTag("mkgmap:dest_hint", hint); - } else { - log.error("Internal error in process_destination with way",hintWay); - } - - if (log.isInfoEnabled()) - log.info("Cut off exit hint way", hintWay, hintWay.toTagString()); - } - } - } - } - } - - /** - * Retrieves the angle in clockwise direction between the line (cCenter, c1) - * and the line (cCenter, c2). - * @param cCenter the common point of both lines - * @param c1 point of the first line - * @param c2 point of the second line - * @return the angle [-180; 180] - */ - private double getAngle(Coord cCenter, Coord c1, Coord c2) - { - int dx1 = c1.getLongitude() - cCenter.getLongitude(); - int dy1 = -(c1.getLatitude() - cCenter.getLatitude()); - - int dx2 = c2.getLongitude() - cCenter.getLongitude(); - int dy2 = -(c2.getLatitude() - cCenter.getLatitude()); - - double inRads1 = Math.atan2(dy1, dx1); - double inRads2 = Math.atan2(dy2, dx2); - - return Math.toDegrees(inRads2) - Math.toDegrees(inRads1); + + if (log.isInfoEnabled()) + log.info("Cut off exit hint way", hintWay, hintWay.toTagString()); + } + } + } + + /** + * Try to split the way. If successful, it creates two or three parts. + *
    + *
  • wayPart1: original tags only
  • + *
  • hintWay: original tags plus the mkgmap:exit_hint* tags
  • + *
  • rest of the original way (if any remains)
  • + *
+ * @param w the way to split + * @param hintType the type of hint that is added (exit or destination), use for logging only + * @return null if not successful, else the hintWay + */ + private Way splitWay(Way w, String hintType) { + // calc the way length to decide how to cut the way + double wayLength = w.calcLengthInMetres(); + double cut1 = Math.min(wayLength / 2, 20.0); + double cut2 = Math.min(wayLength, 100); + Way wayPart1 = cutoffWay(w, cut1, cut2); + if (wayPart1 == null) { + log.info("Way", w, "is too short to cut at least", cut1, "m from it. Cannot create", hintType, "hint."); + return null; + } + if (log.isDebugEnabled()) + log.debug("Cut off way", wayPart1, wayPart1.toTagString()); + + Way hintWay = w; + if (wayLength > 50) { + hintWay = cutoffWay(w, 10.0, 50.0); + } + if (hintWay == null) { + log.info("Way", w, "is too short to cut at least 20m from it. Cannot create", hintType, "hint."); + } + return hintWay; + } + + private static boolean canSplit(Way w) { + if (isNotOneway(w)) { + log.warn("Ignore way", w, "because it is not oneway"); + return false; + } + if (w.isViaWay()) { + log.warn("Ignore way", w, "because it is a via way in a restriction relation"); + return false; + } + return w.calcLengthInMetres() >= 0.3; } /** @@ -732,7 +577,6 @@ adjacentWays = null; wayNodes = null; destinationLinkWays = null; - linkTypes = null; saver = null; nameFinder = null; } @@ -747,6 +591,7 @@ // referenced in the style file Set tags = new HashSet<>(); tags.add("highway"); + tags.add("oneway"); tags.add("destination"); tags.add("destination:lanes"); tags.add("destination:lanes:forward"); @@ -754,7 +599,7 @@ tags.add("destination:forward"); tags.add("destination:backward"); tags.add("destination:street"); - if (processExits){ + if (processExits) { tags.add("exit_to"); tags.add("ref"); } @@ -765,11 +610,8 @@ public void end() { log.info("LinkDestinationHook started"); - retrieveWays(); - - if (processExits || processDestinations) - processWays(); - + retrieveWaysAndRelations(); + processWays(); cleanup(); log.info("LinkDestinationHook finished"); @@ -780,16 +622,16 @@ * @param w the way * @return true way is oneway */ - private boolean isOnewayInDirection(Way w) { - if (w.tagIsLikeYes("oneway")) { + private static boolean isOnewayInDirection(Way w) { + if (w.tagIsLikeYes(TAG_KEY_ONEWAY)) { return true; } // check if oneway is set implicitly by the highway type (motorway and motorway_link) - String onewayTag = w.getTag("oneway"); - String highwayTag = w.getTag("highway"); + String onewayTag = w.getTag(TAG_KEY_ONEWAY); + String highwayTag = w.getTag(TAG_KEY_HIGHWAY); return onewayTag == null && highwayTag != null - && (highwayTag.equals("motorway") || highwayTag.equals("motorway_link")); + && ("motorway".equals(highwayTag)|| "motorway_link".equals(highwayTag)); } /** @@ -797,8 +639,8 @@ * @param w the way * @return true way is oneway in opposite direction */ - private boolean isOnewayOppositeDirection(Way w) { - return "-1".equals(w.getTag("oneway")); + private static boolean isOnewayOppositeDirection(Way w) { + return "-1".equals(w.getTag(TAG_KEY_ONEWAY)); } /** @@ -806,30 +648,7 @@ * @param w the way * @return true way is not oneway */ - private boolean isNotOneway(Way w) { - return "no".equals(w.getTag("oneway")) || (!isOnewayInDirection(w) && !isOnewayOppositeDirection(w)); - } - - /** Private length function without caching */ - private LengthFunction length = new LengthFunction() { - @Override - public boolean isCached() { - return false; - } - }; - - /** - * Retrieve the length of the given way. - * @param w way - * @return length in m - */ - private double getLength(Way w) { - String lengthValue = length.value(w); - try { - return Math.round(Double.valueOf(lengthValue)); - } catch (Exception exp) { - return 0; - } - } - + private static boolean isNotOneway(Way w) { + return "no".equals(w.getTag(TAG_KEY_ONEWAY)) || (!isOnewayInDirection(w) && !isOnewayOppositeDirection(w)); + } } diff --git a/src/uk/me/parabola/mkgmap/reader/osm/RestrictionRelation.java b/src/uk/me/parabola/mkgmap/reader/osm/RestrictionRelation.java index f50d47d..943823f 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/RestrictionRelation.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/RestrictionRelation.java @@ -43,25 +43,25 @@ public class RestrictionRelation extends Relation { private static final Logger log = Logger.getLogger(RestrictionRelation.class); - private List fromWayIds = new ArrayList<>(2); - private List toWayIds = new ArrayList<>(2); - private List viaWayIds = new ArrayList<>(2); - private List viaPoints = new ArrayList<>(2); - private Map> updatedViaWays = new HashMap<>(); - private Coord viaCoord; - private String restriction; + private List fromWayIds = new ArrayList<>(2); + private List toWayIds = new ArrayList<>(2); + private List viaWayIds = new ArrayList<>(2); + private List viaPoints = new ArrayList<>(2); + private Map> updatedViaWays = new HashMap<>(); + private Coord viaCoord; + private String restriction; private byte exceptMask; - private char dirIndicator; - private String messagePrefix; - private boolean valid; - private boolean evalWasCalled; + private char dirIndicator; + private String msgPrefix; + private boolean valid; + private boolean evalWasCalled; // These tags are not loaded by default but if they exist issue a warning - private final static String[] unsupportedTags = { "day_on", "day_off", "hour_on", "hour_off" }; - - private final static byte DEFAULT_EXCEPT_MASK = AccessTagsAndBits.FOOT | AccessTagsAndBits.EMERGENCY; - - private final static List supportedRestrictions = Arrays.asList( + private static final String[] unsupportedTags = { "day_on", "day_off", "hour_on", "hour_off" }; + + private static final byte DEFAULT_EXCEPT_MASK = AccessTagsAndBits.FOOT | AccessTagsAndBits.EMERGENCY; + + private static final List supportedRestrictions = Arrays.asList( "no_right_turn", "no_left_turn", "no_u_turn", "no_straight_on", "only_right_turn", "only_left_turn", "only_straight_on", "no_entry", "no_exit" @@ -75,121 +75,112 @@ */ public RestrictionRelation(Relation other) { setId(other.getId()); - messagePrefix = "Turn restriction " + toBrowseURL(); + msgPrefix = "Turn restriction " + toBrowseURL(); copyTags(other); for (Map.Entry pair : other.getElements()) { addElement(pair.getKey(), pair.getValue()); } } - + /** * The evaluation should happen after style processing. * Normally this is called from the {@link RelationStyleHook} * Performs also diverse plausibility checks. * @param bbox tile boundary */ - public void eval(Area bbox){ - if (evalWasCalled){ - log.error(messagePrefix,"internal error: eval() was already called"); + public void eval(Area bbox) { + if (evalWasCalled) { + log.error(msgPrefix, "internal error: eval() was already called"); fromWayIds.clear(); toWayIds.clear(); viaWayIds.clear(); } evalWasCalled = true; - if (getTag("type") == null){ + if (getTag("type") == null) { // style removed the tag - log.info(messagePrefix, "type tag was removed, relation is ignored"); - valid = false; - return; - } - - List fromWays = new ArrayList<>(); - List toWays = new ArrayList<>(); - List viaWays = new ArrayList<>(); + log.info(msgPrefix, "type tag was removed, relation is ignored"); + valid = false; + return; + } + + List fromWays = new ArrayList<>(); + List toWays = new ArrayList<>(); + List viaWays = new ArrayList<>(); final String browseURL = toBrowseURL(); valid = true; // find out what kind of restriction we have and to which vehicles it applies exceptMask = DEFAULT_EXCEPT_MASK; - String specifc_type = getTag("restriction"); - int count_unknown = 0; + String specificType = getTag("restriction"); + int countUnknown = 0; Map vehicles = getTagsWithPrefix("restriction:", true); - if (vehicles.isEmpty() == false){ + if (!vehicles.isEmpty()) { exceptMask = (byte) 0xff; Iterator> iter = vehicles.entrySet().iterator(); - while (iter.hasNext()){ + while (iter.hasNext()) { Map.Entry entry = iter.next(); String vehicle = entry.getKey(); - if (setExceptMask(vehicle, false) == false) - count_unknown++; - if (specifc_type == null) - specifc_type = entry.getValue(); - else if (specifc_type.equals(entry.getValue()) == false){ - log.warn(messagePrefix, "is invalid, it specifies different kinds of turns"); + if (!setExceptMask(vehicle, false)) + countUnknown++; + if (specificType == null) + specificType = entry.getValue(); + else if (!specificType.equals(entry.getValue())) { + log.warn(msgPrefix, "is invalid, it specifies different kinds of turns"); valid = false; break; } } - if (valid && vehicles.size() == count_unknown){ - log.warn(messagePrefix, "no supported vehicle in turn restriction"); + if (valid && vehicles.size() == countUnknown) { + log.warn(msgPrefix, "no supported vehicle in turn restriction"); valid = false; return; } } - if (specifc_type == null){ + if (specificType == null) { // style removed the tag - log.info(messagePrefix, "no valid restriction tag found"); - valid = false; - return; - } - restriction = specifc_type.trim(); - - messagePrefix = "Turn restriction (" + restriction + ") " + browseURL; - if (supportedRestrictions.contains(restriction) == false){ - log.warn(messagePrefix, "ignoring unsupported restriction type '" + restriction + "'"); - valid = false; - return; - } - - String dirInfo = ""; + log.info(msgPrefix, "no valid restriction tag found"); + valid = false; + return; + } + restriction = specificType.trim(); + + msgPrefix = "Turn restriction (" + restriction + ") " + browseURL; + if (!supportedRestrictions.contains(restriction)) { + log.warn(msgPrefix, "ignoring unsupported restriction type '" + restriction + "'"); + valid = false; + return; + } + + dirIndicator = '?'; if (restriction.contains("left")) - dirInfo += "l"; - if (restriction.contains("right")) - dirInfo += "r"; - if (restriction.contains("straight")) - dirInfo += "s"; - if (restriction.endsWith("u_turn")) - dirInfo += "u"; - if (dirInfo.length() > 1){ - log.warn(messagePrefix, "ignoring unsupported restriction type '" + restriction + "'"); - valid = false; - return; - } else if (dirInfo.length() == 1){ - dirIndicator = dirInfo.charAt(0); - } else - dirIndicator = '?'; - + dirIndicator = 'l'; + else if (restriction.contains("right")) + dirIndicator = 'r'; + else if (restriction.contains("straight")) + dirIndicator = 's'; + else if (restriction.endsWith("u_turn")) + dirIndicator = 'u'; String type = getTag("type"); - if (type.startsWith("restriction:")){ + if (type.startsWith("restriction:")) { exceptMask = (byte) 0xff; String vehicle = type.substring("restriction:".length()); - if (setExceptMask(vehicle, false) == false) { - log.warn(messagePrefix, "ignoring unsupported '" + vehicle + "' in turn restriction"); + if (!setExceptMask(vehicle, false)) { + log.warn(msgPrefix, "ignoring unsupported '" + vehicle + "' in turn restriction"); valid = false; return; } } String except = getTag("except"); - if(except != null) { - for(String vehicle : except.split("[,;]")) { // be nice + if (except != null) { + for (String vehicle : except.split("[,;]")) { // be nice vehicle = vehicle.trim(); setExceptMask(vehicle, true); } } - + for (String unsupportedTag : unsupportedTags) { if (getTag(unsupportedTag) != null) { - log.warn(messagePrefix, "ignoring unsupported '" + unsupportedTag + "' tag"); + log.warn(msgPrefix, "ignoring unsupported '" + unsupportedTag + "' tag"); } } @@ -200,125 +191,119 @@ Coord location = null; - if(viaCoord != null) + if (viaCoord != null) location = viaCoord; - else if(!fromWays.isEmpty() && !fromWays.get(0).getPoints().isEmpty()) + else if (!fromWays.isEmpty() && !fromWays.get(0).getPoints().isEmpty()) location = fromWays.get(0).getFirstPoint(); - else if(!toWays.isEmpty() && !toWays.get(0).getPoints().isEmpty()) + else if (!toWays.isEmpty() && !toWays.get(0).getPoints().isEmpty()) location = toWays.get(0).getFirstPoint(); - else if(!viaWays.isEmpty() && !viaWays.get(0).getPoints().isEmpty()) + else if (!viaWays.isEmpty() && !viaWays.get(0).getPoints().isEmpty()) location = viaWays.get(0).getFirstPoint(); - if(location != null) - messagePrefix = "Turn restriction (" + restriction + ") " + browseURL + " (at " + location.toOSMURL() + ")"; - - if("to".equals(role)) { - if(!(el instanceof Way)) { - log.warn(messagePrefix, "'to' member", el.toBrowseURL(), "is not a way but it should be"); - } - else if(((Way)el).getPoints().isEmpty()) { - log.warn(messagePrefix, "ignoring empty 'to' way", el.toBrowseURL()); - } - else - toWays.add((Way)el); - } - else if("from".equals(role)) { - if(!(el instanceof Way)) { - log.warn(messagePrefix, "'from' member", el.toBrowseURL(), "is not a way but it should be"); - } - else if(((Way)el).getPoints().isEmpty()) { - log.warn(messagePrefix, "ignoring empty 'from' way", el.toBrowseURL()); - } - else - fromWays.add((Way)el); - } - else if("via".equals(role)) { - if(el instanceof Node) { - if (viaCoord != null){ - log.warn(messagePrefix, "has extra 'via' node", el.toBrowseURL()); - valid = false; - } else - viaCoord = ((Node)el).getLocation(); - } - else if(el instanceof Way) { - if (viaCoord != null){ - log.warn(messagePrefix, "has extra 'via' way", el.toBrowseURL()); - valid = false; - } else - viaWays.add((Way)el); - } - else { - log.warn(messagePrefix, "'via' member", el.toBrowseURL(), "is not a node or way"); - } - } - else if("location_hint".equals(role)) { + if (location != null) + msgPrefix = "Turn restriction (" + restriction + ") " + browseURL + " (at " + location.toOSMURL() + ")"; + + if ("to".equals(role)) { + if (!(el instanceof Way)) { + log.warn(msgPrefix, "'to' member", el.toBrowseURL(), "is not a way but it should be"); + } else if (((Way) el).getPoints().isEmpty()) { + log.warn(msgPrefix, "ignoring empty 'to' way", el.toBrowseURL()); + } else { + toWays.add((Way) el); + } + } else if ("from".equals(role)) { + if (!(el instanceof Way)) { + log.warn(msgPrefix, "'from' member", el.toBrowseURL(), "is not a way but it should be"); + } else if (((Way) el).getPoints().isEmpty()) { + log.warn(msgPrefix, "ignoring empty 'from' way", el.toBrowseURL()); + } else { + fromWays.add((Way) el); + } + } else if ("via".equals(role)) { + if (el instanceof Node) { + if (viaCoord != null) { + log.warn(msgPrefix, "has extra 'via' node", el.toBrowseURL()); + valid = false; + } else { + viaCoord = ((Node) el).getLocation(); + } + } else if (el instanceof Way) { + if (viaCoord != null) { + log.warn(msgPrefix, "has extra 'via' way", el.toBrowseURL()); + valid = false; + } else { + viaWays.add((Way) el); + } + } else { + log.warn(msgPrefix, "'via' member", el.toBrowseURL(), "is not a node or way"); + } + } else if ("location_hint".equals(role)) { // relax - we don't care about this - } - else { - log.warn(messagePrefix, "unknown member role '" + role + "'"); - } - } - + } else { + log.warn(msgPrefix, "unknown member role '" + role + "'"); + } + } if (!valid) return; - - if ("no_entry".equals(restriction) == false){ - if (fromWays.size() > 1){ - log.warn(messagePrefix, "multiple 'from' members are only accepted for no_entry restrictions"); - valid = false; - return; - } - } - if ("no_exit".equals(restriction) == false){ - if (toWays.size() > 1){ - log.warn(messagePrefix, "multiple 'to' members are only accepted for no_exit restrictions"); - valid = false; - return; - } - } - if (viaWays.isEmpty() && viaCoord == null && fromWays.size() == 1 && toWays.size() == 1){ + + if (!"no_entry".equals(restriction) && fromWays.size() > 1) { + log.warn(msgPrefix, "multiple 'from' members are only accepted for no_entry restrictions"); + valid = false; + return; + } + if (!"no_exit".equals(restriction) && toWays.size() > 1) { + log.warn(msgPrefix, "multiple 'to' members are only accepted for no_exit restrictions"); + valid = false; + return; + } + if (viaWays.isEmpty() && viaCoord == null && fromWays.size() == 1 && toWays.size() == 1) { Way fromWay = fromWays.get(0); Way toWay = toWays.get(0); - ListfromPoints = fromWay.getPoints(); - ListtoPoints = toWay.getPoints(); + List fromPoints = fromWay.getPoints(); + List toPoints = toWay.getPoints(); int countSame = 0; - for(Coord fp : fromPoints) { - for(Coord tp : toPoints) { - if(fp == tp){ + for (Coord fp : fromPoints) { + for (Coord tp : toPoints) { + if (fp == tp) { countSame++; viaCoord = fp; } } } - if (countSame > 1){ - log.warn(messagePrefix, "lacks 'via' node and way and the 'from' (", fromWay.toBrowseURL(), ") and 'to' (", toWay.toBrowseURL(), ") ways connect in more than one place"); + if (countSame > 1) { + log.warn(msgPrefix, "lacks 'via' node and way and the 'from' (", fromWay.toBrowseURL(), + ") and 'to' (", toWay.toBrowseURL(), ") ways connect in more than one place"); valid = false; - } else if (viaCoord == null){ - log.warn(messagePrefix, "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + toWay.toBrowseURL() + ") ways don't connect"); + } else if (viaCoord == null) { + log.warn(msgPrefix, "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + + toWay.toBrowseURL() + ") ways don't connect"); valid = false; } else { - if (fromPoints.get(0) != viaCoord && fromPoints.get(fromPoints.size()-1) != viaCoord || - toPoints.get(0) != viaCoord && toPoints.get(toPoints.size()-1) != viaCoord){ - log.warn(messagePrefix, "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + toWay.toBrowseURL() + ") ways don't connect at an end point"); + if (fromPoints.get(0) != viaCoord && fromPoints.get(fromPoints.size() - 1) != viaCoord + || toPoints.get(0) != viaCoord && toPoints.get(toPoints.size() - 1) != viaCoord) { + log.warn(msgPrefix, "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + + toWay.toBrowseURL() + ") ways don't connect at an end point"); valid = false; - } else - log.warn(messagePrefix, "lacks 'via' node (guessing it should be at", viaCoord.toOSMURL() + ", why don't you add it to the OSM data?)"); - } - } - - if(fromWays.isEmpty() ) { - log.warn(messagePrefix, "lacks 'from' way"); - valid = false; - } - - if(toWays.isEmpty()) { - log.warn(messagePrefix, "lacks 'to' way"); - valid = false; - } - - if ((fromWays.size() > 1 || toWays.size() > 1) && viaWays.isEmpty() == false){ - log.warn(messagePrefix, "'via' way(s) are not supported with multiple 'from' or 'to' ways"); + } else { + log.warn(msgPrefix, "lacks 'via' node (guessing it should be at", + viaCoord.toOSMURL() + ", why don't you add it to the OSM data?)"); + } + } + } + + if (fromWays.isEmpty()) { + log.warn(msgPrefix, "lacks 'from' way"); + valid = false; + } + + if (toWays.isEmpty()) { + log.warn(msgPrefix, "lacks 'to' way"); + valid = false; + } + + if ((fromWays.size() > 1 || toWays.size() > 1) && !viaWays.isEmpty()) { + log.warn(msgPrefix, "'via' way(s) are not supported with multiple 'from' or 'to' ways"); valid = false; } if (!valid) @@ -326,13 +311,13 @@ for (List ways : Arrays.asList(fromWays,viaWays,toWays)){ for (Way way : ways){ if (way.getPoints().size() < 2){ - log.warn(messagePrefix,"way",way.toBrowseURL(),"has less than 2 points, restriction is ignored"); + log.warn(msgPrefix,"way",way.toBrowseURL(),"has less than 2 points, restriction is ignored"); valid = false; } else { if (way.hasIdenticalEndPoints()){ if (ways == toWays && dirIndicator != '?') continue; // we try to determine the correct part in RoadNetwork - log.warn(messagePrefix, "way", way.toBrowseURL(), "starts and ends at same node, don't know which one to use"); + log.warn(msgPrefix, "way", way.toBrowseURL(), "starts and ends at same node, don't know which one to use"); valid = false; } } @@ -340,7 +325,7 @@ } if (!valid) return; - if (viaPoints.isEmpty() == false) + if (!viaPoints.isEmpty()) viaCoord = viaPoints.get(0); if(viaCoord == null && viaWays.isEmpty()) { @@ -351,12 +336,12 @@ viaPoints.clear(); Coord v1 = viaCoord; Coord v2 = viaCoord; - if (viaWays.isEmpty() == false){ + if (!viaWays.isEmpty()) { v1 = viaWays.get(0).getFirstPoint(); v2 = viaWays.get(0).getLastPoint(); } // check if all from ways are connected at the given via point or with the given via ways - for (Way fromWay : fromWays){ + for (Way fromWay : fromWays) { Coord e1 = fromWay.getFirstPoint(); Coord e2 = fromWay.getLastPoint(); if (e1 == v1 || e2 == v1) @@ -364,23 +349,23 @@ else if (e1 == v2 || e2 == v2) viaCoord = v2; else { - log.warn(messagePrefix, "'from' way", fromWay.toBrowseURL(), "doesn't start or end at 'via' node or way"); + log.warn(msgPrefix, "'from' way", fromWay.toBrowseURL(), "doesn't start or end at 'via' node or way"); valid = false; - } + } } if (!valid) return; viaPoints.add(viaCoord); // check if via ways are connected in the given order - for (int i = 0; i < viaWays.size();i++){ + for (int i = 0; i < viaWays.size(); i++) { Way way = viaWays.get(i); - Coord v = viaPoints.get(viaPoints.size()-1); + Coord v = viaPoints.get(viaPoints.size() - 1); if (way.getFirstPoint() == v) v2 = way.getLastPoint(); else if (way.getLastPoint() == v) v2 = way.getFirstPoint(); else { - log.warn(messagePrefix, "'via' way", way.toBrowseURL(), "doesn't start or end at",v.toDegreeString()); + log.warn(msgPrefix, "'via' way", way.toBrowseURL(), "doesn't start or end at", v.toDegreeString()); valid = false; } viaPoints.add(v2); @@ -388,55 +373,55 @@ // check if all via points are inside the bounding box int countInside = 0; - for (Coord via: viaPoints){ - if(bbox.contains(via)) + for (Coord via : viaPoints) { + if (bbox.contains(via)) ++countInside; } if (countInside == 0) valid = false; - else if (countInside > 0 && countInside < viaPoints.size()){ - log.warn(messagePrefix,"via way crosses tile boundary. Don't know how to save that, ignoring it"); - valid = false; - } - + else if (countInside > 0 && countInside < viaPoints.size()) { + log.warn(msgPrefix, "via way crosses tile boundary. Don't know how to save that, ignoring it"); + valid = false; + } + if (!valid) return; // check if all to ways are connected to via point or last via way - Coord lastVia = viaPoints.get(viaPoints.size()-1); - for (Way toWay : toWays){ + Coord lastVia = viaPoints.get(viaPoints.size() - 1); + for (Way toWay : toWays) { Coord e1 = toWay.getFirstPoint(); Coord e2 = toWay.getLastPoint(); - if(e1 != lastVia && e2 != lastVia) { - log.warn(messagePrefix, "'to' way", toWay.toBrowseURL(), "doesn't start or end at 'via' node or way"); + if (e1 != lastVia && e2 != lastVia) { + log.warn(msgPrefix, "'to' way", toWay.toBrowseURL(), "doesn't start or end at 'via' node or way"); valid = false; - } - } - if (valid && !viaWays.isEmpty() && restriction.startsWith("only")){ - log.warn(messagePrefix, "check: 'via' way(s) are used in",restriction,"restriction"); - } - if (valid){ - // make sure that via way(s) don't appear in the from or to lists - for (Way w: viaWays){ - if (fromWays.contains(w)){ - log.warn(messagePrefix, "'via' way",w.toBrowseURL(),"appears also as 'from' way"); + } + } + if (valid && !viaWays.isEmpty() && restriction.startsWith("only")) { + log.warn(msgPrefix, "check: 'via' way(s) are used in", restriction, "restriction"); + } + if (valid) { + // make sure that via way(s) don't appear in the from or to lists + for (Way w : viaWays) { + if (fromWays.contains(w)) { + log.warn(msgPrefix, "'via' way", w.toBrowseURL(), "appears also as 'from' way"); valid = false; } - if (toWays.contains(w)){ - log.warn(messagePrefix, "'via' way",w.toBrowseURL(),"appears also as 'to' way"); + if (toWays.contains(w)) { + log.warn(msgPrefix, "'via' way", w.toBrowseURL(), "appears also as 'to' way"); valid = false; } } } - if (valid){ - for (Way w: fromWays) + if (valid) { + for (Way w : fromWays) fromWayIds.add(w.getId()); - for (Way w: toWays) + for (Way w : toWays) toWayIds.add(w.getId()); - for (Way w: viaWays){ + for (Way w : viaWays) { w.setViaWay(true); viaWayIds.add(w.getId()); } - for (Coord v: viaPoints) + for (Coord v : viaPoints) v.setViaNodeOfRestriction(true); } } @@ -477,7 +462,7 @@ else if(vehicle.equals("foot")) flag = AccessTagsAndBits.FOOT; if (flag == 0){ - log.warn(messagePrefix, "ignoring unsupported vehicle class '" + vehicle + "' in turn restriction"); + log.warn(msgPrefix, "ignoring unsupported vehicle class '" + vehicle + "' in turn restriction"); return false; } @@ -501,7 +486,7 @@ if (viaPoints.get(i) == oldP){ viaPoints.set(i, newP); if (log.isDebugEnabled()){ - log.debug(messagePrefix, restriction, "'via' coord redefined from", + log.debug(msgPrefix, restriction, "'via' coord redefined from", oldP.toOSMURL(), "to", newP.toOSMURL()); } return; @@ -516,14 +501,14 @@ for (Coord v: viaPoints){ CoordNode vn = nodeIdMap.get(v); if (vn == null){ - log.warn(messagePrefix,"via node is not a routing node, restriction relation is ignored"); + log.warn(msgPrefix,"via node is not a routing node, restriction relation is ignored"); return; } viaNodes.add(vn); } if (viaNodes.size() > 6){ - log.warn(messagePrefix,"has more than 6 via nodes, this is not supported"); + log.warn(msgPrefix,"has more than 6 via nodes, this is not supported"); return; } if(restriction == null){ @@ -535,7 +520,7 @@ if(restriction.startsWith("no_")){ for (long fromWayId : fromWayIds){ for (long toWayId : toWayIds){ - grr = new GeneralRouteRestriction("not", exceptMask, messagePrefix); + grr = new GeneralRouteRestriction("not", exceptMask, msgPrefix); grr.setFromWayId(fromWayId); grr.setToWayId(toWayId); grr.setViaNodes(viaNodes); @@ -545,10 +530,10 @@ } } if (log.isInfoEnabled()) - log.info(messagePrefix, restriction, "translated to",addedRestrictions,"img file restrictions"); + log.info(msgPrefix, restriction, "translated to",addedRestrictions,"img file restrictions"); } else if(restriction.startsWith("only_")){ - grr = new GeneralRouteRestriction("only", exceptMask, messagePrefix); + grr = new GeneralRouteRestriction("only", exceptMask, msgPrefix); grr.setFromWayId(fromWayIds.get(0)); grr.setToWayId(toWayIds.get(0)); grr.setViaNodes(viaNodes); @@ -556,7 +541,7 @@ grr.setDirIndicator(dirIndicator); int numAdded = collector.addRestriction(grr); if (numAdded > 0) - log.info(messagePrefix, restriction, "added - allows routing to way", toWayIds.get(0)); + log.info(msgPrefix, restriction, "added - allows routing to way", toWayIds.get(0)); } else { @@ -570,6 +555,7 @@ // relax } + @Override public String toString() { String s = "[restriction id = " + getId() + "(" + restriction + ")"; if (!fromWayIds.isEmpty() && !toWayIds.isEmpty() && viaCoord != null ) @@ -623,9 +609,9 @@ public boolean replaceWay(long oldWayId, long newWayId) { assert evalWasCalled; boolean matched = false; - for (List ways: Arrays.asList(fromWayIds, viaWayIds, toWayIds)){ - for (int i = 0; i < ways.size(); i++){ - if (ways.get(i) == oldWayId){ + for (List ways : Arrays.asList(fromWayIds, viaWayIds, toWayIds)) { + for (int i = 0; i < ways.size(); i++) { + if (ways.get(i) == oldWayId) { ways.set(i, newWayId); matched = true; } @@ -635,24 +621,18 @@ } /** - * check if restriction is still valid if the way with the given id is not in the map - * @param wayId - * @return + * Check if restriction is still valid if the way with the given id is not in the map. + * Can be true if the restriction is a no_exit or no_entry restriction. + * @param wayId the id of the way that was removed + * @return true if the restriction is still valid without this way */ public boolean isValidWithoutWay(long wayId) { assert evalWasCalled; if (viaWayIds.contains(wayId)) return false; fromWayIds.remove(wayId); - if (fromWayIds.isEmpty()) - return false; - // else it must be a no_entry restriction which still is valid - toWayIds.remove(wayId); - if (toWayIds.isEmpty()) - return false; - // else it must be a no_exit restriction which still is valid - return true; + return (!fromWayIds.isEmpty() && !toWayIds.isEmpty()); } /** @@ -662,59 +642,56 @@ * @param nodeIndices */ public void updateViaWay(Way way, List nodeIndices) { - if (!valid) - return; - if (viaWayIds.contains(way.getId()) == false) + if (!valid || !viaWayIds.contains(way.getId())) return; List wayViaPoints = new ArrayList<>(); for (int i : nodeIndices) { wayViaPoints.add(way.getPoints().get(i)); } - List prevViaPoints = updatedViaWays.get(way.getId()); - if(prevViaPoints != null){ + List prevViaPoints = updatedViaWays.get(way.getId()); + if (prevViaPoints != null) { // we may get here when the style adds multiple routable ways for the // OSM way if (prevViaPoints.equals(wayViaPoints)) { // already up to date return; } else { - log.error(messagePrefix, "internal error: via way is updated again with different nodes"); + log.error(msgPrefix, "internal error: via way is updated again with different nodes"); } } Coord first = wayViaPoints.get(0); - Coord last = wayViaPoints.get(wayViaPoints.size()-1); + Coord last = wayViaPoints.get(wayViaPoints.size() - 1); int posFirst = -1; int posLast = -1; for (int i = 0; i < viaPoints.size(); i++) { - if (first == viaPoints.get(i)) + if (first == viaPoints.get(i)) posFirst = i; - if (last== viaPoints.get(i)) + if (last == viaPoints.get(i)) posLast = i; - if (posFirst >= 0 && posLast >= 0){ - if (Math.abs(posLast - posFirst) == 1){ + if (posFirst >= 0 && posLast >= 0) { + if (Math.abs(posLast - posFirst) == 1) { break; } else { -// log.error(messagePrefix, "check self intersection!"); - } - } - } - if (posFirst < 0 || posLast < 0){ - log.error(messagePrefix, "internal error: via way doesn't contain expected points"); - valid = false; - return; - } - if (Math.abs(posLast - posFirst) != 1){ - log.error(messagePrefix, "internal error: via way doesn't contain points in expected position"); - valid = false; - return; - } - List midPoints = new ArrayList<>(wayViaPoints.subList(1, wayViaPoints.size()-1)); - if (posFirst < posLast){ +// check self intersection ? + } + } + } + if (posFirst < 0 || posLast < 0) { + log.error(msgPrefix, "internal error: via way doesn't contain expected points"); + valid = false; + return; + } + if (Math.abs(posLast - posFirst) != 1) { + log.error(msgPrefix, "internal error: via way doesn't contain points in expected position"); + valid = false; + return; + } + List midPoints = new ArrayList<>(wayViaPoints.subList(1, wayViaPoints.size() - 1)); + if (posFirst < posLast) { if (posLast - posFirst > 1) - viaPoints.subList(posFirst+1, posLast).clear(); + viaPoints.subList(posFirst + 1, posLast).clear(); viaPoints.addAll(posFirst + 1, midPoints); - } - else { + } else { if (posFirst - posLast > 1) viaPoints.subList(posLast + 1, posFirst).clear(); Collections.reverse(midPoints); @@ -722,16 +699,16 @@ } int wayPos = viaWayIds.indexOf(way.getId()); - while(viaWayIds.size() > wayPos + 1 && viaWayIds.get(wayPos+1) == way.getId()) + while (viaWayIds.size() > wayPos + 1 && viaWayIds.get(wayPos + 1) == way.getId()) viaWayIds.remove(wayPos); - for (int i = 0; i < midPoints.size(); i++){ - viaWayIds.add(wayPos+1, way.getId()); - } - if (viaPoints.size() != viaWayIds.size()+1){ - log.error(messagePrefix,"internal error: number of via points and via ways no longer fits"); - valid = false; - } else if (viaPoints.size() > 6){ - log.warn(messagePrefix,"has more than 6 via nodes, this is not supported"); + for (int i = 0; i < midPoints.size(); i++) { + viaWayIds.add(wayPos + 1, way.getId()); + } + if (viaPoints.size() != viaWayIds.size() + 1) { + log.error(msgPrefix, "internal error: number of via points and via ways no longer fits"); + valid = false; + } else if (viaPoints.size() > 6) { + log.warn(msgPrefix, "has more than 6 via nodes, this is not supported"); valid = false; } updatedViaWays.put(way.getId(), wayViaPoints); diff --git a/src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java b/src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java index bf929f0..f3f9aa0 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/SeaGenerator.java @@ -60,6 +60,7 @@ public class SeaGenerator implements OsmReadingHooks { private static final Logger log = Logger.getLogger(SeaGenerator.class); + private String precompSea; private boolean generateSeaUsingMP = true; private int maxCoastlineGap; private boolean allowSeaSectors = true; @@ -73,34 +74,29 @@ private ElementSaver saver; - private List shoreline = new ArrayList(); - private boolean roadsReachBoundary; // todo needs setting somehow + private List shoreline = new ArrayList<>(); private boolean generateSeaBackground = true; private String[] coastlineFilenames; private StyleImpl fbRules; /** The size (lat and long) of the precompiled sea tiles */ - public final static int PRECOMP_RASTER = 1 << 15; - - /** - * The directory of the precompiled sea tiles or null if - * precompiled sea should not be used. - */ - private File precompSeaDir; - + public static final int PRECOMP_RASTER = 1 << 15; + + // flags used in the index private static final byte SEA_TILE = 's'; private static final byte LAND_TILE = 'l'; private static final byte MIXED_TILE = 'm'; - private static ThreadLocal precompIndex = new ThreadLocal(); + private static ThreadLocal precompIndex = new ThreadLocal<>(); // useful constants defining the min/max map units of the precompiled sea tiles private static final int MIN_LAT = Utils.toMapUnit(-90.0); private static final int MAX_LAT = Utils.toMapUnit(90.0); private static final int MIN_LON = Utils.toMapUnit(-180.0); private static final int MAX_LON = Utils.toMapUnit(180.0); - private final static Pattern keySplitter = Pattern.compile(Pattern.quote("_")); + private static final Pattern KEY_SPLITTER = Pattern.compile(Pattern.quote("_")); + private static final Pattern SEMICOLON_SPLITTER = Pattern.compile(Pattern.quote(";")); /** * When order-by-decreasing-area we need all bit of sea to be output consistently. @@ -119,139 +115,29 @@ * Returns true only if the option to generate the sea is active, so that * the whole thing is omitted if not used. */ + @Override public boolean init(ElementSaver saver, EnhancedProperties props) { this.saver = saver; - String precompSea = props.getProperty("precomp-sea", null); + precompSea = props.getProperty("precomp-sea", null); if (precompSea != null) { - precompSeaDir = new File(precompSea); - if (precompSeaDir.exists()){ - if (precompIndex.get() == null) { - PrecompData precompData = null; - String internalPath = null; - InputStream indexStream = null; - String indexFileName = "index.txt.gz"; - ZipFile zipFile = null; - try{ - if (precompSeaDir.isDirectory()){ - File indexFile = new File(precompSeaDir, indexFileName); - if (indexFile.exists() == false) { - // check if the unzipped index file exists - indexFileName = "index.txt"; - indexFile = new File(precompSeaDir, indexFileName); - } - if (indexFile.exists()) { - indexStream = new FileInputStream(indexFile); - } - } else if (precompSea.endsWith(".zip")){ - zipFile = new ZipFile(precompSeaDir); - internalPath = "sea/"; - ZipEntry entry = zipFile.getEntry(internalPath + indexFileName); - if (entry == null){ - indexFileName = "index.txt"; - entry = zipFile.getEntry(internalPath + indexFileName); - } - if (entry == null){ - internalPath = ""; - indexFileName = "index.txt.gz"; - entry = zipFile.getEntry(internalPath + indexFileName); - } - if (entry != null){ - indexStream = zipFile.getInputStream(entry); - } else - log.error("Don't know how to read " + precompSeaDir); - } else { - log.error("Don't know how to read " + precompSeaDir); - } - if (indexStream != null){ - if (indexFileName.endsWith(".gz")) { - indexStream = new GZIPInputStream(indexStream); - } - precompData = loadIndex(indexStream); - if (precompData != null){ - if (zipFile != null){ - precompData.precompZipFileInternalPath = internalPath; - precompData.zipFile = zipFile; - } - precompIndex.set(precompData); - } - indexStream.close(); - } - } catch (IOException exp) { - log.error("Cannot read index file", indexFileName, "in", precompSea, exp); -// exp.printStackTrace(); - throw new ExitException("Failed to read required index file in " + precompSeaDir); - } - precompIndex.set(precompData); - } - } else { - log.error("Directory or zip file with precompiled sea does not exist: " - + precompSea); - System.err.println("Directory or zip file with precompiled sea does not exist: " - + precompSea); - precompSeaDir = null; - } + initPrecompSeaIndex(precompSea); } String gs = props.getProperty("generate-sea", null); - boolean generateSea = gs != null || precompSea != null; if (gs != null) { - for(String o : gs.split(",")) { - if("no-mp".equals(o) || - "polygon".equals(o) || - "polygons".equals(o)) - generateSeaUsingMP = false; - else if("multipolygon".equals(o)) - generateSeaUsingMP = true; - else if(o.startsWith("land-tag=")) - landTag = o.substring(9).split("="); - else if (precompSea == null) { - // the other options are valid only if not using precompiled sea data - if(o.startsWith("close-gaps=")) - maxCoastlineGap = (int)Double.parseDouble(o.substring(11)); - else if("no-sea-sectors".equals(o)) - allowSeaSectors = false; - else if("extend-sea-sectors".equals(o)) { - allowSeaSectors = false; - extendSeaSectors = true; - } - else if("floodblocker".equals(o)) - floodblocker = true; - else if(o.startsWith("fbgap=")) - fbGap = (int)Double.parseDouble(o.substring("fbgap=".length())); - else if(o.startsWith("fbratio=")) - fbRatio = Double.parseDouble(o.substring("fbratio=".length())); - else if(o.startsWith("fbthres=")) - fbThreshold = (int)Double.parseDouble(o.substring("fbthres=".length())); - else if("fbdebug".equals(o)) - fbDebug = true; - else { - printOptionHelpMsg(precompSea != null, o); - } - } - else if(o.isEmpty()) - continue; - else { - printOptionHelpMsg(precompSea != null, o); - } - } + parseGenerateSeaOption(gs, precompSea != null); // init floodblocker and coastlinefile loader only // if precompSea is not set if (precompSea == null) { if (floodblocker) { - try { - fbRules = new StyleImpl(null, "floodblocker"); - } catch (FileNotFoundException e) { - log.error("Cannot load file floodblocker rules. Continue floodblocking disabled."); - floodblocker = false; - } + loadFloodblockerStyle(); } String coastlineFileOpt = props.getProperty("coastlinefile", null); if (coastlineFileOpt != null) { coastlineFilenames = coastlineFileOpt.split(","); - CoastlineFileLoader.getCoastlineLoader().setCoastlineFiles( - coastlineFilenames); + CoastlineFileLoader.getCoastlineLoader().setCoastlineFiles(coastlineFilenames); CoastlineFileLoader.getCoastlineLoader().loadCoastlines(); log.info("Coastlines loaded"); } else { @@ -260,18 +146,137 @@ } } - return generateSea; + return gs != null || precompSea != null; + } + + private void loadFloodblockerStyle() { + try { + fbRules = new StyleImpl(null, "floodblocker"); + } catch (FileNotFoundException e) { + log.error("Cannot load file floodblocker rules. Continue floodblocking disabled."); + floodblocker = false; + } + } + + private void parseGenerateSeaOption(String gs, boolean forPrecompSea) { + for (String option : gs.split(",")) { + if ("no-mp".equals(option) || "polygon".equals(option) || "polygons".equals(option)) + generateSeaUsingMP = false; + else if ("multipolygon".equals(option)) + generateSeaUsingMP = true; + else if (option.startsWith("land-tag=")) + landTag = option.substring(9).split("="); + else if (!forPrecompSea) { + // the other options are only valid if precompiled sea is not used + if (option.startsWith("close-gaps=")) + maxCoastlineGap = (int) Double.parseDouble(option.substring(11)); + else if ("no-sea-sectors".equals(option)) + allowSeaSectors = false; + else if ("extend-sea-sectors".equals(option)) { + allowSeaSectors = false; + extendSeaSectors = true; + } else if ("floodblocker".equals(option)) { + floodblocker = true; + } else if (option.startsWith("fbgap=")) { + fbGap = (int) Double.parseDouble(option.substring("fbgap=".length())); + } else if (option.startsWith("fbratio=")) { + fbRatio = Double.parseDouble(option.substring("fbratio=".length())); + } else if (option.startsWith("fbthres=")) { + fbThreshold = (int) Double.parseDouble(option.substring("fbthres=".length())); + } else if ("fbdebug".equals(option)) { + fbDebug = true; + } else { + printOptionHelpMsg(forPrecompSea, option); + } + } else if (option.isEmpty()) { + // nothing to do + } else { + printOptionHelpMsg(forPrecompSea, option); + } + } + } + + private static void initPrecompSeaIndex(String precompSea) { + if (precompIndex.get() != null) { + return; + } + /** + * The directory of the precompiled sea tiles or null if + * precompiled sea should not be used. + */ + File precompSeaDir = new File(precompSea); + if (!precompSeaDir.exists()) { + log.error("Directory or zip file with precompiled sea does not exist: " + precompSea); + return; + } + + String internalPath = null; + String indexFileName = "index.txt.gz"; + ZipFile zipFile = null; + PrecompData precompData = null; + try { + if (precompSeaDir.isDirectory()) { + File indexFile = new File(precompSeaDir, indexFileName); + if (!indexFile.exists()) { + // check if the unzipped index file exists + indexFileName = "index.txt"; + indexFile = new File(precompSeaDir, indexFileName); + } + if (indexFile.exists()) { + precompData = readIndexStream(indexFileName, new FileInputStream(indexFile)); + } + } else if (precompSea.endsWith(".zip")) { + zipFile = new ZipFile(precompSeaDir); // don't close here! + internalPath = "sea/"; + ZipEntry entry = zipFile.getEntry(internalPath + indexFileName); + if (entry == null) { + indexFileName = "index.txt"; + entry = zipFile.getEntry(internalPath + indexFileName); + } + if (entry == null) { + internalPath = ""; + indexFileName = "index.txt.gz"; + entry = zipFile.getEntry(internalPath + indexFileName); + } + if (entry != null) { + precompData = readIndexStream(indexFileName, zipFile.getInputStream(entry)); + } else { + log.error("Don't know how to read " + precompSeaDir); + } + } else { + log.error("Don't know how to read " + precompSeaDir); + } + if (precompData != null) { + precompData.dirFile = precompSeaDir; + if (zipFile != null) { + precompData.precompZipFileInternalPath = internalPath; + precompData.zipFile = zipFile; + } + precompIndex.set(precompData); + } + } catch (IOException exp) { + log.error("Cannot read index file", indexFileName, "in", precompSea, exp); + throw new ExitException("Failed to read required index file in " + precompSeaDir); + } + } + + private static PrecompData readIndexStream(String indexFileName, InputStream indexStream) throws IOException { + if (indexFileName.endsWith(".gz")) { + indexStream = new GZIPInputStream(indexStream); + } + PrecompData precompData = loadIndex(indexStream); + indexStream.close(); + return precompData; } /** * Show valid generate-sea options * @param forPrecompSea set to true if --precomp-sea is used - * @param o either "help" or an option that was not recognized - */ - void printOptionHelpMsg (boolean forPrecompSea, String o){ - - if(!"help".equals(o)) - System.err.println("Unknown sea generation option '" + o + "'"); + * @param option either "help" or an option that was not recognized + */ + private static void printOptionHelpMsg(boolean forPrecompSea, String option) { + if(!"help".equals(option)) + System.err.println("Unknown sea generation option '" + option + "'"); System.err.println("Known sea generation options " + (forPrecompSea ? "with" : "without") + " --precomp-sea are:"); System.err.println(" multipolygon use a multipolygon (default)"); @@ -286,23 +291,20 @@ System.err.println(" fbgap=NUM points closer to the coastline are ignored for flood blocking (default 40)"); System.err.println(" fbthres=NUM min points contained in a polygon to be flood blocked (default 20)"); System.err.println(" fbratio=NUM min ratio (points/area size) for flood blocking (default 0.5)"); - - } + } + /** * Read the index from stream and populate the index grid. * @param fileStream already opened stream */ - private PrecompData loadIndex(InputStream fileStream) throws IOException{ - int indexWidth = (SeaGenerator.getPrecompTileStart(MAX_LON) - SeaGenerator.getPrecompTileStart(MIN_LON)) / SeaGenerator.PRECOMP_RASTER; - int indexHeight = (SeaGenerator.getPrecompTileStart(MAX_LAT) - SeaGenerator.getPrecompTileStart(MIN_LAT)) / SeaGenerator.PRECOMP_RASTER; + private static PrecompData loadIndex(InputStream fileStream) throws IOException{ + int indexWidth = (getPrecompTileStart(MAX_LON) - getPrecompTileStart(MIN_LON)) / PRECOMP_RASTER; + int indexHeight = (getPrecompTileStart(MAX_LAT) - getPrecompTileStart(MIN_LAT)) / PRECOMP_RASTER; PrecompData pi = null; - LineNumberReader indexReader = new LineNumberReader( - new InputStreamReader(fileStream)); - Pattern csvSplitter = Pattern.compile(Pattern - .quote(";")); + LineNumberReader indexReader = new LineNumberReader(new InputStreamReader(fileStream)); String indexLine = null; - byte[][] indexGrid = new byte[indexWidth+1][indexHeight+1]; + byte[][] indexGrid = new byte[indexWidth + 1][indexHeight + 1]; boolean detectExt = true; String prefix = null; String ext = null; @@ -312,34 +314,29 @@ // comment continue; } - String[] items = csvSplitter.split(indexLine); + String[] items = SEMICOLON_SPLITTER.split(indexLine); if (items.length != 2) { - log.warn("Invalid format in index file name:", - indexLine); + log.warn("Invalid format in index file name:", indexLine); continue; } String precompKey = items[0]; byte type = updatePrecompSeaTileIndex(precompKey, items[1], indexGrid); - if (type == '?'){ - log.warn("Invalid format in index file name:", - indexLine); + if (type == '?') { + log.warn("Invalid format in index file name:", indexLine); continue; } - if (type == MIXED_TILE){ + if (type == MIXED_TILE) { // make sure that all file names are using the same name scheme int prePos = items[1].indexOf(items[0]); - if (prePos >= 0){ - if (detectExt){ + if (prePos >= 0) { + if (detectExt) { prefix = items[1].substring(0, prePos); - ext = items[1].substring(prePos+items[0].length()); + ext = items[1].substring(prePos + items[0].length()); detectExt = false; } else { - StringBuilder sb = new StringBuilder(prefix); - sb.append(precompKey); - sb.append(ext); - if (items[1].equals(sb.toString()) == false){ - log.warn("Unexpected file name in index file:", - indexLine); + String fname = prefix + precompKey + ext; + if (!items[1].equals(fname)) { + log.warn("Unexpected file name in index file:", indexLine); } } } @@ -354,7 +351,7 @@ return pi; } - /** + /** * Retrieves the start value of the precompiled tile. * @param value the value for which the start value is calculated * @return the tile start value @@ -386,18 +383,19 @@ } } + @Override public Set getUsedTags() { - HashSet usedTags = new HashSet(); + HashSet usedTags = new HashSet<>(); if (coastlineFilenames == null) { usedTags.add("natural"); } if (floodblocker) { usedTags.addAll(fbRules.getUsedTags()); } - + if (log.isDebugEnabled()) - log.debug("Sea generator used tags: "+usedTags); - + log.debug("Sea generator used tags: " + usedTags); + return usedTags; } @@ -406,44 +404,38 @@ * we save it. * @param way The way to test. */ + @Override public void onAddWay(Way way) { String natural = way.getTag("natural"); - if(natural != null) { - if("coastline".equals(natural)) { - if (precompSeaDir != null) - splitCoastLineToLineAndShape(way, natural); - else { - if (coastlineFilenames == null){ - way.deleteTag("natural"); - shoreline.add(way); - } - } - } else if (natural.contains(";")) { - // cope with compound tag value - String others = null; - boolean foundCoastline = false; - for(String n : natural.split(";")) { - if("coastline".equals(n.trim())) - foundCoastline = true; - else if(others == null) - others = n; - else - others += ";" + n; - } - - if(foundCoastline) { - if (precompSeaDir != null) - splitCoastLineToLineAndShape(way, natural); - else { - if (coastlineFilenames == null){ - way.deleteTag("natural"); - if(others != null) - way.addTag("natural", others); - shoreline.add(way); - } - } - } - } + if (natural == null) + return; + + // cope with compound tag value + StringBuilder others = null; + boolean foundCoastline = false; + for (String n : SEMICOLON_SPLITTER.split(natural)) { + if ("coastline".equals(n.trim())) + foundCoastline = true; + else if (others == null) + others = new StringBuilder(n); + else + others.append(';').append(n); + } + if (!foundCoastline) + return; + if (precompSea != null) + splitCoastLineToLineAndShape(way, natural); + else if (coastlineFilenames == null) { + // create copy of way that has only the natural=coastline tag + Way shore = new Way(way.getOriginalId(), way.getPoints()); + shore.setFakeId(); + shore.addTag("natural", "coastline"); + saver.addWay(shore); + + way.deleteTag("natural"); + if (others != null) + way.addTag("natural", others.toString()); + shoreline.add(way); } } @@ -456,7 +448,7 @@ * @param naturalVal the tag value */ private void splitCoastLineToLineAndShape(Way way, String naturalVal){ - if (precompSeaDir == null) + if (precompSea == null) return; if (way.hasIdenticalEndPoints()){ // add a copy of this way to be able to draw it as a shape @@ -480,7 +472,7 @@ * @return all ways of the tile * @throws FileNotFoundException if the tile could not be found */ - private Collection loadPrecompTile(InputStream is, String filename) { + private static Collection loadPrecompTile(InputStream is, String filename) { OsmPrecompSeaDataSource src = new OsmPrecompSeaDataSource(); EnhancedProperties props = new EnhancedProperties(); props.setProperty("style", "empty"); @@ -503,7 +495,7 @@ */ private List getPrecompKeyNames() { Area bounds = saver.getBoundingBox(); - List precompKeys = new ArrayList(); + List precompKeys = new ArrayList<>(); for (int lat = getPrecompTileStart(bounds.getMinLat()); lat < getPrecompTileEnd(bounds .getMaxLat()); lat += PRECOMP_RASTER) { for (int lon = getPrecompTileStart(bounds.getMinLong()); lon < getPrecompTileEnd(bounds @@ -519,11 +511,11 @@ * @param precompKey The key name is compiled of {@code lat+"_"+lon}. * @return either "land" or "sea" or a file name or null */ - private String getTileName(String precompKey){ + private static String getTileName(String precompKey){ PrecompData pi = precompIndex.get(); - String[] tileCoords = keySplitter.split(precompKey); - int lat = Integer.valueOf(tileCoords[0]); - int lon = Integer.valueOf(tileCoords[1]); + String[] tileCoords = KEY_SPLITTER.split(precompKey); + int lat = Integer.parseInt(tileCoords[0]); + int lon = Integer.parseInt(tileCoords[1]); int latIndex = (MAX_LAT-lat) / PRECOMP_RASTER; int lonIndex = (MAX_LON-lon) / PRECOMP_RASTER; byte type = pi.precompIndex[lonIndex][latIndex]; @@ -543,12 +535,12 @@ * @param indexGrid the previously allocated index grid * @return the byte that was saved in the index grid */ - private byte updatePrecompSeaTileIndex (String precompKey, String fileName, byte[][] indexGrid){ - String[] tileCoords = keySplitter.split(precompKey); + private static byte updatePrecompSeaTileIndex (String precompKey, String fileName, byte[][] indexGrid){ + String[] tileCoords = KEY_SPLITTER.split(precompKey); byte type = '?'; if (tileCoords.length == 2){ - int lat = Integer.valueOf(tileCoords[0]); - int lon = Integer.valueOf(tileCoords[1]); + int lat = Integer.parseInt(tileCoords[0]); + int lon = Integer.parseInt(tileCoords[1]); int latIndex = (MAX_LAT - lat) / PRECOMP_RASTER; int lonIndex = (MAX_LON - lon) / PRECOMP_RASTER; @@ -573,37 +565,62 @@ // flag if all tiles contains sea or way only // this is important for polygon processing - boolean distinctTilesOnly = true; + boolean distinctTilesOnly; - List landWays = new ArrayList(); - List seaWays = new ArrayList(); - List seaOnlyAreas = new ArrayList(); - List landOnlyAreas = new ArrayList(); + List landWays = new ArrayList<>(); + List seaWays = new ArrayList<>(); // get the index with assignment key => sea/land/tilename + distinctTilesOnly = loadLandAndSee(landWays, seaWays); + + if (generateSeaUsingMP || distinctTilesOnly) { + // when using multipolygons use the data directly from the precomp files + // also with polygons if all tiles are using either sea or land only + for (Way w : seaWays) { + w.setFullArea(SEA_SIZE); + saver.addWay(w); + } + } else { + // using polygons + // first add the complete bounding box as sea + saver.addWay(createSeaWay(saver.getBoundingBox(), false)); + } - ZipFile zipFile = null; + // check if the land tags need to be changed + boolean changeLadTag = landTag != null && ("natural".equals(landTag[0]) && !"land".equals(landTag[1])); + for (Way w : landWays) { + if (changeLadTag) { + w.deleteTag("natural"); + w.addTag(landTag[0], landTag[1]); + } + saver.addWay(w); + } + } + + + private boolean loadLandAndSee(List landWays, List seaWays) { + boolean distinctTilesOnly = true; + List seaOnlyAreas = new ArrayList<>(); + List landOnlyAreas = new ArrayList<>(); + PrecompData pd = precompIndex.get(); - if (precompSeaDir.getName().endsWith(".zip")){ - zipFile = pd.zipFile; - } for (String precompKey : getPrecompKeyNames()) { String tileName = getTileName(precompKey); - if (tileName == null ) { - log.error("Precompile sea tile "+precompKey+" is missing in the index. Skipping."); + if (tileName == null) { + log.error("Precompile sea tile " + precompKey + " is missing in the index. Skipping."); continue; } if ("sea".equals(tileName) || "land".equals(tileName)) { // the whole precompiled tile is filled with either land or sea - // => create a rectangle that covers the whole precompiled tile - String[] tileCoords = keySplitter.split(precompKey); - int minLat = Integer.valueOf(tileCoords[0]); - int minLon = Integer.valueOf(tileCoords[1]); - Rectangle r = new Rectangle(minLon,minLat,PRECOMP_RASTER,PRECOMP_RASTER); - + // => create a rectangle that covers the whole precompiled tile + String[] tileCoords = KEY_SPLITTER.split(precompKey); + int minLat = Integer.parseInt(tileCoords[0]); + int minLon = Integer.parseInt(tileCoords[1]); + Rectangle r = new Rectangle(minLon, minLat, PRECOMP_RASTER, PRECOMP_RASTER); + if ("sea".equals(tileName)) { seaOnlyAreas = addWithoutCreatingHoles(seaOnlyAreas, new java.awt.geom.Area(r)); } else { @@ -611,84 +628,53 @@ } } else { distinctTilesOnly = false; - try { - InputStream is = null; - if (zipFile != null){ - ZipEntry entry = zipFile.getEntry(pd.precompZipFileInternalPath + tileName); - if (entry != null){ - is = zipFile.getInputStream(entry); - } else { - log.error("Preompiled sea tile " + tileName + " not found."); - } - } else { - File precompTile = new File(precompSeaDir,tileName); - is = new FileInputStream(precompTile); - } - if (is != null){ - Collection seaPrecompWays = loadPrecompTile(is, tileName); - if (log.isDebugEnabled()) - log.debug(seaPrecompWays.size(), "precomp sea ways from", - tileName, "loaded."); - - for (Way w : seaPrecompWays) { - // set a new id to be sure that the precompiled ids do not - // interfere with the ids of this run - w.setFakeId(); - - if ("land".equals(w.getTag("natural"))) { - landWays.add(w); - } else { - seaWays.add(w); - } - } - } - } catch (FileNotFoundException exp) { - log.error("Preompiled sea tile " + tileName + " not found."); - } catch (Exception exp) { - log.error(exp); - exp.printStackTrace(); - } + loadMixedTile(pd, tileName, landWays, seaWays); } } landWays.addAll(areaToWays(landOnlyAreas,"land")); seaWays.addAll(areaToWays(seaOnlyAreas,"sea")); - landOnlyAreas = null; - seaOnlyAreas = null; - - // check if the land tags need to be changed - if (landTag != null && ("natural".equals(landTag[0]) && "land".equals(landTag[1])) == false) { - for (Way w : landWays) { - w.deleteTag("natural"); - w.addTag(landTag[0], landTag[1]); - } - } - - if (generateSeaUsingMP || distinctTilesOnly) { - // when using multipolygons use the data directly from the precomp files - // also with polygons if all tiles are using either sea or land only - for (Way w : landWays) { - saver.addWay(w); - } - for (Way w : seaWays) { - w.setFullArea(SEA_SIZE); - saver.addWay(w); - } - } else { - // using polygons - - Area bounds = saver.getBoundingBox(); - // first add the complete bounding box as sea - Way sea = new Way(FakeIdGenerator.makeFakeId(),bounds.toCoords()); - sea.addTag("natural", "sea"); - sea.setFullArea(SEA_SIZE); - - for (Way w : landWays) { - saver.addWay(w); - } - } - } - - + return distinctTilesOnly; + } + + private static void loadMixedTile(PrecompData pd, String tileName, List landWays, List seaWays) { + try { + InputStream is = null; + if (pd.zipFile != null) { + ZipEntry entry = pd.zipFile.getEntry(pd.precompZipFileInternalPath + tileName); + if (entry != null) { + is = pd.zipFile.getInputStream(entry); + } else { + log.error("Preompiled sea tile " + tileName + " not found."); + } + } else { + File precompTile = new File(pd.dirFile, tileName); + is = new FileInputStream(precompTile); + } + if (is != null) { + Collection seaPrecompWays = loadPrecompTile(is, tileName); + if (log.isDebugEnabled()) + log.debug(seaPrecompWays.size(), "precomp sea ways from", tileName, "loaded."); + + for (Way w : seaPrecompWays) { + // set a new id to be sure that the precompiled ids do not + // interfere with the ids of this run + w.setFakeId(); + + if ("land".equals(w.getTag("natural"))) { + landWays.add(w); + } else { + seaWays.add(w); + } + } + } + } catch (FileNotFoundException exp) { + log.error("Preompiled sea tile " + tileName + " not found."); + } catch (Exception exp) { + log.error(exp); + exp.printStackTrace(); + } + } + /** * Try to merge an area with one or more other areas without creating holes. * If it cannot be merged, it is added to the list. @@ -696,27 +682,27 @@ * @param toAdd area to add * @return new list of areas */ - private List addWithoutCreatingHoles(List areas, + private static List addWithoutCreatingHoles(List areas, final java.awt.geom.Area toAdd) { - List result = new LinkedList(); + List result = new LinkedList<>(); java.awt.geom.Area toMerge = new java.awt.geom.Area(toAdd); - for (java.awt.geom.Area area:areas ){ + for (java.awt.geom.Area area : areas) { java.awt.geom.Area mergedArea = new java.awt.geom.Area(area); mergedArea.add(toMerge); - if (mergedArea.isSingular() == false){ + if (!mergedArea.isSingular()) { result.add(area); continue; } toMerge = mergedArea; } // create a sorted list with "smallest" area at the beginning - int dimNew = Math.max(toMerge.getBounds().width,toMerge.getBounds().height); + int dimNew = Math.max(toMerge.getBounds().width, toMerge.getBounds().height); boolean added = false; - for (int i = 0; i < result.size(); i++){ + for (int i = 0; i < result.size(); i++) { java.awt.geom.Area area = result.get(i); - if (dimNew < Math.max(area.getBounds().width,area.getBounds().height)){ - result.add(i,toMerge); + if (dimNew < Math.max(area.getBounds().width, area.getBounds().height)) { + result.add(i, toMerge); added = true; break; } @@ -731,13 +717,11 @@ * @param type * @return */ - private List areaToWays(List areas, String type) { - List ways = new ArrayList(); -// int count = 0; + private static List areaToWays(List areas, String type) { + List ways = new ArrayList<>(); for (java.awt.geom.Area area : areas) { List> shapes = Java2DConverter.areaToShapes(area); for (List points : shapes) { -// uk.me.parabola.util.GpxCreator.createGpx(type + "_" + count++, points); Way w = new Way(FakeIdGenerator.makeFakeId(), points); w.addTag("natural", type); ways.add(w); @@ -751,154 +735,123 @@ * @param segments a list of closed and unclosed ways * @return a list of ways completely joined */ - public static ArrayList joinWays(Collection segments) { - ArrayList joined = new ArrayList((int)Math.ceil(segments.size()*0.5)); - Map beginMap = new IdentityHashMap(); + public static List joinWays(Collection segments) { + ArrayList joined = new ArrayList<>((int) Math.ceil(segments.size() * 0.5)); + Map beginMap = new IdentityHashMap<>(); for (Way w : segments) { if (w.hasIdenticalEndPoints()) { joined.add(w); - } else if (w.getPoints() != null && w.getPoints().size() > 1){ + } else if (w.getPoints().size() > 1){ beginMap.put(w.getFirstPoint(), w); } else { - log.info("Discard coastline way",w.getId(),"because consists of less than 2 points"); + log.info("Discarding coastline way", w.getId(), "because it consists of less than 2 points"); } } segments.clear(); - - int merged = 1; - while (merged > 0) { - merged = 0; + + boolean merged; + do { + merged = false; for (Way w1 : beginMap.values()) { - if (w1.hasIdenticalEndPoints()) { - // this should not happen - log.error("joinWays2: Way "+w1+" is closed but contained in the begin map"); - joined.add(w1); - beginMap.remove(w1.getPoints().get(0)); - merged=1; + Way w2 = beginMap.get(w1.getLastPoint()); + if (w2 != null) { + merge(beginMap, joined, w1, w2); + merged = true; break; } - - List points1 = w1.getPoints(); - Way w2 = beginMap.get(points1.get(points1.size() - 1)); - if (w2 != null) { - log.info("merging: ", beginMap.size(), w1.getId(), - w2.getId()); - List points2 = w2.getPoints(); - Way wm; - if (FakeIdGenerator.isFakeId(w1.getId())) { - wm = w1; - } else { - wm = new Way(w1.getOriginalId()); - wm.setFakeId(); - wm.getPoints().addAll(points1); - beginMap.put(points1.get(0), wm); - } - wm.getPoints().addAll(points2.subList(1, points2.size())); - beginMap.remove(points2.get(0)); - merged++; - - if (wm.hasIdenticalEndPoints()) { - joined.add(wm); - beginMap.remove(wm.getFirstPoint()); - } - break; - } - } - } - log.info(joined.size(),"closed ways.",beginMap.size(),"unclosed ways."); + } + } while (merged); + + log.info(joined.size(), "closed ways.", beginMap.size(), "unclosed ways."); joined.addAll(beginMap.values()); return joined; } + // merge the ways and maintain maps and list + private static void merge(Map beginMap, List joined, Way w1, Way w2) { + log.info("merging: ", beginMap.size(), w1.getId(), w2.getId()); + Way wm; + if (FakeIdGenerator.isFakeId(w1.getId())) { + wm = w1; + } else { + wm = new Way(w1.getOriginalId(), w1.getPoints()); + wm.setFakeId(); + beginMap.put(wm.getFirstPoint(), wm); + } + beginMap.remove(w2.getFirstPoint()); + wm.getPoints().addAll(w2.getPoints().subList(1, w2.getPoints().size())); + + if (wm.hasIdenticalEndPoints()) { + joined.add(wm); + beginMap.remove(wm.getFirstPoint()); + } + } + /** * All done, process the saved shoreline information and construct the polygons. */ + @Override public void end() { // precompiled sea has highest priority // if it is set do not perform any other algorithm - if (precompSeaDir != null) { + if (precompSea != null && precompIndex.get() != null) { addPrecompSea(); return; } - final Area seaBounds = saver.getBoundingBox(); + final Area tileBounds = saver.getBoundingBox(); if (coastlineFilenames == null) { log.info("Shorelines before join", shoreline.size()); shoreline = joinWays(shoreline); } else { - shoreline.addAll(CoastlineFileLoader.getCoastlineLoader() - .getCoastlines(seaBounds)); + shoreline.addAll(CoastlineFileLoader.getCoastlineLoader().getCoastlines(tileBounds)); log.info("Shorelines from extra file:", shoreline.size()); } - int closedS = 0; - int unclosedS = 0; - for (Way w : shoreline) { - if (w.hasIdenticalEndPoints()) { - closedS++; - } else { - unclosedS++; - } - } - log.info("Closed shorelines", closedS); - log.info("Unclosed shorelines", unclosedS); - + if (log.isInfoEnabled()) { + long closed = shoreline.stream().filter(Way::hasIdenticalEndPoints).count(); + log.info("Closed shorelines", closed); + log.info("Unclosed shorelines", shoreline.size() - closed); + } + // clip all shoreline segments - clipShorlineSegments(shoreline, seaBounds); - - log.info("generating sea, seaBounds=", seaBounds); - int minLat = seaBounds.getMinLat(); - int maxLat = seaBounds.getMaxLat(); - int minLong = seaBounds.getMinLong(); - int maxLong = seaBounds.getMaxLong(); - Coord nw = new Coord(minLat, minLong); - Coord ne = new Coord(minLat, maxLong); - Coord sw = new Coord(maxLat, minLong); - Coord se = new Coord(maxLat, maxLong); + clipShorlineSegments(shoreline, tileBounds); if(shoreline.isEmpty()) { - // no sea required - // even though there is no sea, generate a land + // No sea required + // Even though there is no sea, generate a land // polygon so that the tile's background colour will // match the land colour on the tiles that do contain // some sea - long landId = FakeIdGenerator.makeFakeId(); - Way land = new Way(landId, seaBounds.toCoords()); - land.addTag(landTag[0], landTag[1]); - // no matter if the multipolygon option is used it is + // No matter if the multipolygon option is used it is // only necessary to create a land polygon - saver.addWay(land); + saver.addWay(createLandWay(tileBounds)); // nothing more to do return; } long multiId = FakeIdGenerator.makeFakeId(); Relation seaRelation = null; - if(generateSeaUsingMP) { - log.debug("Generate seabounds relation",multiId); + if (generateSeaUsingMP) { + log.debug("Generate seabounds relation", multiId); seaRelation = new GeneralRelation(multiId); seaRelation.addTag("type", "multipolygon"); seaRelation.addTag("natural", "sea"); } - - List islands = new ArrayList(); + + + List islands = new ArrayList<>(); // handle islands (closed shoreline components) first (they're easy) - handleIslands(shoreline, seaBounds, islands); + handleIslands(shoreline, tileBounds, islands); // the remaining shoreline segments should intersect the boundary // find the intersection points and store them in a SortedMap - NavigableMap hitMap = findIntesectionPoints(shoreline, seaBounds, seaRelation); + NavigableMap hitMap = findIntesectionPoints(shoreline, tileBounds, seaRelation); // now construct inner ways from these segments - boolean shorelineReachesBoundary = createInnerWays(seaBounds, islands, hitMap); - - if(!shorelineReachesBoundary && roadsReachBoundary) { - // try to avoid tiles being flooded by anti-lakes or other - // bogus uses of natural=coastline - generateSeaBackground = false; - } + createLandPolygons(tileBounds, islands, hitMap); List antiIslands = removeAntiIslands(seaRelation, islands); if (islands.isEmpty()) { @@ -912,53 +865,13 @@ if (generateSeaBackground) { // the background is sea so all anti-islands should be // contained by land otherwise they won't be visible - - for (Way ai : antiIslands) { - boolean containedByLand = false; - for(Way i : islands) { - if(i.containsPointsOf(ai)) { - containedByLand = true; - break; - } - } - - if (!containedByLand) { - // found an anti-island that is not contained by - // land so convert it back into an island - ai.deleteTag("natural"); - ai.addTag(landTag[0], landTag[1]); - if (generateSeaUsingMP) { - // create a "inner" way for the island - assert seaRelation != null; - seaRelation.addElement("inner", ai); - } - log.warn("Converting anti-island starting at", ai.getFirstPoint().toOSMURL() , "into an island as it is surrounded by water"); - } - } - - long seaId = FakeIdGenerator.makeFakeId(); - Way sea = new Way(seaId); - // the sea background area must be a little bigger than all - // inner land areas. this is a workaround for a mp shortcoming: - // mp is not able to combine outer and inner if they intersect - // or have overlaying lines - // the added area will be clipped later by the style generator - sea.addPoint(new Coord(nw.getLatitude() - 1, - nw.getLongitude() - 1)); - sea.addPoint(new Coord(sw.getLatitude() + 1, - sw.getLongitude() - 1)); - sea.addPoint(new Coord(se.getLatitude() + 1, - se.getLongitude() + 1)); - sea.addPoint(new Coord(ne.getLatitude() - 1, - ne.getLongitude() + 1)); - sea.addPoint(sea.getFirstPoint()); // close shape - sea.addTag("natural", "sea"); - sea.setFullArea(SEA_SIZE); + verifyIslands(islands, antiIslands, seaRelation); + + Way sea = createSeaWay(tileBounds, true); log.info("sea: ", sea); saver.addWay(sea); - if(generateSeaUsingMP) { - assert seaRelation != null; + if(seaRelation != null) { seaRelation.addElement("outer", sea); } } else { @@ -967,11 +880,9 @@ // generate a land polygon so that the tile's // background colour will match the land colour on the // tiles that do contain some sea - long landId = FakeIdGenerator.makeFakeId(); - Way land = new Way(landId, seaBounds.toCoords()); - land.addTag(landTag[0], landTag[1]); + Way land = createLandWay(tileBounds); saver.addWay(land); - if (generateSeaUsingMP) { + if(seaRelation != null) { seaRelation.addElement("inner", land); } } @@ -993,14 +904,70 @@ shoreline = null; } + private void verifyIslands(List islands, List antiIslands, Relation seaRelation) { + for (Way ai : antiIslands) { + boolean containedByLand = false; + for (Way i : islands) { + if (i.containsPointsOf(ai)) { + containedByLand = true; + break; + } + } + + if (!containedByLand) { + // found an anti-island that is not contained by + // land so convert it back into an island + ai.deleteTag("natural"); + ai.addTag(landTag[0], landTag[1]); + if (seaRelation != null) { + // create a "inner" way for the island + seaRelation.addElement("inner", ai); + } + log.warn("Converting anti-island starting at", ai.getFirstPoint().toOSMURL(), + "into an island as it is surrounded by water"); + } + } + } + + private Way createLandWay(Area tileBounds) { + long landId = FakeIdGenerator.makeFakeId(); + Way land = new Way(landId, tileBounds.toCoords()); + land.addTag(landTag[0], landTag[1]); + return land; + } + + /** + * Create a sea polygon from the given tile bounds + * @param tileBounds + * @param enlarge if true, make sure that the polygon is slightly larger than the tile bounds + * @return the created way + */ + private static Way createSeaWay(Area tileBounds, boolean enlarge) { + log.info("generating sea, seaBounds=", tileBounds); + Area bbox = tileBounds; + long seaId = FakeIdGenerator.makeFakeId(); + if (enlarge) { + // the sea background area must be a little bigger than all + // inner land areas. this is a workaround for a multipolygon shortcoming: + // mp is not able to combine outer and inner if they intersect + // or have overlaying lines + // the added area will be clipped later + bbox = new Area(bbox.getMinLat() - 1, bbox.getMinLong() - 1, bbox.getMaxLat() + 1, bbox.getMaxLong() + 1); + } + Way sea = new Way(seaId, bbox.toCoords()); + sea.addTag("natural", "sea"); + sea.setFullArea(SEA_SIZE); + return sea; + } + /** * Clip the shoreline ways to the bounding box of the map. * @param shoreline All the the ways making up the coast. * @param bounds The map bounds. */ - private void clipShorlineSegments(List shoreline, Area bounds) { - List toBeRemoved = new ArrayList(); - List toBeAdded = new ArrayList(); + private static void clipShorlineSegments(List shoreline, Area bounds) { + List toBeRemoved = new ArrayList<>(); + List toBeAdded = new ArrayList<>(); for (Way segment : shoreline) { List points = segment.getPoints(); List> clipped = LineClipper.clip(bounds, points); @@ -1025,10 +992,10 @@ * shore line list and added to the island list. * * @param shoreline The collected shore line ways. - * @param seaBounds The map boundary. + * @param tileBounds The map boundary. * @param islands The islands are saved to this list. */ - private void handleIslands(List shoreline, Area seaBounds, List islands) { + private void handleIslands(List shoreline, Area tileBounds, List islands) { Iterator it = shoreline.iterator(); while (it.hasNext()) { Way w = it.next(); @@ -1039,7 +1006,7 @@ } } - closeGaps(shoreline, seaBounds); + closeGaps(shoreline, tileBounds); // there may be more islands now it = shoreline.iterator(); while (it.hasNext()) { @@ -1052,69 +1019,137 @@ } } - private boolean createInnerWays(Area seaBounds, List islands, NavigableMap hitMap) { - NavigableSet hits = hitMap.navigableKeySet(); - boolean shorelineReachesBoundary = false; + private void closeGaps(List shoreline, Area bounds) { + if (maxCoastlineGap <= 0) + return; + + // join up coastline segments whose end points are less than + // maxCoastlineGap metres apart + boolean changed; + do { + changed = false; + Iterator iter = shoreline.iterator(); + while (!changed && iter.hasNext()) { + Way w1 = iter.next(); + if (w1.hasIdenticalEndPoints()) + continue; + Coord w1e = w1.getLastPoint(); + if (!bounds.onBoundary(w1e)) { + Way closed = tryCloseGap(shoreline, w1, bounds); + if (closed != null) { + saver.addWay(closed); + changed = true; + } + } + } + } while (changed); + } + + private Way tryCloseGap(List shoreline, Way w1, Area bounds) { + Coord w1e = w1.getLastPoint(); + Way nearest = null; + double smallestGap = Double.MAX_VALUE; + for (Way w2 : shoreline) { + if (w1 == w2 || w2.hasIdenticalEndPoints()) + continue; + Coord w2s = w2.getFirstPoint(); + if (!bounds.onBoundary(w2s)) { + double gap = w1e.distance(w2s); + if (gap < smallestGap) { + nearest = w2; + smallestGap = gap; + } + } + } + if (nearest != null && smallestGap < maxCoastlineGap) { + Coord w2s = nearest.getFirstPoint(); + log.warn("Bridging " + (int) smallestGap + "m gap in coastline from " + w1e.toOSMURL() + " to " + + w2s.toOSMURL()); + Way wm; + if (FakeIdGenerator.isFakeId(w1.getId())) { + wm = w1; + } else { + wm = new Way(w1.getOriginalId()); + wm.setFakeId(); + shoreline.remove(w1); + shoreline.add(wm); + wm.getPoints().addAll(w1.getPoints()); + wm.copyTags(w1); + } + wm.getPoints().addAll(nearest.getPoints()); + shoreline.remove(nearest); + // make a line that shows the filled gap + Way w = new Way(FakeIdGenerator.makeFakeId()); + w.addTag("natural", "mkgmap:coastline-gap"); + w.addPoint(w1e); + w.addPoint(w2s); + return w; + } + return null; + } + + /** + * Add lines to ways that touch or cross the sea bounds so that the way is closed along the edges of the bounds. + * Adds complete edges or parts of them. This is done counter-clockwise. + * @param tileBounds the bounds + * @param islands list of land masses to which the closed ways are added + * @param hitMap A map of the 'hits' where the shore line intersects the boundary. + */ + private void createLandPolygons(Area tileBounds, List islands, NavigableMap hitMap) { + NavigableSet hits = hitMap.navigableKeySet(); while (!hits.isEmpty()) { - long id = FakeIdGenerator.makeFakeId(); - Way w = new Way(id); + Way w = new Way(FakeIdGenerator.makeFakeId()); saver.addWay(w); - EdgeHit hit = hits.first(); - EdgeHit hFirst = hit; + Double hit = hits.first(); + Double hFirst = hit; do { Way segment = hitMap.get(hit); log.info("current hit:", hit); - EdgeHit hNext; + Double hNext; if (segment != null) { // add the segment and get the "ending hit" log.info("adding:", segment); - for(Coord p : segment.getPoints()) - w.addPointIfNotEqualToLastPoint(p); - hNext = getEdgeHit(seaBounds, segment.getLastPoint()); + segment.getPoints().forEach(w::addPointIfNotEqualToLastPoint); + + hNext = getEdgeHit(tileBounds, segment.getLastPoint()); } else { - w.addPointIfNotEqualToLastPoint(hit.getPoint(seaBounds)); + w.addPointIfNotEqualToLastPoint(getPoint(tileBounds, hit)); hNext = hits.higher(hit); if (hNext == null) hNext = hFirst; - - Coord p; - if (hit.compareTo(hNext) < 0) { - log.info("joining: ", hit, hNext); - for (int i=hit.edge; i 0) { - log.info("joining: ", hit, hNext); - for (int i=hit.edge; i<4; i++) { - EdgeHit corner = new EdgeHit(i, 1.0); - p = corner.getPoint(seaBounds); - log.debug("way: ", corner, p); - w.addPointIfNotEqualToLastPoint(p); - } - for (int i=0; i removeAntiIslands(Relation seaRelation, List islands) { - List antiIslands = new ArrayList(); - for (Way w : islands) { - + List antiIslands = new ArrayList<>(); + Iterator iter = islands.iterator(); + while (iter.hasNext()) { + Way w = iter.next(); if (!FakeIdGenerator.isFakeId(w.getId())) { - Way w1 = new Way(w.getOriginalId()); - w1.setFakeId(); - w1.getPoints().addAll(w.getPoints()); - // only copy the name tags - for (Entry tagEntry : w.getTagEntryIterator()){ - if(tagEntry.getKey().equals("name") || tagEntry.getKey().contains("name")) - w1.addTag(tagEntry.getKey(), tagEntry.getValue()); - } - w = w1; + w = copyWithNameTags(w); } // determine where the water is @@ -1148,20 +1176,35 @@ // make it visible above the land) w.addTag("natural", "water"); antiIslands.add(w); + iter.remove(); saver.addWay(w); } else { // water on the outside of the poly, it's an island w.addTag(landTag[0], landTag[1]); saver.addWay(w); - if(generateSeaUsingMP) { + if (seaRelation != null) { // create a "inner" way for each island seaRelation.addElement("inner", w); - } - } - } - - islands.removeAll(antiIslands); + } + } + } return antiIslands; + } + + /** + * Create copy of way, but ignore tags that don't contain "name" in the key + * @param w + * @return + */ + private static Way copyWithNameTags(Way w) { + Way w1 = new Way(w.getOriginalId(), w.getPoints()); + w1.setFakeId(); + for (Entry tagEntry : w.getTagEntryIterator()) { + if ("name".equals(tagEntry.getKey()) || tagEntry.getKey().contains("name")) { + w1.addTag(tagEntry.getKey(), tagEntry.getValue()); + } + } + return w1; } /** @@ -1169,23 +1212,27 @@ * map boundary. * * @param shoreline The remaining shore line segments. - * @param seaBounds The map boundary. + * @param tileBounds The map boundary. * @param seaRelation If we are using a multi-polygon, this is it. Otherwise it will be null. * @return A map of the 'hits' where the shore line intersects the boundary. */ - private NavigableMap findIntesectionPoints(List shoreline, Area seaBounds, Relation seaRelation) { - assert !generateSeaUsingMP || seaRelation != null; - - NavigableMap hitMap = new TreeMap(); + private NavigableMap findIntesectionPoints(List shoreline, Area tileBounds, Relation seaRelation) { + if (generateSeaUsingMP && seaRelation == null) + throw new MapFailedException("seaRelation is null"); + + NavigableMap hitMap = new TreeMap<>(); for (Way w : shoreline) { - List points = w.getPoints(); - Coord pStart = points.get(0); - Coord pEnd = points.get(points.size()-1); - - EdgeHit hStart = getEdgeHit(seaBounds, pStart); - EdgeHit hEnd = getEdgeHit(seaBounds, pEnd); - if (hStart == null || hEnd == null) { - + Coord pStart = w.getFirstPoint(); + Coord pEnd = w.getLastPoint(); + + Double hStart = getEdgeHit(tileBounds, pStart); + Double hEnd = getEdgeHit(tileBounds, pEnd); + if (hStart != null && hEnd != null) { + // nice case: both ends touch the boundary + log.debug("hits: ", hStart, hEnd); + hitMap.put(hStart, w); + hitMap.put(hEnd, null); + } else { /* * This problem occurs usually when the shoreline is cut by osmosis (e.g. country-extracts from geofabrik) * There are two possibilities to solve this problem: @@ -1197,43 +1244,29 @@ * * Usually, the first choice is appropriate if the segment is "nearly" closed. */ - double length = 0; - Coord p0 = pStart; - for (Coord p1 : points.subList(1, points.size()-1)) { - length += p0.distance(p1); - p0 = p1; - } - boolean nearlyClosed = pStart.distance(pEnd) < 0.1 * length; + List points = w.getPoints(); + boolean nearlyClosed = pStart.distance(pEnd) < 0.1 * w.calcLengthInMetres(); if (nearlyClosed) { // close the way - points.add(pStart); + points.add(pStart); // XXX original way is modified, is that correct? - if(!FakeIdGenerator.isFakeId(w.getId())) { - Way w1 = new Way(w.getOriginalId()); - w1.setFakeId(); - w1.getPoints().addAll(w.getPoints()); - // only copy the name tags - for (Entry tagEntry : w.getTagEntryIterator()){ - if(tagEntry.getKey().equals("name") || tagEntry.getKey().contains("name")) - w1.addTag(tagEntry.getKey(), tagEntry.getValue()); - } - w = w1; + if (!FakeIdGenerator.isFakeId(w.getId())) { + w = copyWithNameTags(w); } w.addTag(landTag[0], landTag[1]); saver.addWay(w); - if(generateSeaUsingMP) - { + if (generateSeaUsingMP) { seaRelation.addElement("inner", w); } } else if(allowSeaSectors) { Way sea; - if (seaRelation != null) { + if (generateSeaUsingMP) { sea = new Way(seaRelation.getOriginalId()); sea.setFakeId(); + } else { + sea = new Way(FakeIdGenerator.makeFakeId()); } - else - sea = new Way(FakeIdGenerator.makeFakeId()); sea.getPoints().addAll(points); sea.addPoint(new Coord(pEnd.getLatitude(), pStart.getLongitude())); sea.addPoint(pStart); @@ -1246,12 +1279,12 @@ } else if (extendSeaSectors) { // create additional points at next border to prevent triangles from point 2 if (null == hStart) { - hStart = getNextEdgeHit(seaBounds, pStart); - w.getPoints().add(0, hStart.getPoint(seaBounds)); + hStart = getNextEdgeHit(tileBounds, pStart); + w.getPoints().add(0, getPoint(tileBounds, hStart)); } if (null == hEnd) { - hEnd = getNextEdgeHit(seaBounds, pEnd); - w.getPoints().add(hEnd.getPoint(seaBounds)); + hEnd = getNextEdgeHit(tileBounds, pEnd); + w.getPoints().add(getPoint(tileBounds, hEnd)); } log.debug("hits (second try): ", hStart, hEnd); hitMap.put(hStart, w); @@ -1260,215 +1293,140 @@ // show the coastline even though we can't produce // a polygon for the land w.addTag("natural", "coastline"); - if (w.hasIdenticalEndPoints() == false){ + if (!w.hasIdenticalEndPoints()) { log.error("adding sea shape that is not really closed"); } saver.addWay(w); } - } else { - log.debug("hits: ", hStart, hEnd); - hitMap.put(hStart, w); - hitMap.put(hEnd, null); } } return hitMap; } - /** - * Specifies where an edge of the bounding box is hit. - */ - private static class EdgeHit implements Comparable - { - private final int edge; - private final double t; - - EdgeHit(int edge, double t) { - this.edge = edge; - this.t = t; - } - - public int compareTo(EdgeHit o) { - if (edge < o.edge) - return -1; - else if (edge > o.edge) - return +1; - else if (t > o.t) - return +1; - else if (t < o.t) - return -1; - else - return 0; - } - - public boolean equals(Object o) { - if (o instanceof EdgeHit) { - EdgeHit h = (EdgeHit) o; - return (h.edge == edge && Double.compare(h.t, t) == 0); - } else - return false; - } - - private Coord getPoint(Area a) { - log.info("getPoint: ", this, a); - switch (edge) { - case 0: - return new Coord(a.getMinLat(), (int) (a.getMinLong() + t * (a.getMaxLong()-a.getMinLong()))); - - case 1: - return new Coord((int)(a.getMinLat() + t * (a.getMaxLat()-a.getMinLat())), a.getMaxLong()); - - case 2: - return new Coord(a.getMaxLat(), (int)(a.getMaxLong() - t * (a.getMaxLong()-a.getMinLong()))); - - case 3: - return new Coord((int)(a.getMaxLat() - t * (a.getMaxLat()-a.getMinLat())), a.getMinLong()); - - default: - throw new MapFailedException("illegal state"); - } - } - - public String toString() { - return "EdgeHit " + edge + "@" + t; - } - } - - private EdgeHit getEdgeHit(Area a, Coord p) { - return getEdgeHit(a, p, 10); - } - - private EdgeHit getEdgeHit(Area a, Coord p, int tolerance) { - int lat = p.getLatitude(); - int lon = p.getLongitude(); - int minLat = a.getMinLat(); - int maxLat = a.getMaxLat(); - int minLong = a.getMinLong(); - int maxLong = a.getMaxLong(); - - log.info(String.format("getEdgeHit: (%d %d) (%d %d %d %d)", lat, lon, minLat, minLong, maxLat, maxLong)); - if (lat <= minLat+tolerance) { - return new EdgeHit(0, ((double)(lon - minLong))/(maxLong-minLong)); - } else if (lon >= maxLong-tolerance) { - return new EdgeHit(1, ((double)(lat - minLat))/(maxLat-minLat)); - } else if (lat >= maxLat-tolerance) { - return new EdgeHit(2, ((double)(maxLong - lon))/(maxLong-minLong)); - } else if (lon <= minLong+tolerance) { - return new EdgeHit(3, ((double)(maxLat - lat))/(maxLat-minLat)); - } else - return null; + // create the point where the shoreline hits the sea bounds + private static Coord getPoint(Area a, double edgePos) { + log.info("getPoint: ", a, edgePos); + int aMinLongHP = a.getMinLong() << Coord.DELTA_SHIFT; + int aMaxLongHP = a.getMaxLong() << Coord.DELTA_SHIFT; + int aMinLatHP = a.getMinLat() << Coord.DELTA_SHIFT; + int aMaxLatHP = a.getMaxLat() << Coord.DELTA_SHIFT; + int platHp; + int plonHp; + int edge = (int) edgePos; + double t = edgePos - edge; + if (edge >= 4) + edge -= 4; + switch (edge) { + case 0: // southern + platHp = aMinLatHP; + plonHp = (int) Math.round(aMinLongHP + t * (aMaxLongHP - aMinLongHP)); + break; + case 1: // eastern + platHp = (int) Math.round(aMinLatHP + t * (aMaxLatHP - aMinLatHP)); + plonHp = aMaxLongHP; + break; + case 2: // northern + platHp = aMaxLatHP; + plonHp = (int) Math.round(aMaxLongHP - t * (aMaxLongHP - aMinLongHP)); + break; + case 3: // western + platHp = (int) Math.round(aMaxLatHP - t * (aMaxLatHP - aMinLatHP)); + plonHp = aMinLongHP; + break; + default: + throw new MapFailedException("illegal state"); + } + return Coord.makeHighPrecCoord(platHp, plonHp); + } + + /** + * Calculate a Double that represents the position where the given point touches + * the boundary. + * + * @param bounds the boundary + * @param p the point + * @return null if the point is not touching the boundary, else a value + * between 0.0 (inclusive) and 4.0 (exclusive), where 0 means the lower + * left corner, 0.5 means the middle of the bottom edge, 1.5 the + * middle of the right edge, 4 would be the lower left corner again + */ + private static Double getEdgeHit(Area bounds, Coord p) { + Double hit = getEdgeHit(bounds, p, 10); // 10 points in garmin units + if (hit != null && hit >= 4) + hit = 0.0; + return hit; + } + + private static Double getEdgeHit(Area a, Coord p, int tolerance24) { + final int toleranceHp = tolerance24 << Coord.DELTA_SHIFT; + final int latHp = p.getHighPrecLat(); + final int lonHp = p.getHighPrecLon(); + final int minLatHp = a.getMinLat() << Coord.DELTA_SHIFT; + final int maxLatHp = a.getMaxLat() << Coord.DELTA_SHIFT; + final int minLongHp = a.getMinLong() << Coord.DELTA_SHIFT; + final int maxLongHp = a.getMaxLong() << Coord.DELTA_SHIFT; + + log.info(String.format("getEdgeHit: (%d %d) (%d %d %d %d)", latHp, lonHp, minLatHp, minLongHp, maxLatHp, maxLongHp)); + if (latHp <= minLatHp + toleranceHp) { + return (double) (lonHp - minLongHp) / (maxLongHp - minLongHp); + } else if (lonHp >= maxLongHp - toleranceHp) { + return 1 + ((double) (latHp - minLatHp) / (maxLatHp - minLatHp)); + } else if (latHp >= maxLatHp - toleranceHp) { + return 2 + ((double) (maxLongHp - lonHp) / (maxLongHp - minLongHp)); + } else if (lonHp <= minLongHp + toleranceHp) { + return 3 + ((double) (maxLatHp - latHp) / (maxLatHp - minLatHp)); + } + return null; } /** * Find the nearest edge for supplied Coord p. */ - private EdgeHit getNextEdgeHit(Area a, Coord p) - { - int lat = p.getLatitude(); - int lon = p.getLongitude(); - int minLat = a.getMinLat(); - int maxLat = a.getMaxLat(); - int minLong = a.getMinLong(); - int maxLong = a.getMaxLong(); - - log.info(String.format("getNextEdgeHit: (%d %d) (%d %d %d %d)", lat, lon, minLat, minLong, maxLat, maxLong)); + private static Double getNextEdgeHit(Area a, Coord p) { + int latHp = p.getHighPrecLat(); + int lonHp = p.getHighPrecLon(); + int minLatHp = a.getMinLat() << Coord.DELTA_SHIFT; + int maxLatHp = a.getMaxLat() << Coord.DELTA_SHIFT; + int minLongHp = a.getMinLong() << Coord.DELTA_SHIFT; + int maxLongHp = a.getMaxLong() << Coord.DELTA_SHIFT; + + log.info(String.format("getNextEdgeHit: (%d %d) (%d %d %d %d)", latHp, lonHp, minLatHp, minLongHp, maxLatHp, maxLongHp)); // shortest distance to border (init with distance to southern border) - int min = lat - minLat; + int min = latHp - minLatHp; // number of edge as used in getEdgeHit. - // 0 = southern - // 1 = eastern - // 2 = northern + // 0 = bottom + // 1 = right + // 2 = upper // 3 = western edge of Area a int i = 0; // normalized position at border (0..1) - double l = ((double)(lon - minLong))/(maxLong-minLong); + double t = ((double) (lonHp - minLongHp)) / (maxLongHp - minLongHp); // now compare distance to eastern border with already known distance - if (maxLong - lon < min) { + if (maxLongHp - lonHp < min) { // update data if distance is shorter - min = maxLong - lon; + min = maxLongHp - lonHp; i = 1; - l = ((double)(lat - minLat))/(maxLat-minLat); + t = ((double) (latHp - minLatHp)) / (maxLatHp - minLatHp); } // same for northern border - if (maxLat - lat < min) { - min = maxLat - lat; + if (maxLatHp - latHp < min) { + min = maxLatHp - latHp; i = 2; - l = ((double)(maxLong - lon))/(maxLong-minLong); + t = ((double) (maxLongHp - lonHp)) / (maxLongHp - minLongHp); } // same for western border - if (lon - minLong < min) { + if (lonHp - minLongHp < min) { i = 3; - l = ((double)(maxLat - lat))/(maxLat-minLat); + t = ((double) (maxLatHp - latHp)) / (maxLatHp - minLatHp); } // now created the EdgeHit for found values - return new EdgeHit(i, l); - } - - private void closeGaps(List ways, Area bounds) { - - // join up coastline segments whose end points are less than - // maxCoastlineGap metres apart - if (maxCoastlineGap > 0) { - boolean changed = true; - while (changed) { - changed = false; - for (Way w1 : ways) { - if(w1.hasIdenticalEndPoints()) - continue; - List points1 = w1.getPoints(); - Coord w1e = w1.getLastPoint(); - if(bounds.onBoundary(w1e)) - continue; - Way nearest = null; - double smallestGap = Double.MAX_VALUE; - for (Way w2 : ways) { - if(w1 == w2 || w2.hasIdenticalEndPoints()) - continue; - List points2 = w2.getPoints(); - Coord w2s = points2.get(0); - if(bounds.onBoundary(w2s)) - continue; - double gap = w1e.distance(w2s); - if(gap < smallestGap) { - nearest = w2; - smallestGap = gap; - } - } - if (nearest != null && smallestGap < maxCoastlineGap) { - Coord w2s = nearest.getPoints().get(0); - log.warn("Bridging " + (int)smallestGap + "m gap in coastline from " + w1e.toOSMURL() + " to " + w2s.toOSMURL()); - Way wm; - if (FakeIdGenerator.isFakeId(w1.getId())) { - wm = w1; - } else { - wm = new Way(w1.getOriginalId()); - wm.setFakeId(); - ways.remove(w1); - ways.add(wm); - wm.getPoints().addAll(points1); - wm.copyTags(w1); - } - wm.getPoints().addAll(nearest.getPoints()); - ways.remove(nearest); - // make a line that shows the filled gap - Way w = new Way(FakeIdGenerator.makeFakeId()); - w.addTag("natural", "mkgmap:coastline-gap"); - w.addPoint(w1e); - w.addPoint(w2s); - saver.addWay(w); - changed = true; - break; - } - } - } - } + return i + t; } /** * Helper class for threadlocal vars - * - * - */ - class PrecompData { + */ + private static class PrecompData { /** * The index is a grid [lon][lat]. Each element defines the content of one precompiled * sea tile which are {@link #SEA_TYPE}, {@link #LAND_TYPE}, or {@link #MIXED_TYPE}, or 0 for unknown @@ -1478,6 +1436,7 @@ private String precompSeaPrefix; private String precompZipFileInternalPath; private ZipFile zipFile; - - } + private File dirFile; + } + } diff --git a/src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java b/src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java index 2c423c9..e6c6bd2 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/SeaPolygonRelation.java @@ -31,8 +31,7 @@ * @author WanMil */ public class SeaPolygonRelation extends MultiPolygonRelation { - private static final Logger log = Logger - .getLogger(SeaPolygonRelation.class); + private static final Logger log = Logger.getLogger(SeaPolygonRelation.class); private final QuadTree landCoords; private final QuadTree seaCoords; @@ -45,7 +44,7 @@ private final DecimalFormat format = new DecimalFormat("0.0000"); private Rule floodBlockerRules; - private final String[] landTag = {"natural","land"}; + private final String[] landTag = { "natural", "land" }; public SeaPolygonRelation(Relation other, Map wayMap, Area bbox) { super(other, wayMap, bbox); @@ -61,6 +60,7 @@ return false; } + @Override protected void postProcessing() { if (isFloodBlocker()) { removeFloodedAreas(); @@ -108,10 +108,9 @@ // create a copy of all resulting ways - the tile way map contains only // polygons from // the sea generation - ArrayList polygons = new ArrayList(getMpPolygons().values()); - - log.info("Starting flood blocker. Polygons to check:", getMpPolygons() - .size()); + ArrayList polygons = new ArrayList<>(getMpPolygons().values()); + + log.info("Starting flood blocker. Polygons to check:", getMpPolygons().size()); String baseName = GpxCreator.getGpxBaseName(); if (debug) { @@ -128,15 +127,14 @@ String polyType = (sea ? "sea" : "land"); String otherType = (sea ? "land" : "sea"); - List minusCoords = badCoords.get(p.getPoints(), - getFloodBlockerGap()); + List minusCoords = badCoords.get(p.getPoints(), getFloodBlockerGap()); List positiveCoords = goodCoords.get(p.getPoints()); log.info(polyType,"polygon", p.getId(), "contains", minusCoords.size(), otherType,"coords and", positiveCoords.size(), polyType,"coords."); - - if (minusCoords.size() > 0) { + + if (!minusCoords.isEmpty()) { double area = MultiPolygonRelation.calcAreaSize(p.getPoints()); double ratio = ((minusCoords.size() - positiveCoords.size()) * 100000.0d / area); String areaFMT = format.format(area); @@ -158,7 +156,7 @@ + positiveCoords.size() + "_" + ratioFMT, null, minusCoords); - if (positiveCoords.isEmpty() == false) { + if (!positiveCoords.isEmpty()) { GpxCreator.createGpx( baseName + p.getId() + "_pro_" + minusCoords.size() + "_" @@ -178,7 +176,6 @@ p.deleteTag(landTag[0]); p.addTag("natural", "sea"); } -// getMpPolygons().remove(p.getId()); } else { log.info("Polygon",p.getId(), "is not blocked"); } @@ -224,10 +221,6 @@ this.floodBlockerThreshold = floodBlockerThreshold; } - public boolean isDebug() { - return debug; - } - public void setDebug(boolean debug) { this.debug = debug; } diff --git a/src/uk/me/parabola/mkgmap/reader/osm/Way.java b/src/uk/me/parabola/mkgmap/reader/osm/Way.java index 4c3191f..4e5c11f 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/Way.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/Way.java @@ -16,7 +16,7 @@ */ package uk.me.parabola.mkgmap.reader.osm; -import java.awt.*; +import java.awt.Polygon; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -100,7 +100,7 @@ } public void addPointIfNotEqualToLastPoint(Coord co) { - if(points.isEmpty() || !co.equals(points.get(points.size() - 1))) + if(points.isEmpty() || !co.highPrecEquals(getLastPoint())) points.add(co); } @@ -282,10 +282,23 @@ this.fullArea = fullArea; } - public long getFullArea() { // this is unadulterated size, +ve if clockwise - if (this.fullArea == Long.MAX_VALUE && points.size() >= 4 && points.get(0).highPrecEquals(points.get(points.size()-1))) { + public long getFullArea() { // this is unadulterated size, positive if clockwise + if (this.fullArea == Long.MAX_VALUE && points.size() >= 4 && getFirstPoint().highPrecEquals(getLastPoint())) { this.fullArea = ShapeMergeFilter.calcAreaSizeTestVal(points); } return this.fullArea; } + + public double calcLengthInMetres() { + double length = 0; + if (points.size() > 1) { + Coord p0 = points.get(0); + for (int i = 1; i < points.size(); i++) { + Coord p1 = points.get(i); + length += p0.distance(p1); + p0 = p1; + } + } + return length; + } } diff --git a/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java b/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java index 55d283b..6c99ccd 100644 --- a/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java +++ b/src/uk/me/parabola/mkgmap/reader/osm/o5m/O5mBinHandler.java @@ -16,6 +16,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.mkgmap.reader.osm.Element; @@ -49,17 +50,16 @@ private static final int STRING_TABLE_SIZE = 15000; private static final int MAX_STRING_PAIR_SIZE = 250 + 2; private static final String[] REL_REF_TYPES = {"node", "way", "relation", "?"}; - private static final double FACTOR = 1d/1000000000; // used with 100**FACTOR + private static final double FACTOR = 1d/1_000_000_000; // used with 100**FACTOR private BufferedInputStream fis; private InputStream is; - private ByteArrayInputStream bis; // buffer for byte -> String conversions private byte[] cnvBuffer; private byte[] ioBuf; - private int ioPos; + private int ioBufPos; // the o5m string table private String[][] stringTable; private String[] stringPair; @@ -67,24 +67,23 @@ // a counter that must be maintained by all routines that read data from the stream private int bytesToRead; // total number of bytes read from stream - long countBytes; + private long countBytes; // for delta calculations private long lastNodeId; private long lastWayId; private long lastRelId; - private long lastRef[]; + private long[] lastRef; private long lastTs; private long lastChangeSet; - private int lastLon,lastLat; + private int lastLon; + private int lastLat; /** * A parser for the o5m format - * @param processor A mapProcessor instance - * @param skipArray An Array of longs that is used to hold information of file position of the first occurrence of - * each known 05m data type (esp. nodes, ways, and relations). */ public O5mBinHandler() { + // nothing to do } @Override @@ -93,62 +92,62 @@ } /** - * parse the input stream + * Parse the input stream. */ @Override - public void parse(InputStream stream){ + public void parse(InputStream stream) { this.fis = new BufferedInputStream(stream); is = fis; this.cnvBuffer = new byte[4000]; // OSM data should not contain string pairs with length > 512 this.ioBuf = new byte[8192]; - this.ioPos = 0; + this.ioBufPos = 0; this.stringPair = new String[2]; this.lastRef = new long[3]; reset(); try { int start = is.read(); ++countBytes; - if (start != RESET_FLAG) + if (start != RESET_FLAG) throw new IOException("wrong header byte " + start); readFile(); } catch (IOException e) { + System.err.println("exception after " + countBytes + " bytes"); e.printStackTrace(); } } private void readFile() throws IOException{ boolean done = false; - while(!done){ + while(!done) { is = fis; long size = 0; int fileType = is.read(); ++countBytes; - if (fileType >= 0 && fileType < 0xf0){ + if (fileType >= 0 && fileType < 0xf0) { bytesToRead = 0; size = readUnsignedNum64FromStream(); - countBytes += size - bytesToRead; // bytesToRead is negative - bytesToRead = (int)size; + countBytes += size - bytesToRead; // bytesToRead is negative + bytesToRead = (int) size; - switch(fileType){ + switch (fileType) { case NODE_DATASET: case WAY_DATASET: case REL_DATASET: case BBOX_DATASET: case TIMESTAMP_DATASET: case HEADER_DATASET: - if (bytesToRead > ioBuf.length){ - ioBuf = new byte[(int)bytesToRead+100]; + if (bytesToRead > ioBuf.length) { + ioBuf = new byte[bytesToRead + 100]; } - int bytesRead = 0; + int bytesRead = 0; int neededBytes = bytesToRead; - while (neededBytes > 0){ + while (neededBytes > 0) { bytesRead += is.read(ioBuf, bytesRead, neededBytes); neededBytes -= bytesRead; - } - ioPos = 0; - bis = new ByteArrayInputStream(ioBuf,0,bytesToRead); - is = bis; - break; + } + ioBufPos = 0; + is = new ByteArrayInputStream(ioBuf, 0, bytesToRead); + break; default: } } @@ -162,16 +161,16 @@ else if (fileType == EOD_FLAG) done = true; else if (fileType == RESET_FLAG) reset(); else { - if (fileType < 0xf0 )skip(size); // skip unknown data set - } - } - } - - /** - * read (and ignore) the file timestamp data set - */ - private void readFileTimestamp(){ - /*long fileTimeStamp = */readSignedNum64(); + if (fileType < 0xf0 ) skip(size); // skip unknown data set + } + } + } + + /** + * Read (and ignore) the file timestamp data set. + */ + private void readFileTimestamp() { + /* long fileTimeStamp = */readSignedNum64(); } /** @@ -179,30 +178,30 @@ * @param bytes * @throws IOException */ - private void skip(long bytes)throws IOException{ + private void skip(long bytes) throws IOException { long toSkip = bytes; - while (toSkip > 0) + while (toSkip > 0) { toSkip -= is.skip(toSkip); - } - - /** - * read the bounding box data set - * @throws IOException + } + } + + /** + * Read the bounding box data set. */ private void readBBox() { - double leftf = (double) (100L*readSignedNum32()) * FACTOR; - double bottomf = (double) (100L*readSignedNum32()) * FACTOR; - double rightf = (double) (100L*readSignedNum32()) * FACTOR; - double topf = (double) (100L*readSignedNum32()) * FACTOR; + double minlon = FACTOR * 100L * readSignedNum32(); + double minlat = FACTOR * 100L * readSignedNum32(); + double maxlon = FACTOR * 100L * readSignedNum32(); + double maxlat = FACTOR * 100L * readSignedNum32(); + assert bytesToRead == 0; - setBBox(bottomf, leftf, topf, rightf); - } - - /** - * read a node data set - * @throws IOException - */ - private void readNode() throws IOException{ + setBBox(minlat, minlon, maxlat, maxlon); + } + + /** + * Read a node data set. + */ + private void readNode() { lastNodeId += readSignedNum64(); if (bytesToRead == 0) return; // only nodeId: this is a delete action, we ignore it @@ -212,17 +211,17 @@ int lon = readSignedNum32() + lastLon; lastLon = lon; int lat = readSignedNum32() + lastLat; lastLat = lat; - double flon = (double)(100L*lon) * FACTOR; - double flat = (double)(100L*lat) * FACTOR; + double flon = FACTOR * (100L * lon); + double flat = FACTOR * (100L * lat); assert flat >= -90.0 && flat <= 90.0; assert flon >= -180.0 && flon <= 180.0; Coord co = new Coord(flat, flon); saver.addPoint(lastNodeId, co); - if (bytesToRead > 0){ - Node node = new Node(lastNodeId,co); + if (bytesToRead > 0) { + Node node = new Node(lastNodeId, co); readTags(node); - if (node.getTagCount() > 0){ + if (node.getTagCount() > 0) { // If there are tags, then we save a proper node for it. saver.addNode(node); hooks.onAddNode(node); @@ -231,35 +230,33 @@ } /** - * read a way data set - * @throws IOException - */ - private void readWay() throws IOException{ + * Read a way data set. + */ + private void readWay() { lastWayId += readSignedNum64(); if (bytesToRead == 0) - return; // only wayId: this is a delete action, we ignore it + return; // only wayId: this is a delete action, we ignore it readVersionTsAuthor(); if (bytesToRead == 0) - return; // only wayId + version: this is a delete action, we ignore it + return; // only wayId + version: this is a delete action, we ignore it Way way = startWay(lastWayId); long refSize = readUnsignedNum32(); long stop = bytesToRead - refSize; - - while(bytesToRead > stop){ + + while (bytesToRead > stop) { lastRef[0] += readSignedNum64(); addCoordToWay(way, lastRef[0]); } - + readTags(way); endWay(way); } /** - * read a relation data set - * @throws IOException - */ - private void readRel() throws IOException{ + * Read a relation data set. + */ + private void readRel() { lastRelId += readSignedNum64(); if (bytesToRead == 0) return; // only relId: this is a delete action, we ignore it @@ -270,29 +267,27 @@ GeneralRelation rel = new GeneralRelation(lastRelId); long refSize = readUnsignedNum32(); long stop = bytesToRead - refSize; - while(bytesToRead > stop){ + while (bytesToRead > stop) { Element el = null; long deltaRef = readSignedNum64(); int refType = readRelRef(); String role = stringPair[1]; lastRef[refType] += deltaRef; long memId = lastRef[refType]; - if (refType == 0){ + if (refType == 0) { el = saver.getNode(memId); - if(el == null) { + if (el == null) { // we didn't make a node for this point earlier, // do it now (if it exists) Coord co = saver.getCoord(memId); - if(co != null) { + if (co != null) { el = new Node(memId, co); - saver.addNode((Node)el); + saver.addNode((Node) el); } } - } - else if (refType == 1){ + } else if (refType == 1) { el = saver.getWay(memId); - } - else if (refType == 2){ + } else if (refType == 2) { el = saver.getRelation(memId); if (el == null) { saver.deferRelation(memId, rel, role); @@ -308,9 +303,9 @@ saver.addRelation(rel); } - private boolean readTags(Element elem) throws IOException{ + private boolean readTags(Element elem) { boolean tagsIncomplete = false; - while (bytesToRead > 0){ + while (bytesToRead > 0) { readStringPair(); String key = stringPair[0]; String val = stringPair[1]; @@ -322,16 +317,17 @@ key = keepTag(key, val); if (key != null) elem.addTagFromRawOSM(key, val); - else + else tagsIncomplete = true; } assert bytesToRead == 0; return tagsIncomplete; } - /** - * Store a new string pair (length check must be performed by caller) - */ - private void storeStringPair(){ + + /** + * Store a new string pair (length check must be performed by caller). + */ + private void storeStringPair() { stringTable[0][currStringTablePos] = stringPair[0]; stringTable[1][currStringTablePos] = stringPair[1]; ++currStringTablePos; @@ -340,87 +336,85 @@ } /** - * set stringPair to the values referenced by given string reference - * No checking is performed. + * set stringPair to the values referenced by given string reference No checking + * is performed. + * * @param ref valid values are 1 .. STRING_TABLE_SIZE */ - private void setStringRefPair(int ref){ + private void setStringRefPair(int ref) { int pos = currStringTablePos - ref; - if (pos < 0) + if (pos < 0) pos += STRING_TABLE_SIZE; stringPair[0] = stringTable[0][pos]; stringPair[1] = stringTable[1][pos]; } /** - * Read version, time stamp and change set and author. - * We are not interested in the values, but we have to maintain the string table. - * @throws IOException - */ - - private void readVersionTsAuthor() throws IOException { - int version = readUnsignedNum32(); - if (version != 0){ + * Read version, time stamp and change set and author. We are not interested in + * the values, but we have to maintain the string table. + */ + private void readVersionTsAuthor() { + int version = readUnsignedNum32(); + if (version != 0) { // version info - long ts = readSignedNum64() + lastTs; lastTs = ts; - if (ts != 0){ - long changeSet = readSignedNum32() + lastChangeSet; lastChangeSet = changeSet; + long ts = readSignedNum64() + lastTs; + lastTs = ts; + if (ts != 0) { + long changeSet = readSignedNum32() + lastChangeSet; + lastChangeSet = changeSet; readAuthor(); } } } - /** - * Read author . - * @throws IOException - */ - private void readAuthor() throws IOException{ + + /** + * Read author. + */ + private void readAuthor() { int stringRef = readUnsignedNum32(); - if (stringRef == 0){ + if (stringRef == 0) { long toReadStart = bytesToRead; long uidNum = readUnsignedNum64(); if (uidNum == 0) stringPair[0] = ""; - else{ - stringPair[0] = Long.toString(uidNum); - ioPos++; // skip terminating zero from uid + else { + stringPair[0] = Long.toUnsignedString(uidNum); + ioBufPos++; // skip terminating zero from uid --bytesToRead; } stringPair[1] = readString(); long bytes = toReadStart - bytesToRead; if (bytes <= MAX_STRING_PAIR_SIZE) storeStringPair(); - } - else + } else { setStringRefPair(stringRef); - - //System.out.println(pair[0]+ "/" + pair[1]); - } - - /** - * read object type ("0".."2") concatenated with role (single string) + } + } + + /** + * Read object type ("0".."2") concatenated with role (single string) . * @return 0..3 for type (3 means unknown) */ - private int readRelRef () throws IOException{ + private int readRelRef () { int refType = -1; long toReadStart = bytesToRead; int stringRef = readUnsignedNum32(); - if (stringRef == 0){ - refType = ioBuf[ioPos++] - 0x30; + if (stringRef == 0) { + refType = ioBuf[ioBufPos++] - 0x30; --bytesToRead; if (refType < 0 || refType > 2) refType = 3; stringPair[0] = REL_REF_TYPES[refType]; - + stringPair[1] = readString(); long bytes = toReadStart - bytesToRead; if (bytes <= MAX_STRING_PAIR_SIZE) storeStringPair(); - } - else { + } else { setStringRefPair(stringRef); char c = stringPair[0].charAt(0); - switch (c){ + switch (c) { case 'n': refType = 0; break; case 'w': refType = 1; break; case 'r': refType = 2; break; @@ -431,43 +425,41 @@ } /** - * read a string pair (see o5m definition) - * @throws IOException - */ - private void readStringPair() throws IOException{ + * Read a string pair (see o5m definition). + */ + private void readStringPair() { int stringRef = readUnsignedNum32(); - if (stringRef == 0){ + if (stringRef == 0) { long toReadStart = bytesToRead; int cnt = 0; - while (cnt < 2){ + while (cnt < 2) { stringPair[cnt++] = readString(); } long bytes = toReadStart - bytesToRead; if (bytes <= MAX_STRING_PAIR_SIZE) storeStringPair(); - } - else + } else { setStringRefPair(stringRef); + } } /** * Read a zero-terminated string (see o5m definition). * @throws IOException */ - String readString() throws IOException { - int length = 0; + private String readString() { + int length = 0; while (true) { - final int b = ioBuf[ioPos++]; + final int b = ioBuf[ioBufPos++]; --bytesToRead; if (b == 0) - return new String(cnvBuffer, 0, length, "UTF-8"); + return new String(cnvBuffer, 0, length, StandardCharsets.UTF_8); cnvBuffer[length++] = (byte) b; } } - /** reset the delta values and string table */ - private void reset(){ + private void reset() { lastNodeId = 0; lastWayId = 0; lastRelId = 0; lastRef[0] = 0; lastRef[1] = 0;lastRef[2] = 0; lastTs = 0; lastChangeSet = 0; @@ -477,153 +469,109 @@ } /** - * read and verify o5m header (known values are o5m2 and o5c2) - * @throws IOException + * Read and verify o5m header (known values are o5m2 and o5c2). + * @throws IOException in case of error */ private void readHeader() throws IOException { - if (ioBuf[0] != 'o' || ioBuf[1] != '5' || (ioBuf[2]!='c'&&ioBuf[2]!='m') ||ioBuf[3] != '2' ){ + if (ioBuf[0] != 'o' || ioBuf[1] != '5' || (ioBuf[2] != 'c' && ioBuf[2] != 'm') || ioBuf[3] != '2') { throw new IOException("unsupported header"); } } /** - * read a varying length signed number (see o5m definition) - * @return the number - * @throws IOException + * Read a varying length signed number (see o5m definition). + * @return the number as int */ private int readSignedNum32() { - int result; - int b = ioBuf[ioPos++]; + return (int) readSignedNum64(); + } + + /** + * Read a varying length signed number (see o5m definition). + * @return the number as long + */ + private long readSignedNum64() { + long result; + int b = ioBuf[ioBufPos++]; --bytesToRead; result = b; - if ((b & 0x80) == 0){ // just one byte + if ((b & 0x80) == 0) { // just one byte if ((b & 0x01) == 1) - return -1-(result>>1); - else - return result>>1; + return -1 - (result >> 1); + return result >> 1; + } int sign = b & 0x01; - result = (result & 0x7e)>>1; - int fac = 0x40; - while (((b = ioBuf[ioPos++]) & 0x80) != 0){ // more bytes will follow + result = (result & 0x7e) >> 1; + int shift = 6; + while (((b = ioBuf[ioBufPos++]) & 0x80) != 0) { // more bytes will follow --bytesToRead; - result += fac * (b & 0x7f) ; - fac <<= 7; + result += ((long) (b & 0x7f)) << shift; + shift += 7; } --bytesToRead; - result += fac * b; + result += ((long) b) << shift; if (sign == 1) // negative - return -1-result; - else - return result; - - } - - /** - * read a varying length signed number (see o5m definition) - * @return the number - * @throws IOException - */ - private long readSignedNum64() { - long result; - int b = ioBuf[ioPos++]; - --bytesToRead; - result = b; - if ((b & 0x80) == 0){ // just one byte - if ((b & 0x01) == 1) - return -1-(result>>1); - else - return result>>1; - } - int sign = b & 0x01; - result = (result & 0x7e)>>1; - long fac = 0x40; - while (((b = ioBuf[ioPos++]) & 0x80) != 0){ // more bytes will follow - --bytesToRead; - result += fac * (b & 0x7f) ; - fac <<= 7; - } - --bytesToRead; - result += fac * b; - if (sign == 1) // negative - return -1-result; - else - return result; - - } - - /** - * read a varying length unsigned number (see o5m definition) - * @return a long - * @throws IOException - */ - private long readUnsignedNum64FromStream()throws IOException { + return -1 - result; + return result; + } + + /** + * Read a varying length unsigned number (see o5m definition). + * + * @return the number as long + * @throws IOException in case of I/O error + */ + private long readUnsignedNum64FromStream() throws IOException { int b = is.read(); --bytesToRead; long result = b; - if ((b & 0x80) == 0){ // just one byte + if ((b & 0x80) == 0) { // just one byte return result; } result &= 0x7f; - long fac = 0x80; - while (((b = is.read()) & 0x80) != 0){ // more bytes will follow + int shift = 7; + while (((b = is.read()) & 0x80) != 0) { // more bytes will follow --bytesToRead; - result += fac * (b & 0x7f) ; - fac <<= 7; + result += ((long) (b & 0x7f)) << shift; + shift += 7; } --bytesToRead; - result += fac * b; + result += ((long) b) << shift; return result; } - - /** - * read a varying length unsigned number (see o5m definition) - * @return a long - * @throws IOException - */ - private long readUnsignedNum64(){ - int b = ioBuf[ioPos++]; + /** + * Read a varying length unsigned number (see o5m definition). + * + * @return the number as long + */ + private long readUnsignedNum64() { + int b = ioBuf[ioBufPos++]; --bytesToRead; long result = b; - if ((b & 0x80) == 0){ // just one byte + if ((b & 0x80) == 0) { // just one byte return result; } result &= 0x7f; - long fac = 0x80; - while (((b = ioBuf[ioPos++]) & 0x80) != 0){ // more bytes will follow + int shift = 7; + while (((b = ioBuf[ioBufPos++]) & 0x80) != 0) { // more bytes will follow --bytesToRead; - result += fac * (b & 0x7f) ; - fac <<= 7; + result += ((long) (b & 0x7f)) << shift; + shift += 7; } --bytesToRead; - result += fac * b; + result += ((long) b) << shift; return result; } /** - * read a varying length unsigned number (see o5m definition) - * is similar to the 64 bit version. - * @return an int - * @throws IOException - */ - private int readUnsignedNum32(){ - int b = ioBuf[ioPos++]; - --bytesToRead; - int result = b; - if ((b & 0x80) == 0){ // just one byte - return result; - } - result &= 0x7f; - long fac = 0x80; - while (((b = ioBuf[ioPos++]) & 0x80) != 0){ // more bytes will follow - --bytesToRead; - result += fac * (b & 0x7f) ; - fac <<= 7; - } - --bytesToRead; - result += fac * b; - return result; + * Read a varying length unsigned number (see o5m definition). + * + * @return the number as int + */ + private int readUnsignedNum32() { + return (int) readUnsignedNum64(); } } diff --git a/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaGenerator.java b/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaGenerator.java index 998b7cd..6a844c1 100644 --- a/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaGenerator.java +++ b/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaGenerator.java @@ -142,18 +142,18 @@ * Retrieve the areas of all precompiled tiles that have to be worked out. * @return the areas of all tiles */ - private List getTiles() { + private static List getTiles() { return getTiles(uk.me.parabola.imgfmt.app.Area.PLANET); } - private List getTiles( + private static List getTiles( uk.me.parabola.imgfmt.app.Area wholeArea) { int minLat = wholeArea.getMinLat(); int maxLat = wholeArea.getMaxLat(); int minLon = wholeArea.getMinLong(); int maxLon = wholeArea.getMaxLong(); - List tiles = new ArrayList(); + List tiles = new ArrayList<>(); for (int lon = SeaGenerator.getPrecompTileStart(minLon); lon < maxLon; lon += SeaGenerator.PRECOMP_RASTER) { for (int lat = SeaGenerator.getPrecompTileStart(minLat); lat < maxLat; lat += SeaGenerator.PRECOMP_RASTER) { uk.me.parabola.imgfmt.app.Area tile = new uk.me.parabola.imgfmt.app.Area( @@ -180,6 +180,7 @@ setDaemon(true); } + @Override public void run() { long count = 0; do { @@ -198,7 +199,7 @@ * @param geometry a polygon as {@link Geometry} object * @return the polygon converted to an {@link Area} object. */ - private Area convertToArea(Geometry geometry) { + private static Area convertToArea(Geometry geometry) { Coordinate[] c = geometry.getCoordinates(); List points = new ArrayList<>(c.length); for (int n = 0; n < c.length; n++) { @@ -219,7 +220,7 @@ Collection tiles, CountDownLatch tilesCountdown, BlockingQueue>> saveQueue) { - List mergers = new ArrayList(); + List mergers = new ArrayList<>(); for (uk.me.parabola.imgfmt.app.Area bounds : tiles) { @@ -236,7 +237,7 @@ } private void createShapefileAccess() throws IOException { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("url", shapeFile.toURI().toURL()); DataStore dataStore = DataStoreFinder.getDataStore(map); String typeName = dataStore.getTypeNames()[0]; @@ -276,10 +277,10 @@ // perform several cycles which is necessary to reduce memory // requirements - while (remainingTiles.isEmpty() == false) { + while (!remainingTiles.isEmpty()) { // create a list with all tiles that are processed within this cycle - List tiles = new ArrayList(); + List tiles = new ArrayList<>(); tiles.addAll(remainingTiles.subList(0, Math.min(tilesPerCycle, remainingTiles.size()))); remainingTiles.subList(0, @@ -402,7 +403,7 @@ String shapeCRS = args[1]; File outputDir = new File(args[2]); - if (shapeFile.exists() == false) { + if (!shapeFile.exists()) { throw new FileNotFoundException("File "+shapeFile+" does not exist."); } diff --git a/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaMerger.java b/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaMerger.java index ab30851..93b6899 100644 --- a/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaMerger.java +++ b/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaMerger.java @@ -47,18 +47,18 @@ private final BlockingQueue>> saveQueue; private ExecutorService service; - static class MergeData { - public final Rectangle2D bounds; - public final BlockingQueue toMerge; - public final AtomicBoolean ready = new AtomicBoolean(false); - public Path2D.Double tmpLandPath = new Path2D.Double(); - public Area landArea = new Area(); + private static class MergeData { + final Rectangle2D bounds; + final BlockingQueue toMerge; + final AtomicBoolean ready = new AtomicBoolean(false); + Path2D.Double tmpLandPath = new Path2D.Double(); + Area landArea = new Area(); private final String key; public MergeData(Rectangle2D bounds, String key) { this.key = key; this.bounds = bounds; - toMerge = new LinkedBlockingQueue(); + toMerge = new LinkedBlockingQueue<>(); } public String getKey() { @@ -95,9 +95,9 @@ return mergeData.bounds; } - private List convertToWays(Area a, String naturalTag) { + private static List convertToWays(Area a, String naturalTag) { List> pointLists = Java2DConverter.areaToShapes(a); - List ways = new ArrayList(pointLists.size()); + List ways = new ArrayList<>(pointLists.size()); for (List points : pointLists) { Way w = new Way(FakeIdGenerator.makeFakeId(), points); w.addTag("natural", naturalTag); @@ -138,8 +138,7 @@ merge = mergeData.toMerge.poll(); } - if (mergeData.ready.get() == false - || mergeData.toMerge.isEmpty() == false) { + if (!mergeData.ready.get() || !mergeData.toMerge.isEmpty()) { // repost the merge thread service.execute(this); return; @@ -159,7 +158,7 @@ // no land in this tile => create a sea way only ways.addAll(convertToWays(new Area(mergeData.bounds), "sea")); } else { - Map landWays = new HashMap(); + Map landWays = new HashMap<>(); List> landParts = Java2DConverter .areaToShapes(mergeData.landArea); for (List landPoints : landParts) { @@ -187,6 +186,7 @@ { // do not calculate the area size => it is not required and adds // a superfluous tag + @Override protected boolean isAreaSizeCalculated() { return false; } @@ -203,7 +203,7 @@ .getTag(MultiPolygonRelation.STYLE_FILTER_TAG))) { String tag = w.getTag("natural"); - if ("sea".equals(tag) == false) { + if (!"sea".equals(tag)) { // ignore the land polygons - we already have them in our list continue; } @@ -216,8 +216,7 @@ try { // forward the ways to the queue of the saver thread - saveQueue.put(new SimpleEntry>( - mergeData.getKey(), ways)); + saveQueue.put(new SimpleEntry>(mergeData.getKey(), ways)); } catch (InterruptedException exp) { exp.printStackTrace(); } diff --git a/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaSaver.java b/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaSaver.java index 0270b9e..d522863 100644 --- a/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaSaver.java +++ b/src/uk/me/parabola/mkgmap/sea/optional/PrecompSeaSaver.java @@ -13,12 +13,12 @@ package uk.me.parabola.mkgmap.sea.optional; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -32,6 +32,8 @@ import java.util.regex.Pattern; import java.util.zip.GZIPOutputStream; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import uk.me.parabola.imgfmt.ExitException; import uk.me.parabola.imgfmt.Utils; import uk.me.parabola.imgfmt.app.Coord; import uk.me.parabola.mkgmap.reader.osm.SeaGenerator; @@ -53,14 +55,14 @@ private final File outputDir; - private final BlockingQueue>> saveQueue = new LinkedBlockingQueue>>(); + private final BlockingQueue>> saveQueue = new LinkedBlockingQueue<>(); public PrecompSeaSaver(File outputDir, boolean usePbf) { this.outputDir = outputDir; finishWait = new CountDownLatch(1); this.usePbf = usePbf; - idMapping = new HashMap(); - index = new TreeMap(); + idMapping = new HashMap<>(); + index = new TreeMap<>(); this.outputDir.mkdirs(); } @@ -70,10 +72,10 @@ private OSMWriter createWriter(int id, String key) { String[] parts = key.split(Pattern.quote("_")); - int lat = Integer.valueOf(parts[0]); - int lon = Integer.valueOf(parts[1]); - uk.me.parabola.splitter.Area bounds = new uk.me.parabola.splitter.Area( - lat, lon, lat + SeaGenerator.PRECOMP_RASTER, lon + SeaGenerator.PRECOMP_RASTER); + int lat = Integer.parseInt(parts[0]); + int lon = Integer.parseInt(parts[1]); + uk.me.parabola.splitter.Area bounds = new uk.me.parabola.splitter.Area(lat, lon, + lat + SeaGenerator.PRECOMP_RASTER, lon + SeaGenerator.PRECOMP_RASTER); OSMWriter writer = (usePbf ? new BinaryMapWriter(bounds, outputDir, nextId, 0) : new OSMXMLWriter(bounds, outputDir, nextId, 0)); idMapping.put(id, key); @@ -87,7 +89,7 @@ } public void run() { - while (saveQueue.isEmpty() == false || finished.get() == false) { + while (!saveQueue.isEmpty() || !finished.get()) { Entry> tileData = null; try { tileData = saveQueue.poll(1, TimeUnit.MINUTES); @@ -95,7 +97,7 @@ exp.printStackTrace(); } if (tileData != null) { - int id = ++nextId; + int fakeMapid = ++nextId; if (tileData.getValue().size() == 1) { // do not write the tile because it consists of one @@ -105,82 +107,77 @@ String naturalTag = singleWay.getTag("natural"); index.put(tileData.getKey(), naturalTag); } else { - String ext = (usePbf ? "pbf" : "gz"); - index.put(tileData.getKey(), "sea_" + tileData.getKey() - + ".osm." + ext); - - OSMWriter writer = createWriter(id, tileData.getKey()); - - Long2ObjectOpenHashMap coordIds = new Long2ObjectOpenHashMap<>(); - Map pbfWays = new TreeMap(); - long maxNodeId = 1; - for (Way w : tileData.getValue()) { - uk.me.parabola.splitter.Way pbfWay = new uk.me.parabola.splitter.Way(); - pbfWay.set(w.getId()); - for (Entry tag : w - .getTagEntryIterator()) { - pbfWay.addTag(tag.getKey(), tag.getValue()); - } - for (Coord c : w.getPoints()) { - Node n = new Node(); - long key = Utils.coord2Long(c); - Long nodeId = coordIds.get(key); - if (nodeId == null) { - nodeId = new Long(maxNodeId++); - coordIds.put(key, nodeId); - n.set(nodeId, - c.getLatDegrees(), - c.getLonDegrees()); - try { - writer.write(n); - } catch (IOException exp) { - exp.printStackTrace(); - } - } - pbfWay.addRef(nodeId); - } - pbfWays.put(pbfWay.getId(),pbfWay); + try { + writeTile(tileData, fakeMapid); + } catch (IOException e) { + throw new ExitException(e.getLocalizedMessage()); } - for (uk.me.parabola.splitter.Way pbfWay : pbfWays.values()) { - try { - writer.write(pbfWay); - } catch (IOException exp) { - exp.printStackTrace(); - } - } - writer.finishWrite(); - - File tileFile = new File(outputDir, String.format( - "%08d.osm." + ext, id)); - File precompFile = new File(outputDir, "sea_" - + tileData.getKey() + ".osm." + ext); - if (precompFile.exists()) { - precompFile.delete(); - } - - tileFile.renameTo(precompFile); } } } - writeIndex(); + try { + writeIndex(); + } catch (IOException e) { + e.printStackTrace(); + } finishWait.countDown(); } - private void writeIndex() { - try { - PrintWriter indexWriter = new PrintWriter( - new GZIPOutputStream(new FileOutputStream( - new File(outputDir, "index.txt.gz")))); - + /** + * Use splitter.jar to write the tile in osm format. + * @param tileData the tile data containing the + * @param fakeMapid the pseudo-mapid used for this tile + * @throws IOException in case of error + */ + private void writeTile(Entry> tileData, int fakeMapid) throws IOException { + String ext = (usePbf ? "pbf" : "gz"); + index.put(tileData.getKey(), "sea_" + tileData.getKey() + ".osm." + ext); + + OSMWriter writer = createWriter(fakeMapid, tileData.getKey()); + + Long2ObjectOpenHashMap coordIds = new Long2ObjectOpenHashMap<>(); + Map pbfWays = new TreeMap<>(); + long maxNodeId = 1; + for (Way w : tileData.getValue()) { + uk.me.parabola.splitter.Way pbfWay = new uk.me.parabola.splitter.Way(); + pbfWay.set(w.getId()); + for (Entry tag : w.getTagEntryIterator()) { + pbfWay.addTag(tag.getKey(), tag.getValue()); + } + for (Coord c : w.getPoints()) { + Node n = new Node(); + long key = Utils.coord2Long(c); + Long nodeId = coordIds.get(key); + if (nodeId == null) { + nodeId = maxNodeId++; + coordIds.put(key, nodeId); + n.set(nodeId, c.getLatDegrees(), c.getLonDegrees()); + writer.write(n); + } + pbfWay.addRef(nodeId); + } + pbfWays.put(pbfWay.getId(), pbfWay); + } + for (uk.me.parabola.splitter.Way pbfWay : pbfWays.values()) { + writer.write(pbfWay); + } + + writer.finishWrite(); + + File tileFile = new File(outputDir, String.format("%08d.osm.%s", fakeMapid, ext)); + File precompFile = new File(outputDir, "sea_" + tileData.getKey() + ".osm." + ext); + Files.move(tileFile.toPath(), precompFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + + private void writeIndex() throws IOException { + try (PrintWriter indexWriter = new PrintWriter( + new GZIPOutputStream(new FileOutputStream(new File(outputDir, "index.txt.gz"))))) { + for (Entry ind : index.entrySet()) { - indexWriter.format("%s;%s\n", ind.getKey(), ind.getValue()); + indexWriter.format("%s;%s", ind.getKey(), ind.getValue()).append('\n'); } - - indexWriter.close(); - } catch (IOException exp1) { - exp1.printStackTrace(); } } }diff --git a/src/uk/me/parabola/util/QuadTree.java b/src/uk/me/parabola/util/QuadTree.java index b1e32c2..5b9de5d 100644 --- a/src/uk/me/parabola/util/QuadTree.java +++ b/src/uk/me/parabola/util/QuadTree.java @@ -22,11 +22,11 @@ } public boolean addAll(Collection coordList) { - boolean oneAdded = false; + long oldCount = itemCount; for (Coord c : coordList) { - oneAdded = add(c) | oneAdded; + add(c); } - return oneAdded; + return itemCount > oldCount; } public boolean add(Coord c) { @@ -43,8 +43,7 @@ } public List get(Collection> polygons) { - return root.get(new QuadTreePolygon(polygons), new ArrayList( - 2000)); + return root.get(new QuadTreePolygon(polygons), new ArrayList(2000)); } public List get(List polygon) { @@ -55,11 +54,10 @@ if (polygon.size() < 3) { return Collections.emptyList(); } - if (polygon.get(0).equals(polygon.get(polygon.size() - 1)) == false) { - return null; + if (!polygon.get(0).equals(polygon.get(polygon.size() - 1))) { + throw new IllegalArgumentException("polygon is not closed"); } - ArrayList points = root.get(new QuadTreePolygon(polygon), - new ArrayList(2000)); + List points = root.get(new QuadTreePolygon(polygon), new ArrayList(2000)); if (offset > 0) { ListIterator pointIter = points.listIterator(); while (pointIter.hasNext()) { @@ -80,8 +78,7 @@ return itemCount; } - private static boolean isCloseToPolygon(Coord point, List polygon, - int gap) { + private static boolean isCloseToPolygon(Coord point, List polygon, int gap) { Iterator polyIter = polygon.iterator(); Coord c2 = polyIter.next(); while (polyIter.hasNext()) { diff --git a/src/uk/me/parabola/util/QuadTreeNode.java b/src/uk/me/parabola/util/QuadTreeNode.java index 1ce8d0d..c218cc0 100644 --- a/src/uk/me/parabola/util/QuadTreeNode.java +++ b/src/uk/me/parabola/util/QuadTreeNode.java @@ -1,7 +1,6 @@ package uk.me.parabola.util; -import java.awt.*; -import java.util.ArrayList; +import java.awt.Rectangle; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -90,7 +89,7 @@ this.points = points; split(); } else { - this.points = new HashSet(points); + this.points = new HashSet<>(points); } } @@ -102,7 +101,7 @@ if (coveredBounds == null) { coveredBounds = new Area(c.getLatitude(), c.getLongitude(), c.getLatitude(), c.getLongitude()); - } else if (coveredBounds.contains(c) == false) { + } else if (!coveredBounds.contains(c)) { coveredBounds = new Area(Math.min(coveredBounds.getMinLat(), c.getLatitude()), Math.min(coveredBounds.getMinLong(), c.getLongitude()), Math.max(coveredBounds.getMaxLat(), @@ -126,11 +125,7 @@ public List get(Area bbox, List resultList) { if (isLeaf()) { - if (bbox.getMinLat() <= coveredBounds.getMinLat() - && bbox.getMaxLat() >= coveredBounds.getMaxLat() - && bbox.getMinLong() <= coveredBounds.getMinLong() - && bbox.getMaxLong() >= coveredBounds.getMaxLong()) { - + if (bbox.contains(coveredBounds)) { // the bounding box is contained completely in the bbox // => add all points without further check resultList.addAll(points); @@ -152,7 +147,7 @@ return resultList; } - public ArrayList get(QuadTreePolygon polygon, ArrayList resultList) { + public List get(QuadTreePolygon polygon, List resultList) { if (polygon.getBbox().intersects(getBounds())) { if (isLeaf()) { for (Coord c : points) { @@ -176,9 +171,9 @@ } - private java.awt.geom.Area createArea(Area bbox) { - return new java.awt.geom.Area(new Rectangle(bbox.getMinLong(), - bbox.getMinLat(), bbox.getWidth(), bbox.getHeight())); + private static java.awt.geom.Area createArea(Area bbox) { + return new java.awt.geom.Area( + new Rectangle(bbox.getMinLong(), bbox.getMinLat(), bbox.getWidth(), bbox.getHeight())); } public boolean isLeaf() { @@ -217,7 +212,7 @@ public void clear() { this.children = null; - points = new HashSet(); + points = new HashSet<>(); coveredBounds = new Area(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); }