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