Codebase list mkgmap / a0afdf6
New upstream version 0.0.0+svn4555 Bas Couwenberg 3 years ago
234 changed file(s) with 2650 addition(s) and 2603 deletion(s). Raw diff Collapse all Expand all
0
1 settings:
2 language: [java, python]
3
4 build_envs:
5 java: [jdk8, openjdk8, openjdk11]
6
7 build:
8 - shell:
9 - java -version
10 - ant -version
11 #
12 # Set the version and timestamp properties
13 - |
14 echo "svn.version: {{rev.revision}}" > resources/{{rev.project}}-version.properties
15 echo "build.timestamp: $(date -Iseconds)" >> resources/{{rev.project}}-version.properties
16
17 - when: rev.is_trunk
18 set_var:
19 dist_name: mkgmap-r{{rev.revision}}
20 - when: not rev.is_trunk
21 set_var:
22 dist_name: mkgmap-{{rev.branch}}-r{{rev.revision}}
23
24 # Create the source archives
25 - archive:
26 is_src: True
27 type: tar.gz
28 rename_dir: "{{dist_name}}"
29 name: "{{dist_name}}-src.tar.gz"
30
31 - archive:
32 is_src: True
33 type: zip
34 rename_dir: "{{dist_name}}"
35 name: "{{dist_name}}-src.zip"
36
37 - shell: ant -Dhave.version=1 dist
38
39 - shell: ant -Dhave.version=1 test
40 when: rev.is_trunk
41
42 - when: builder_java_version == 'jdk8'
43 block:
44 - shell:
45 - pip install mwconv mkgmap-pygments pygments-xslfo-formatter
46 - scripts/download/mkdoc {{rev.project}}
47
48 - archive:
49 dir: dist/mkgmap.jar
50 name: "{{dist_name}}.jar"
51
52 - archive:
53 dir: dist
54 type: [tar.gz, zip]
55 rename_dir: "{{dist_name}}"
56
57 - deploy:
58 type: mkgmap_deploy
59 build_ok: "{{builder|success}}"
173173 <replaceregex pattern="^exported$" replace="" />
174174 </tokenfilter></outputfilterchain></redirector>
175175 </exec>
176 <condition property="svn.version.build" value="${svn.version.tmp}" else="unknown">
176 <condition property="svn.version.build" value="${svn.version.tmp}" else="none">
177177 <and>
178178 <isset property="svn.version.tmp" />
179179 <equals arg1="${svnversion.result}" arg2="0" />
218218
219219 <target name="version-file" unless="have.version">
220220 <property name="project.version" value="${build.timestamp}" />
221 <property name="svn.version.build" value="unknown"/>
221 <property name="svn.version.build" value="none"/>
222222
223223 <propertyfile file="${build.classes}/mkgmap-version.properties">
224224 <entry key="svn.version" value="${svn.version.build}" />
233233 <javac srcdir="${src}" destdir="${build.classes}" encoding="utf-8" debug="true" includeantruntime="false">
234234 <include name="**/*.java" />
235235 <classpath refid="main"/>
236 <compilerarg value="-Xlint:all"/>
237 <compilerarg value="-Xlint:-serial"/>
238 <compilerarg value="-Xlint:-path"/>
236239 <exclude name="**/optional/*.java"/>
237240 </javac>
238241 </target>
6666
6767 ;--gmapi
6868 : Create a directory in the "gmapi" format required by Mac applications.
69 It can also be used for Windows programs; copy the complete directory tree into
70 {user}\AppData\Roaming\Garmin\Maps or \ProgramData\Garmin\Maps
71 and the map will be available in BaseCamp.
69 It can also be used for Windows programs; copy the complete directory tree
70 into {user}\AppData\Roaming\Garmin\Maps or \ProgramData\Garmin\Maps
71 and the map will be available to Garmin PC programs.
7272 The directory name is --family-name with extension .gmap.
7373
7474 ;-c filename
111111 Placing the mkgmap --description option after -c template.args ensures that the
112112 value is applied to gmapsupp.img.
113113 <p>
114 Different GPS devices, BaseCamp and QLandkarte handle descriptions
114 Different GPS devices and PC programs handle descriptions
115115 inconsistently. Some display the description when selecting maps or tiles,
116116 others use the family name.
117117
199199 You can search for road name Conc to find road names like 'Rue de la Concorde'.
200200 However, a search for 'Rue' will not list 'Rue de la Concorde' or 'Rue du Moulin'.
201201 It may list 'Rueben Brookins Road' if that is in the map.
202 Only MapSource shows a corresponding hint.
203202
204203 :: Another effect is that the index is smaller.
205 : See comments in the sample roadNameConfig.txt for further details.
204 : See comments in the example roadNameConfig.txt for further details.
206205
207206 ;--mdr7-excl=name[,name...]
208207 : Specify words which should be omitted from the road index.
327326 ;--overview-levels=level:resolution[,level:resolution...]
328327 : Like levels, specifies additional levels that are to be written to the
329328 overview map. Counting of the levels should continue. Up to 8 additional
330 levels may be specified, but the lowest usable resolution with MapSource
331 seems to be 11. The hard coded default is empty.
329 levels may be specified.
330 The hard coded default is empty.
332331 : See also option --overview-dem-dist.
333332
334333 ;--remove-ovm-work-files
361360 you may want to use a slightly different set of rules for a map of
362361 a whole continent. The tags given will be prefixed with "mkgmap:option:".
363362 If no value is provided the default "true" is used.
364 This option allows to use rules like
365 mkgmap:option:light=true & landuse=farmland {remove landuse}
366 Example: -- style-option=light;routing=car
367 will add the tags mkgmap:option:light=true and mkgmap:option:routing=car
363 : Example: --style-option=light;routing=car
364 : will add the tags mkgmap:option:light=true and mkgmap:option:routing=car
368365 to each element before style processing happens.
366 This can then be used in rules like:
367 : landuse=farmland & mkgmap:option:light=true {delete landuse}
369368
370369 ;--list-styles
371370 : List the available styles. If this option is preceded by a style-file
411410 : The version of the product. Default value is 100 which means version 1.00.
412411
413412 ;--series-name=name
414 : This name will be displayed in BaseCamp in the map selection
413 : This name will be displayed by Garmin PC programs in the map selection
415414 drop-down. The default is "OSM map".
416415
417416 ;--area-name=name
465464
466465 ;--polygon-size-limits=resolution:value[,resolution:value...]
467466 : Allows you to specify different min-size-polygon values for each resolution.
468 Sample:
467 Example:
469468 :: --polygon-size-limits="24:12, 18:10, 16:8, 14:4, 12:2, 11:0"
470469 : If a resolution is not given, mkgmap uses the value for the next higher
471 one. For the given sample, resolutions 19 to 24 will use value 12,
470 one. For the given example, resolutions 19 to 24 will use value 12,
472471 resolution 17 and 18 will use 10, and so on.
473472 Value 0 means to not apply the size filter.
474473 Note that in resolution 24 the filter is not used.
678677 If no value or * is specified for value then presence of the key alone will
679678 cause the dead end check to be skipped. The default is --dead-ends=fixme,FIXME.
680679
681 ;--add-pois-to-lines
682 : Generate POIs for lines. For each line (must not be closed) POIs are
680 ;--add-pois-to-lines[=all|start|end|mid|other]
681 : Generate POIs for lines. The option expects a comma separated list that
682 specifies the positions for which are POI should be generated. The default is
683 to create all possible POI. For each line (must not be closed) POIs are
683684 created at several points of the line. Each POI is tagged with the
684685 same tags like the line and additional tags added by mkgmap:
685686 mkgmap:line2poi=true and tag mkgmap:line2poitype having
920921 processed POI must be within for it to be considered to be nearby and
921922 hence trigger the action part of the rule.
922923
923 : The third part of the rule is the action part and provides three options:
924 : The third part of the rule is the action part and provides two options:
924925
925926 :: delete-poi - the POIS are considered to be duplicates and the
926927 duplicate is deleted. This is the default.
283283
284284 |is_in(*tag*,*value*,*method*) | x | x | |
285285 +true+ if the element is in polygon(s) having the specified *tag*=*value* according to the *method*, +false+ otherwise.
286 *value* can be \'\*' which matches any polgon having *tag*.
286287 The methods available depend on the Style section:
287288
288289 polygons:
305306 The method to match an outside line (*none*) allows part to be on the edge,
306307 likewise, the method to match an inside line (*all*) allows part to be on the edge.
307308 Compared to *all*, the method *all_in_or_on* additionally matches lines which are only on the edge of the polygon.
309
310 |is_drive_on_left() | | x | |
311 +true+ the element is in a drive_on_left country. +false+ the element is not a drive_on_left country or the information given
312 in the tags mkgmap:admin_level2 and mkgmap:country could not be used to retrieve the information.
308313
309314 |====
310315
33 It contains everything you need to write a TYP file description that can be compiled, showing exactly how
44 you can get the different colour effects that are available.
55
6 Although you can edit these files by hand, it is very much more convenient to use one of the graphical editors that are available.
6 The file can be in written in almost any character-set that allows basic ascii characters and can have Strings for any language that can be encoded in that character-set.
7 The mkgmap .txt TYP processor looks at the start of the file for a unicode BOM or a header line of the form:
8 ; -*- coding: charset -*-
9
10 to determine the encoding, and then will convert those Strings that can be represented in the target .IMG code-page and ignore the ones that can't.
11
12 It is safest to edit multi-lingual TYP files (eg. resources/mapnik.txt) with a good text editor;
13 if nessessary, using a graphical editor to manipulate the icons, then cutting the result back into the TYP .txt file.
14
15 For your own TYP file it might be convenient to use one of the graphical editors that are available.
16 However, beware that some of these editors don't preserve the full contents of the file
17 and might change the character representations of the Strings to those in a different character
18 set/encoding.
19 Also they make assumptions about the chars used to represent colours in icons and might change them.
720
821 These produce file formats that differ from each other and have variations to the specification that is presented here. These variations will be supported as they are discovered as long as they do not conflict with each other, but are not listed here for clarity. In particular the files produced by [http://www.pinns.co.uk/osm/ostyp.html TYPWiz] and [http://opheliat.free.fr/michel40/ TYPViewer] are supported.
922
3447 :The level is a number starting at 1.
3548
3649 Example:
37 <pre>
38 [_drawOrder]
39 Type=0x032,1
40 Type=0x10101,2
41 Type=0x002,3
42 Type=0x003,3
43 Type=0x007,3
44 Type=0x009,3
45 Type=0x00c,3
46 Type=0x00e,3
47 Type=0x010,3
48 Type=0x012,3
49 Type=0x015,3
50 Type=0x01b,3
51 ...
52 [end]
53 </pre>
50 [_drawOrder]
51 Type=0x032,1
52 Type=0x10101,2
53 Type=0x002,3
54 Type=0x003,3
55 Type=0x007,3
56 Type=0x009,3
57 Type=0x00c,3
58 Type=0x00e,3
59 Type=0x010,3
60 Type=0x012,3
61 Type=0x015,3
62 Type=0x01b,3
63 ...
64 [end]
65
5466 If a polygon type is not listed in this section, then it will not be displayed at all. If it is then it will be styled according to a definition in a [_polygon] section later in this file, or in the default Garmin style if these is no such definition.
5567
5668 == Element sections ==
89101 :The object type number - the kind of element that it is. If the number is greater than 0xff then it will be treated as a type and subtype combination. Eg 0x2305 is type 0x23 with subtype 0x5.
90102
91103 ;String=#,xxx
92 :Defines a label that will be attached to the element. The first part is a language number, the second the actual string. You can use String1, String2 for compatibility, but just String will do for the mkgmap compiler.
104 ;String=xxx
105 :Defines a label that will be attached to the element.
106 The (optional) first part is a language number, the next is the actual string.
107 You can use String1, String2 for compatibility, but just String will do for the mkgmap compiler.
108 String=Parking Lot
93109 String=0x04,Parking
94110 String=0x03,Parkeerplaats
111 :If the language number isn't specified it defaults to 0x00 which is the generic language.
112 This string is used if there isn't a definition for the currently selected language;
113 If there isn't a default the device shows the first string in the list.
114 The language numbers are defined [https://wiki.openstreetmap.org/wiki/Mkgmap/help/typ_compile#Appendix here].
95115
96116 ;FontStyle=xxx
97117 : xxx can be one of NoLabel, SmallFont, NormalFont, LargeFont.
285305 <p>
286306 An alternate notation is supported where the transparency value is appended to the line in the form "alpha=13", this is a transparency value that goes from 0 to 15, with 15 being completely transparent. As such it works the opposite way to the alpha value of the normal RGBa values in the previous example. The required conversions are made by mkgmap which ever one you use.
287307
288 * Image with up to 16 million different colours
308 * Image with up to 16 million different colours.
289309 By setting the number of colours in the XPM to zero you can specify a
290310 different kind of image, where the colour for each pixel is specified
291311 separately.
4242 @SuppressWarnings({"UnusedDeclaration"})
4343 public class MKGMapTask extends Task {
4444
45 private final ArrayList<Path> paths = new ArrayList<Path>();
45 private final ArrayList<Path> paths = new ArrayList<>();
4646 private String configFile;
4747
4848 public void addPath(Path path) {
5454 }
5555
5656 public void execute() {
57 List<String> args = new ArrayList<String>();
57 List<String> args = new ArrayList<>();
5858
5959 try {
6060 CommandArgsReader argsReader = new CommandArgsReader(new Main());
6363 RuleBasedCollator col = (RuleBasedCollator) Collator.getInstance();
6464
6565 charset = Charset.forName(charsetName);
66 if (charsetName.equalsIgnoreCase("utf-8"))
66 if ("utf-8".equalsIgnoreCase(charsetName))
6767 isUnicode = true;
6868 decoder = charset.newDecoder();
6969
10081008 <variant>SJ</variant>
10091009 <variant>SJM</variant>
10101010 </country>
1011 <country name="Swaziland" abr="SWZ" driveOnLeft="true">
1011 <country name="Kingdom of Eswatini" abr="SWZ" driveOnLeft="true">
10121012 <variant>SZ</variant>
10131013 <variant>SWZ</variant>
10141014 <variant>Swatini</variant>
1015 <variant>Eswatini</variant>
1016 <variant>Swaziland</variant>
10151017 </country>
10161018 <country name="Sverige" abr="SWE" streetBeforeHousenumber="true" postalcodeBeforeCity="true">
10171019 <variant>Sweden</variant>
6767 Create a directory in the "gmapi" format required by Mac applications. It
6868 can also be used for Windows programs; copy the complete directory tree
6969 into {user}\AppData\Roaming\Garmin\Maps or \ProgramData\Garmin\Maps and the
70 map will be available in BaseCamp. The directory name is --family-name with
71 extension .gmap.
70 map will be available to Garmin PC programs. The directory name is
71 --family-name with extension .gmap.
7272
7373 -c filename
7474 --read-config=filename
110110 from the mkgmap command line. Placing the mkgmap --description option after
111111 -c template.args ensures that the value is applied to gmapsupp.img.
112112
113 Different GPS devices, BaseCamp and QLandkarte handle descriptions
114 inconsistently. Some display the description when selecting maps or tiles,
115 others use the family name.
113 Different GPS devices and PC programs handle descriptions inconsistently.
114 Some display the description when selecting maps or tiles, others use the
115 family name.
116116
117117 --country-name=name
118118 Set the map's country name. The default is "COUNTRY".
196196 name. You can search for road name Conc to find road names like 'Rue de
197197 la Concorde'. However, a search for 'Rue' will not list 'Rue de la
198198 Concorde' or 'Rue du Moulin'. It may list 'Rueben Brookins Road' if
199 that is in the map. Only MapSource shows a corresponding hint.
199 that is in the map.
200200
201201 Another effect is that the index is smaller.
202 See comments in the sample roadNameConfig.txt for further details.
202 See comments in the example roadNameConfig.txt for further details.
203203
204204 --mdr7-excl=name[,name...]
205205 Specify words which should be omitted from the road index. It was added
321321 --overview-levels=level:resolution[,level:resolution...]
322322 Like levels, specifies additional levels that are to be written to the
323323 overview map. Counting of the levels should continue. Up to 8 additional
324 levels may be specified, but the lowest usable resolution with MapSource
325 seems to be 11. The hard coded default is empty.
324 levels may be specified. The hard coded default is empty.
326325 See also option --overview-dem-dist.
327326
328327 --remove-ovm-work-files
354353 The intended use is to make a single style more flexible, e.g. you may want
355354 to use a slightly different set of rules for a map of a whole continent.
356355 The tags given will be prefixed with "mkgmap:option:". If no value is
357 provided the default "true" is used. This option allows to use rules like
358 mkgmap:option:light=true & landuse=farmland {remove landuse} Example: --
359 style-option=light;routing=car will add the tags mkgmap:option:light=true
360 and mkgmap:option:routing=car to each element before style processing
361 happens.
356 provided the default "true" is used.
357 Example: --style-option=light;routing=car
358 will add the tags mkgmap:option:light=true and mkgmap:option:routing=car to
359 each element before style processing happens. This can then be used in
360 rules like:
361 landuse=farmland & mkgmap:option:light=true {delete landuse}
362362
363363 --list-styles
364364 List the available styles. If this option is preceded by a style-file
401401 The version of the product. Default value is 100 which means version 1.00.
402402
403403 --series-name=name
404 This name will be displayed in BaseCamp in the map selection drop-down. The
405 default is "OSM map".
404 This name will be displayed by Garmin PC programs in the map selection
405 drop-down. The default is "OSM map".
406406
407407 --area-name=name
408408 Area name is displayed on Garmin units (or at least on eTrex) as the second
453453
454454 --polygon-size-limits=resolution:value[,resolution:value...]
455455 Allows you to specify different min-size-polygon values for each
456 resolution. Sample:
456 resolution. Example:
457457 --polygon-size-limits="24:12, 18:10, 16:8, 14:4, 12:2, 11:0"
458458 If a resolution is not given, mkgmap uses the value for the next higher
459 one. For the given sample, resolutions 19 to 24 will use value 12,
459 one. For the given example, resolutions 19 to 24 will use value 12,
460460 resolution 17 and 18 will use 10, and so on. Value 0 means to not apply the
461461 size filter. Note that in resolution 24 the filter is not used. The
462462 following options are equivalent:
661661 cause the dead end check to be skipped. The default is
662662 --dead-ends=fixme,FIXME.
663663
664 --add-pois-to-lines
665 Generate POIs for lines. For each line (must not be closed) POIs are
664 --add-pois-to-lines[=all|start|end|mid|other]
665 Generate POIs for lines. The option expects a comma separated list that
666 specifies the positions for which are POI should be generated. The default
667 is to create all possible POI. For each line (must not be closed) POIs are
666668 created at several points of the line. Each POI is tagged with the same
667669 tags like the line and additional tags added by mkgmap:
668670 mkgmap:line2poi=true and tag mkgmap:line2poitype having the following
898900 processed POI must be within for it to be considered to be nearby and hence
899901 trigger the action part of the rule.
900902
901 The third part of the rule is the action part and provides three options:
903 The third part of the rule is the action part and provides two options:
902904
903905 delete-poi - the POIS are considered to be duplicates and the duplicate
904906 is deleted. This is the default.
0 svn.version: 4506
1 build.timestamp: 2020-05-28T10:18:05+0100
0 svn.version: 4555
1 build.timestamp: 2020-06-22T17:00:46+0100
3636 #make-opposite-cycleways
3737 #order-by-decreasing-area
3838 #name-tag-list=name:en,int_name,name,place_name,loc_name
39 #nearby-poi-rules=0x4a00:30:delete-poi,0x6605:30:delete-poi
40 # above deletes multiple POI that the default style might generate from picnic_table and bench
118118 aeroway=helipad [0x5904 resolution 23]
119119 aeroway=terminal [0x2f04 resolution 24]
120120
121 amenity=atm [0x2f06 resolution 24]
121 amenity=atm [0x2f06 resolution 24 continue]
122122 amenity=arts_centre [0x2c04 resolution 24]
123123 amenity=bank [0x2f06 resolution 24]
124 amenity=bar [0x2d02 resolution 24]
124 amenity=bar [0x2d02 resolution 24 continue]
125 amenity=bench [0x6605 resolution 24 continue]
125126 amenity=biergarten [0x2d02 resolution 24]
126127 amenity=border_control | barrier=border_control [0x3006 resolution 20]
127128 amenity=bus_station [0x2f08 resolution 23]
128 amenity=cafe [0x2a0e resolution 24]
129 amenity=cafe {delete cuisine} [0x2a0e resolution 24 continue with_actions]
129130 amenity=car_club [0x2f0d resolution 24]
130131 amenity=car_rental [0x2f02 resolution 24]
131132 amenity=car_sharing [0x2f02 resolution 24]
156157 amenity=library [0x2c03 resolution 24]
157158 amenity=nightclub [0x2d02 resolution 24]
158159 amenity=nursing_home [0x2f14 resolution 24]
159 amenity=parking [0x2f0b resolution 24 default_name 'Parking']
160 (amenity=parking | amenity=parking_entrance) & access!=private & access!=no {add name='${access} parking'| 'Parking'} [0x2f0b resolution 24]
160161 amenity=pharmacy [0x2e05 resolution 24]
161162 amenity=place_of_worship [0x2c0b resolution 24]
162163 amenity=police [0x3001 resolution 24]
171172 # amenity=shelter is ambiguous; when possible, consider using other tags:
172173 # tourism=lean_to or tourism=picnic_site
173174 # shelter=yes on highway=bus_stop or highway=tram_stop or railway=halt
174 amenity=shelter & shelter_type!=public_transport [0x2b06 resolution 24 default_name 'Shelter']
175 amenity=shelter & shelter_type=basic_hut [0x2b06 resolution 24 default_name 'Shelter']
175176 # amenity=supermarket is superceded by shop=supermarket
176177 amenity=supermarket [0x2e02 resolution 24]
177178 amenity=taxi [0x2f17 resolution 24]
189190 healthcare=hospital | amenity=hospital | amenity=clinic [0x3002 resolution 22]
190191 healthcare=* | amenity=dentist | amenity=doctors [0x3002 resolution 24]
191192
192 highway=motorway_junction [0x2100 resolution 24]
193
194 highway=services & mkgmap:area2poi!=true [0x210f resolution 24 default_name 'Services']
193 highway=motorway_junction & exit:road_ref=* {add exit:to='${exit_to}' | '${destination}'} [0x2000 resolution 24]
194
195 highway=services {add exit:facility="0x02,I,0x47,Features"} [0x230f resolution 20 default_name 'Services']
195196
196197 historic=museum [0x2c02 resolution 24]
197198 historic=archaeological_site | historic=ruins [0x2c02 resolution 24]
201202 leisure=garden & name=* [0x2c06 resolution 24]
202203 leisure=golf_course [0x2d05 resolution 24]
203204 leisure=ice_rink [0x2d08 resolution 24]
204 leisure=marina [0x4300 resolution 24]
205 leisure=nature_reserve & name=* [0x6612 resolution 24]
205 leisure=marina [0x2f09 resolution 24]
206 leisure=nature_reserve [0x6612 resolution 24]
206207 leisure=park [0x2c06 resolution 24]
207 leisure=pitch {name '${name} (${sport})' | '${sport}'} [0x2c08 resolution 24]
208 leisure=pitch & (name=* | sport=*) {name '${name} (${sport})' | '${sport}'} [0x2c08 resolution 24]
208209 leisure=playground [0x2c06 resolution 24 default_name 'Playground']
209210 leisure=recreation_ground [0x2c08 resolution 24 default_name 'Rec.']
210211 leisure=sports_center | leisure=sports_centre {name '${name} (${sport})' | '${sport}'} [0x2d0a resolution 24]
211212 leisure=stadium {name '${name} (${sport})' | '${sport}'} [0x2c08 resolution 24]
212 leisure=swimming_pool [0x2d09 resolution 24]
213 (leisure=swimming_pool | amenity=swimming_pool) & access!=private & access!=no [0x2d09 resolution 24]
213214 leisure=track {name '${name} (${sport})' | '${sport}'} [0x2c08 resolution 24]
214215 leisure=water_park [0x2d09 resolution 24]
215216
218219 # Edge 705 displays 0x650a,0x6511,0x6512,0x6513,0x6603,0x6614 as hollow white circles, no menu
219220 natural=cave_entrance [0x6601 resolution 24]
220221 natural=cape [0x6606 resolution 24]
221 natural=cliff [0x6607 resolution 24]
222 natural=cliff & name=* [0x6607 resolution 24]
222223 natural=peak {name '${name|def:}${ele|height:m=>ft|def:}'} [0x6616 resolution 24]
223224 natural=rock [0x6614 resolution 24]
224225 natural=volcano [0x2c0c resolution 24]
226 natural=bay & name=* [0x6503 resolution 20]
225227
226228 railway=station [0x2f08 resolution 22]
227 (public_transport=platform & rail=yes) | railway=halt [0x2f08 resolution 23]
228 public_transport=platform | highway=bus_stop | railway=tram_stop [0x2f17 resolution 24]
229 railway=halt [0x2f08 resolution 23]
230 # if option --add-pois-to-lines in effect, add just 1 POI
231 (railway=platform | (public_transport=platform & railway=*)) & (mkgmap:line2poi!=true | mkgmap:line2poitype=mid) [0x2f08 resolution 23]
232 public_transport=platform & (mkgmap:line2poi!=true | mkgmap:line2poitype=mid) [0x2f17 resolution 24]
233 highway=bus_stop | railway=tram_stop [0x2f17 resolution 24]
234
235 shop=* & name!=* & shop!=yes & shop!=no & shop!=none & shop!=vacant {set name='${shop|subst:"_=> "}'}
236 # Uncomment the following lines to enable these extra POI that some devices support
237 #shop=convenience [0x2e0e resolution 24]
238 #shop=florist [0x2e0f resolution 24]
239 #shop=gift | shop=art | shop=antiques [0x2e10 resolution 24]
240 #shop=music [0x2e11 resolution 24]
241 #shop=sports [0x2e12 resolution 24]
242 #shop=wine | shop=alcohol [0x2e13 resolution 24]
243 #shop=books [0x2e14 resolution 24]
244 # to here
229245
230246 shop=bakers [0x2e02 resolution 24]
231247 shop=bakery [0x2e02 resolution 24]
254270 shop=hairdresser [0x2f10 resolution 24]
255271 shop=mall [0x2e04 resolution 24]
256272 shop=organic [0x2e0a resolution 24]
273 shop=outdoor [0x2e08 resolution 24]
257274 shop=shoes [0x2e07 resolution 24]
258275 shop=supermarket [0x2e02 resolution 24]
259276 shop=tires [0x2f07 resolution 24]
260277 shop=tyres [0x2f07 resolution 24]
261 shop=* & shop!=no & shop!=none {add name='${shop|subst:"_=> "}'} [0x2e0c resolution 24]
278 shop=* & name=* [0x2e0c resolution 24]
262279
263280 sport=9pin [0x2d07 resolution 24]
264281 sport=10pin | leisure=bowling_alley [0x2d07 resolution 24]
274291 tourism=artwork [0x2c04 resolution 24]
275292 tourism=aquarium [0x2c07 resolution 24]
276293 tourism=bed_and_breakfast [0x2b02 resolution 24]
277 tourism=camp_site [0x2b05 resolution 24]
278 tourism=caravan_site [0x2b05 resolution 24]
294 # NB: different devices use different codes for camp/caravan_site, have both!
295 tourism=camp_site [0x2b03 resolution 24] [0x2b05 resolution 24]
296 tourism=caravan_site [0x2b03 resolution 24] [0x2b05 resolution 24]
279297 tourism=chalet [0x2b02 resolution 24]
280298 tourism=guest_house [0x2b02 resolution 24]
281299 tourism=hostel [0x2b02 resolution 24]
282 tourism=hotel | tourism=motel [0x2b01 resolution 24]
300 # Have both hotel &| restaurant POIs
301 tourism=hotel | tourism=motel {set tmp:stopMopUp=yes} [0x2b01 resolution 24 continue with_actions]
283302 tourism=information [0x4c00 resolution 24]
284303 # tourism=lean_to replaces some uses of amenity=shelter
285304 tourism=lean_to [0x2b05 resolution 24 default_name 'lean-to']
291310 tourism=viewpoint {name '${name} - ${description}' | '${name}'} [0x2c04 resolution 24]
292311 tourism=wine_cellar [0x2c0a resolution 24]
293312 tourism=zoo [0x2c07 resolution 24]
294 tourism=* & tourism!=yes & tourism!=no {add name='${tourism|subst:"_=> "}'}
295 tourism=* & tourism!=no [0x2c0d resolution 24]
313 tourism=* & name!=* & tourism!=yes & tourism!=no {set name='${tourism|subst:"_=> "}'}
314 tourism=* & tourism!=no & tmp:stopMopUp!=yes [0x2c0d resolution 24]
296315
297316 # amenity=restaurant/fast_food/cafe/pub and shop=* can use cuisine, so have cuisine section later than where the others
298317 # should take precedence
354373 barrier=stile | barrier=kissing_gate | barrier=lift_gate | barrier=swing_gate
355374 {add name='${barrier|subst:"_=> "}'} [0x3200 resolution 24]
356375
357 landuse=basin | landuse=reservoir [0x650f resolution 24]
376 landuse=basin [0x6603 resolution 24]
377 landuse=reservoir | water=reservoir [0x650f resolution 22]
358378
359379 natural=beach [0x6604 resolution 24]
360380 natural=glacier [0x650a resolution 24]
361381 natural=spring [0x6511 resolution 24]
362382 natural=stream [0x6512 resolution 24]
363383 natural=water & (water=canal | water=lock) & name=* [0x6505 resolution 24]
384 (water=lake | water=pond) & name=* [0x650d resolution 24]
364385 natural=water & name=* [0x6603 resolution 24]
365386 natural=waterfall | waterway=waterfall [0x6508 resolution 24]
366387 natural=wetland & name=* [0x6513 resolution 24]
1414
1515 for f in *.txt
1616 do
17 mwtext -t text $f > ../dist/doc/$f
17 mwconv -t text $f > ../dist/doc/$f
1818 done
1919
2020 # Use the actual options help file.
386386 int latHp = co.getHighPrecLat();
387387 int lonHp = co.getHighPrecLon();
388388
389 return (long)(latHp & 0xffffffffL) << 32 | (lonHp & 0xffffffffL);
389 return (latHp & 0xffffffffL) << 32 | (lonHp & 0xffffffffL);
390390 }
391391
392392 public static int numberToPointerSize(int n) {
2828 */
2929 public class Exit {
3030
31 public final static String TAG_ROAD_REF = "exit:road_ref";
32 public final static String TAG_TO = "exit:to";
33 public final static String TAG_FACILITY = "exit:facility";
31 public static final String TAG_ROAD_REF = "exit:road_ref";
32 public static final String TAG_TO = "exit:to";
33 public static final String TAG_FACILITY = "exit:facility";
3434
3535 private final Highway highway;
3636 private Label description;
37 private final List<ExitFacility> facilities = new ArrayList<ExitFacility>();
37 private final List<ExitFacility> facilities = new ArrayList<>();
3838
3939 public Exit(Highway highway) {
4040 this.highway = highway;
6060 * the bounding box of the tile
6161 * @param demPolygonMapUnits
6262 * a bounding polygon which might be smaller than the area
63 * @param pathToHGT
64 * comma separated list of directories or zip files
63 * @param pathsToHGT
64 * string with comma separated list of directories or zip files
6565 * @param pointDistances
6666 * list of distances which determine the resolution
6767 * @param outsidePolygonHeight
6969 * bounding polygon
7070 * @return a new bounding box that should be used for the TRE file
7171 */
72 public Area calc(Area area, java.awt.geom.Area demPolygonMapUnits, String pathToHGT, List<Integer> pointDistances,
72 public Area calc(Area area, java.awt.geom.Area demPolygonMapUnits, String pathsToHGT, List<Integer> pointDistances,
7373 short outsidePolygonHeight, InterpolationMethod interpolationMethod) {
7474 // HGT area is extended by EXTRA degrees in each direction
75 HGTConverter hgtConverter = new HGTConverter(pathToHGT, area, demPolygonMapUnits, EXTRA);
75 HGTConverter hgtConverter = new HGTConverter(pathsToHGT, area, demPolygonMapUnits, EXTRA);
7676 hgtConverter.setInterpolationMethod(interpolationMethod);
7777 hgtConverter.setOutsidePolygonHeight(outsidePolygonHeight);
7878
156156 // wasn't in the right form to guess
157157 throw new ExitException("Invalid character set: " + cs);
158158 }
159 } else if (cs.equals("latin1")) {
159 } else if ("latin1".equals(cs)) {
160160 return 1252;
161161 }
162162 return 0;
1414
1515 import java.util.Locale;
1616
17 import uk.me.parabola.log.Logger;
18
1917 /**
2018 * A sparse character-based transliterator that leaves most characters unchanged.
2119 *
2220 */
2321 public class SparseTransliterator implements Transliterator {
24 private static final Logger log = Logger.getLogger(SparseTransliterator.class);
2522
2623 private final boolean useNoMacron;
2724 private boolean forceUppercase;
2825
2926 public SparseTransliterator(String targetCharset) {
30 useNoMacron = (targetCharset.equals("nomacron")) ? true : false;
27 useNoMacron = "nomacron".equals(targetCharset);
3128 }
3229
3330 /**
4744 // Only macrons are modified, all other chars (including non-ascii) are left unchanged
4845 if (c == 0x101) // Unicode Character 'LATIN SMALL LETTER A WITH MACRON' (U+0101)
4946 c = 'a';
50 if (c == 0x113) // Unicode Character 'LATIN SMALL LETTER E WITH MACRON' (U+0113)
47 else if (c == 0x113) // Unicode Character 'LATIN SMALL LETTER E WITH MACRON' (U+0113)
5148 c = 'e';
52 if (c == 0x12b) // Unicode Character 'LATIN SMALL LETTER I WITH MACRON' (U+012B)
49 else if (c == 0x12b) // Unicode Character 'LATIN SMALL LETTER I WITH MACRON' (U+012B)
5350 c = 'i';
54 if (c == 0x14d) // Unicode Character 'LATIN SMALL LETTER O WITH MACRON' (U+014D)
51 else if (c == 0x14d) // Unicode Character 'LATIN SMALL LETTER O WITH MACRON' (U+014D)
5552 c = 'o';
56 if (c == 0x16b) // Unicode Character 'LATIN SMALL LETTER U WITH MACRON' (U+016B)
53 else if (c == 0x16b) // Unicode Character 'LATIN SMALL LETTER U WITH MACRON' (U+016B)
5754 c = 'u';
5855 }
5956 sb.append(c);
4848
4949 private final Region region;
5050
51 private final List<ExitPoint> exits = new ArrayList<ExitPoint>();
51 private final List<ExitPoint> exits = new ArrayList<>();
5252
5353 private Label label;
5454
1717
1818 import java.util.List;
1919
20 import uk.me.parabola.imgfmt.Utils;
2120 import uk.me.parabola.imgfmt.app.Exit;
2221 import uk.me.parabola.imgfmt.app.ImgFileWriter;
2322 import uk.me.parabola.imgfmt.app.Label;
105104 }
106105
107106 void write(ImgFileWriter writer, int POIGlobalFlags, int realofs,
108 long numCities, long numZips, long numHighways, long numExitFacilities) {
107 int cityPtrSize, int zipPtrSize, int highwayPtrSize, int exitFacilityPtrSize) {
109108 assert offset == realofs : "offset = " + offset + " realofs = " + realofs;
110109 int ptr = poiName.getOffset();
111110 if (POIGlobalFlags != getPOIFlags())
129128 writer.put3u(streetName.getOffset());
130129
131130 if (city != null)
132 writer.putNu(Utils.numberToPointerSize((int)numCities), city.getIndex());
131 writer.putNu(cityPtrSize, city.getIndex());
133132
134133 if (zip != null)
135 writer.putNu(Utils.numberToPointerSize((int)numZips), zip.getIndex());
134 writer.putNu(zipPtrSize, zip.getIndex());
136135
137136 if (complexPhoneNumber != null)
138137 {
162161 writer.put3u(val);
163162
164163 int highwayIndex = exit.getHighway().getIndex();
165 writer.putNu(Utils.numberToPointerSize((int)numHighways), highwayIndex);
164 writer.putNu(highwayPtrSize, highwayIndex);
166165
167166 if(ef != null) {
168167 int exitFacilityIndex = ef.getIndex();
169 writer.putNu(Utils.numberToPointerSize((int)numExitFacilities), exitFacilityIndex);
168 writer.putNu(exitFacilityPtrSize, exitFacilityIndex);
170169 }
171170 }
172171 }
178177 if (simpleStreetNumber.isUsed() || streetNumberName != null)
179178 b |= HAS_STREET_NUM;
180179 if (city != null)
181 b |= HAS_CITY;
180 b |= HAS_CITY;
182181 if (zip != null)
183 b |= HAS_ZIP;
182 b |= HAS_ZIP;
184183 if (simplePhoneNumber.isUsed() || complexPhoneNumber != null)
185184 b |= HAS_PHONE;
186185 if (exit != null)
213212 }
214213
215214 flag |= 0x80; // gpsmapedit asserts for this bit set
216
215
217216 return flag;
218217 }
219218
222221 *
223222 * @return Number of bytes needed by this entry
224223 */
225 int calcOffset(int ofs, int POIGlobalFlags, long numCities, long numZips, long numHighways, long numExitFacilities) {
224 int calcOffset(int ofs, int POIGlobalFlags, int cityPtrSize, int zipPtrSize, int highwayPtrSize, int exitFacilityPtrSize) {
226225 offset = ofs;
227226 int size = 3;
228227 if (exit != null) {
229228 size += 3;
230 size += Utils.numberToPointerSize((int)numHighways);
229 size += highwayPtrSize;
231230 if(!exit.getFacilities().isEmpty())
232 size += Utils.numberToPointerSize((int)numExitFacilities);
231 size += exitFacilityPtrSize;
233232 }
234233 if (POIGlobalFlags != getPOIFlags())
235234 size += 1;
243242 size += 3;
244243 if (streetName != null)
245244 size += 3;
246 if (city != null)
247 {
248 /*
249 depending on how many cities are in the LBL block we have
250 to write one to three bytes
251 */
252 size += Utils.numberToPointerSize((int)numCities);
253 }
254 if (zip != null) {
255 // depending on how many zips are in the LBL block we have to write one to three bytes
256 size += Utils.numberToPointerSize((int)numZips);
257 }
245 if (city != null)
246 size += cityPtrSize;
247 if (zip != null)
248 size += zipPtrSize;
258249 return size;
259250 }
260251
301292 while (i < number.length()) {
302293
303294 int c1 = decodeChar(number.charAt(i++));
304
305 int c2;
306 if (i < number.length()) {
307 c2 = decodeChar(number.charAt(i++));
308 } else
309 c2 = 10;
295 int c2 = (i < number.length()) ? decodeChar(number.charAt(i++)) : 10;
310296
311297 // Only 0-9 and - allowed
312298 if (c1 < 0 || c1 > 10 || c2 < 0 || c2 > 10)
2323 import java.util.Random;
2424 import java.util.TreeMap;
2525
26 import uk.me.parabola.imgfmt.Utils;
2627 import uk.me.parabola.imgfmt.app.Exit;
2728 import uk.me.parabola.imgfmt.app.ImgFileWriter;
2829 import uk.me.parabola.imgfmt.app.Label;
103104
104105 int poistart = writer.position();
105106 int poiglobalflags = placeHeader.getPOIGlobalFlags();
107 final int cityPtrSize = Utils.numberToPointerSize(cityList.size());
108 final int zipPtrSize = Utils.numberToPointerSize(postalCodes.size());
109 final int highwayPtrSize = Utils.numberToPointerSize(highways.size());
110 final int exitFacilityPtrSize = Utils.numberToPointerSize(exitFacilities.size());
106111 for (POIRecord p : pois)
107112 p.write(writer, poiglobalflags,
108 writer.position() - poistart, cityList.size(), postalCodes.size(), highways.size(), exitFacilities.size());
113 writer.position() - poistart, cityPtrSize, zipPtrSize, highwayPtrSize, exitFacilityPtrSize);
109114 placeHeader.endPOI(writer.position());
110115
111116 int numPoiIndexEntries = 0;
286291 placeHeader.setPOIGlobalFlags(poiFlags);
287292
288293 int ofs = 0;
294 final int cityPtrSize = Utils.numberToPointerSize(cityList.size());
295 final int zipPtrSize = Utils.numberToPointerSize(postalCodes.size());
296 final int highwayPtrSize = Utils.numberToPointerSize(highways.size());
297 final int exitFacilityPtrSize = Utils.numberToPointerSize(exitFacilities.size());
289298 for (POIRecord p : pois)
290 ofs += p.calcOffset(ofs, poiFlags, cityList.size(), postalCodes.size(), highways.size(), exitFacilities.size());
299 ofs += p.calcOffset(ofs, poiFlags, cityPtrSize, zipPtrSize, highwayPtrSize, exitFacilityPtrSize);
291300 }
292301
293302 /**
5656 public static final boolean WITH_EXT_TYPE_DATA = true;
5757 public static final boolean WITHOUT_EXT_TYPE_DATA = false;
5858
59 private final Deque<Closeable> toClose = new ArrayDeque<Closeable>();
59 private final Deque<Closeable> toClose = new ArrayDeque<>();
6060
6161 public MapReader(String filename) throws FileNotFoundException {
6262 FileSystem fs = ImgFS.openFs(filename);
111111 * @param level The level, lower numbers are the most detailed.
112112 */
113113 public List<Point> pointsForLevel(int level, boolean withExtType) {
114 List<Point> points = new ArrayList<Point>();
114 List<Point> points = new ArrayList<>();
115115
116116 Subdivision[] subdivisions = treFile.subdivForLevel(level);
117117 for (Subdivision sd : subdivisions) {
134134 * @param level The level, lower numbers are the most detailed.
135135 */
136136 public List<Polyline> linesForLevel(int level) {
137 ArrayList<Polyline> lines = new ArrayList<Polyline>();
137 ArrayList<Polyline> lines = new ArrayList<>();
138138
139139 Subdivision[] subdivisions = treFile.subdivForLevel(level);
140140 for (Subdivision div : subdivisions) {
147147
148148
149149 public List<Polygon> shapesForLevel(int level, boolean witExtTypeData) {
150 ArrayList<Polygon> shapes = new ArrayList<Polygon>();
150 ArrayList<Polygon> shapes = new ArrayList<>();
151151
152152 Subdivision[] subdivisions = treFile.subdivForLevel(level);
153153 for (Subdivision div : subdivisions) {
2929 * @author Steve Ratcliffe
3030 */
3131 public class Mdr10 extends MdrMapSection {
32 // The maximum group number. Note that this is 1 based, not 0 based.
32 /** The maximum group number. Note that this is 1 based, not 0 based. */
3333 private static final int MAX_GROUP_NUMBER = MdrUtils.MAX_GROUP;
3434
35 @SuppressWarnings({"unchecked"})
36 private List<Mdr10Record>[] poiTypes = new ArrayList[MAX_GROUP_NUMBER+1];
35 private List<List<Mdr10Record>> poiTypes = new ArrayList<>();
3736
3837 private int numberOfPois;
3938
4039 public Mdr10(MdrConfig config) {
4140 setConfig(config);
42
43 for (int i = 1; i <= MAX_GROUP_NUMBER; i++) {
44 poiTypes[i] = new ArrayList<>();
45 }
41 while (poiTypes.size() <= MAX_GROUP_NUMBER)
42 poiTypes.add(new ArrayList<>());
4643 }
4744
4845 public void addPoiType(Mdr11Record poi) {
5956 t.setSubtype(MdrUtils.getSubtypeFromFullType(fullType));
6057 }
6158 t.setMdr11ref(poi);
62 poiTypes[group].add(t);
59 poiTypes.get(group).add(t);
6360 }
6461
6562 public void writeSectData(ImgFileWriter writer) {
10299 public Map<Integer, Integer> getGroupSizes() {
103100 Map<Integer, Integer> m = new LinkedHashMap<>();
104101
105 for (int i = 1; i <= MAX_GROUP_NUMBER; i++) {
106 List<Mdr10Record> poiGroup = poiTypes[i];
107 if (!poiGroup.isEmpty())
108 m.put(i, poiGroup.size());
102 for (int group = 1; group <= MAX_GROUP_NUMBER; group++) {
103 int size = poiTypes.get(group).size();
104 if (size > 0)
105 m.put(group, size);
109106 }
110107 return m;
111108 }
362362
363363
364364 public List<Mdr7Record> getStreets() {
365 return Collections.unmodifiableList((ArrayList<Mdr7Record>) allStreets);
365 return Collections.unmodifiableList(allStreets);
366366 }
367367
368368 public List<Mdr7Record> getSortedStreets() {
127127 return Collections.unmodifiableSet(mdr7Excl);
128128 }
129129
130 public void setMdr7Excl(String exclList) {
131 mdr7Excl = stringToSet(exclList);
132 }
133
134130 public Set<String> getMdr7Del() {
135131 return Collections.unmodifiableSet(mdr7Del);
136 }
137
138 public void setMdr7Del(String delList) {
139 mdr7Del = stringToSet(delList);
140 }
141
142 private static Set<String> stringToSet (String opt) {
143 Set<String> set;
144
145 if (opt == null)
146 set = Collections.emptySet();
147 else {
148 if (opt.startsWith("'") || opt.startsWith("\""))
149 opt = opt.substring(1);
150 if (opt.endsWith("'") || opt.endsWith("\""))
151 opt = opt.substring(0, opt.length() - 1);
152 List<String> list = Arrays.asList(opt.split(","));
153 set = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
154 for (String s : list) {
155 set.add(s.trim());
156 }
157 }
158 return set;
159132 }
160133
161134 /**
163136 * @param opt the option string given. Expected is a comma separated list,eg
164137 * "0x2800" or "0x2800, 0x6400-0x661f"
165138 */
166 public void setPoiExcl (String opt) {
167 if (opt == null)
168 return;
169 poiExclTypes = new TreeSet<>();
170
171 String[] opts = opt.split(",");
172 for (String range : opts) {
173 if (range.contains("-")) {
174 String[] ranges = range.split("-");
175 if (ranges.length != 2)
176 throw new IllegalArgumentException("invalid range in option " + range);
177 genTypesForRange(poiExclTypes, ranges[0], ranges[1]);
178 } else {
179 genTypesForRange(poiExclTypes, range,range);
139 public void setPoiExcl (List<String> opts) {
140 if (opts.isEmpty())
141 poiExclTypes = Collections.emptyNavigableSet();
142 else {
143 poiExclTypes = new TreeSet<>();
144 for (String range : opts) {
145 if (range.contains("-")) {
146 String[] ranges = range.split("-");
147 if (ranges.length != 2)
148 throw new IllegalArgumentException("invalid range in option " + range);
149 genTypesForRange(poiExclTypes, ranges[0], ranges[1]);
150 } else {
151 genTypesForRange(poiExclTypes, range,range);
152 }
180153 }
181154 }
182155 }
221194
222195 public void setIndexOptions(CommandArgs args) {
223196 setSplitName(args.get("split-name-index", false));
224 setMdr7Excl(args.get("mdr7-excl", null));
225 setMdr7Del(args.get("mdr7-del", null));
226 setPoiExcl(args.get("poi-excl-index", null));
197 mdr7Excl = args.argToSet("mdr7-excl", null);
198 mdr7Del = args.argToSet("mdr7-del", null);
199 setPoiExcl(args.argToList("poi-excl-index", null));
227200 }
228201 }
9797 private static final short TKM_UNPAVED = TagDict.getInstance().xlate("mkgmap:unpaved");
9898 private static final short TKM_FERRY = TagDict.getInstance().xlate("mkgmap:ferry");
9999 private static final short TKM_THROUGHROUTE = TagDict.getInstance().xlate("mkgmap:throughroute");
100 private static final short TKM_JUNCTION = TagDict.getInstance().xlate("junction");
101 private static final short TKM_ONEWAY = TagDict.getInstance().xlate("oneway");
100 private static final short TK_JUNCTION = TagDict.getInstance().xlate("junction");
101 private static final short TK_ONEWAY = TagDict.getInstance().xlate("oneway");
102102
103103 public static byte evalRouteTags(Element el) {
104104 byte routeFlags = 0;
120120 routeFlags |= R_THROUGHROUTE;
121121
122122 // tags without the mkgmap: prefix
123 if ("roundabout".equals(el.getTag(TKM_JUNCTION)))
123 if ("roundabout".equals(el.getTag(TK_JUNCTION)))
124124 routeFlags |= R_ROUNDABOUT;
125 if (el.tagIsLikeYes(TKM_ONEWAY))
125 if (el.tagIsLikeYes(TK_ONEWAY))
126126 routeFlags |= R_ONEWAY;
127127
128128 return routeFlags;
526526 if (arc.getLengthInMeter() <= 0.0001)
527527 ignoreAngle = true;
528528 Integer angle = Math.round(getAngle(fromArc, arc));
529 List<RouteArc> list = angleMap.get(angle);
530 if (list == null){
531 list = new ArrayList<>();
532 angleMap.put(angle, list);
533 }
534 list.add(arc);
529 angleMap.computeIfAbsent(angle, k-> new ArrayList<>()).add(arc);
535530 }
536531
537532 // find the group that fits best
316316
317317 // We write this big endian
318318 if(log.isDebugEnabled())
319 log.debug("val is", Integer.toHexString((int)val));
319 log.debug("val is", Integer.toHexString(val));
320320 writer.put1u(val >> 8);
321321 writer.put1u(val & 0xff);
322322 }
185185
186186 private Byte morseLetter;
187187
188 private final int DISTANCE_FLAG_METRIC_INDEX = 0;
189 private final int DISTANCE_FLAG_TENTHS_INDEX = 1;
188 private static final int DISTANCE_FLAG_METRIC_INDEX = 0;
189 private static final int DISTANCE_FLAG_TENTHS_INDEX = 1;
190190
191191 private static final byte FLAGS0_RACON_BIT = (1);
192192 private static final byte FLAGS0_NOTE_BIT = (1 << 1);
220220 protected byte[] getExtTypeExtraBytes(MapObject mapObject) {
221221 try {
222222 return encodeExtraBytes(mapObject);
223 }
224 catch (Exception e) {
223 } catch (Exception e) {
225224 log.error(objectName + " (" + e + ")");
226225 return null;
227226 }
276275 lt = "interrupted quick";
277276 else if(parts[0].startsWith("IVQ "))
278277 lt = "interrupted very quick";
279 else if(parts[0].startsWith("UQ"))
278 else if(parts[0].startsWith("IUQ"))
280279 lt = "interrupted ultra quick";
281280 else if(parts[0].startsWith("AI"))
282281 lt = "alternating";
283282 else if(parts[0].startsWith("Mo")) {
284 if(parts[0].indexOf("(") == 2)
283 if(parts[0].indexOf('(') == 2)
285284 lt = parts[0].substring(3, 4);
286285 else if(parts.length > 1 && parts[i].startsWith("(")) {
287286 lt = parts[i].substring(1, 2);
290289 }
291290
292291 String group2 = null;
293 if(!parts[0].startsWith("Mo") && parts[0].indexOf("(") > 0) {
294 group2 = parts[0].substring(parts[0].indexOf("(") + 1, parts[0].length() - 1);
292 if(!parts[0].startsWith("Mo") && parts[0].indexOf('(') > 0) {
293 group2 = parts[0].substring(parts[0].indexOf('(') + 1, parts[0].length() - 1);
295294 }
296295 else if(i < parts.length && parts[i].startsWith("(")) {
297296 // should be (group)
334333 }
335334
336335 String period = null;
337 if((i + 1) < parts.length && parts[i+1].equals("s")) {
336 if((i + 1) < parts.length && "s".equals(parts[i+1])) {
338337 // period
339338 period = parts[i];
340339 i += 2;
341340 }
342341
343342 String height = null;
344 if((i + 1) < parts.length && parts[i+1].equals("m")) {
343 if((i + 1) < parts.length && "m".equals(parts[i+1])) {
345344 // height
346345 height = parts[i];
347346 i += 2;
348347 }
349348
350349 String range = null;
351 if((i + 1) < parts.length && parts[i+1].equals("M")) {
350 if((i + 1) < parts.length && "M".equals(parts[i+1])) {
352351 // range
353352 range = parts[i];
354353 i += 2;
381380 String[] parts = desc.split(":");
382381 if(parts.length == 4) {
383382 colour = parts[0];
384 if (parts[1].equalsIgnoreCase("shore") || parts[2].equalsIgnoreCase("shore")) {
383 if ("shore".equalsIgnoreCase(parts[1]) || "shore".equalsIgnoreCase(parts[2])) {
385384 log.error(objectName + ": shore is no valid sector bound, please annotate a numeric value");
386385 } else {
387386 sectorStart = Double.valueOf(parts[1]).intValue();
399398 else return sectorStart > other.sectorStart? 1: -1;
400399 }
401400 }
402 List<SeamarkLight> lights = new ArrayList<SeamarkLight>();
401 List<SeamarkLight> lights = new ArrayList<>();
403402 // create a SeamarkLight for each light
404403 for(int n = 1; n <= 100; ++n) {
405404 String desc = attributes.get("seamark:light:" + n);
412411 lights.sort(null);
413412 // generate the descriptor string - each light is
414413 // specified as color,range,sectorStartAngle
415 String light = null;
414 StringBuilder light = null;
416415 for(int i = 0; i < lights.size(); ++i) {
417416 SeamarkLight sml = lights.get(i);
418417 if(light == null)
419 light = "";
418 light = new StringBuilder();
420419 else
421 light += "/";
422 light += sml.colour + "," + sml.range + "," + sml.sectorStart;
423 //light += sml.colour + "," + sml.range/10 + "." + sml.range%10 + "," + sml.sectorStart;
420 light.append('/');
421 light.append(sml.colour).append(',').append(sml.range).append(',').append(sml.sectorStart);
424422 if((i + 1) < lights.size()) {
425423 if(sml.sectorEnd != lights.get(i + 1).sectorStart) {
426424 // gap between lit sectors
427 light += "/unlit,0," + sml.sectorEnd;
425 light.append("/unlit,0,").append(sml.sectorEnd);
428426 }
429427 }
430428 else if(sml.sectorEnd != lights.get(0).sectorStart) {
431429 // gap to end
432 light += "/unlit,0," + sml.sectorEnd;
430 light.append("/unlit,0,").append(sml.sectorEnd);
433431 }
434432 }
435433 if(light != null) {
436 //System.err.println(light);
437 attributes.put("light", light);
434 attributes.put("light", light.toString());
438435 if(attributes.get("seamark:light:character") == null)
439436 attributes.put("type", "fixed");
440437 }
443440 String sequence = attributes.get("seamark:light:sequence");
444441
445442 if(sequence != null) {
446 StringBuffer periods = new StringBuffer();
447 StringBuffer eclipse = new StringBuffer();
443 StringBuilder periods = new StringBuilder();
444 StringBuilder eclipse = new StringBuilder();
448445 for(String p : sequence.split("[+,]")) {
449446 if (p.startsWith("(") && p.endsWith(")")) {
450447 // phases of eclipse are enclosed in (), remove them
867864 return null;
868865 }
869866
870 private boolean meansYes(String s) {
867 private static boolean meansYes(String s) {
871868 if(s == null)
872869 return false;
873870 s = s.toLowerCase();
874871 return ("yes".startsWith(s) || "true".startsWith(s) || "1".equals(s));
875872 }
876873
877 private Integer parseDistance(String ds, boolean[] flags) {
874 private static Integer parseDistance(String ds, boolean[] flags) {
878875 ParsePosition pp = new ParsePosition(0);
879876 Number dn = new DecimalFormat().parse(ds, pp);
880877 if(dn != null) {
882879 int di = dn.intValue();
883880 flags[DISTANCE_FLAG_METRIC_INDEX] = true;
884881 flags[DISTANCE_FLAG_TENTHS_INDEX] = false;
885 if("ft".equals(ds.substring(pp.getIndex()).trim().toLowerCase()))
882 if("ft".equalsIgnoreCase(ds.substring(pp.getIndex()).trim()))
886883 flags[DISTANCE_FLAG_METRIC_INDEX] = false;
887884 if((double)di != dd) {
888885 // number has fractional part
10371034 return 0;
10381035 }
10391036
1040 private int[] parsePeriods(String ps) {
1037 private static int[] parsePeriods(String ps) {
10411038 if(ps == null)
10421039 return ZERO_INT_ARRAY;
10431040 String [] psa = ps.split(",");
10541051 String[] defs = new String[0];
10551052 if(ls.startsWith("(")) {
10561053 // handle polish syntax "(c,r,a),(c,r,a)..."
1057 List<String> out = new ArrayList<String>();
1054 List<String> out = new ArrayList<>();
10581055 int start = 0;
10591056 // start should be on the '(' at the start of the loop
10601057 while(start < ls.length()) {
2828 * @author Steve Ratcliffe
2929 */
3030 public interface InternalFiles {
31 public RGNFile getRgnFile();
31 RGNFile getRgnFile();
3232
33 public LBLFile getLblFile();
33 LBLFile getLblFile();
3434
35 public TREFile getTreFile();
35 TREFile getTreFile();
3636
37 public NETFile getNetFile();
37 NETFile getNetFile();
3838
39 public NODFile getNodFile();
39 NODFile getNodFile();
4040 }
7474 BitWriter bsBest = bsSimple;
7575 int xBestBase = xBase;
7676 int yBestBase = yBase;
77 if (xBase > 0 || yBase > 0){
78 if (log.isDebugEnabled())
79 log.debug("start opt:", xBase, yBase, xSameSign, xSignNegative, ySameSign, ySignNegative);
77 if (xBase > 0 || yBase > 0 && log.isDebugEnabled()) {
78 log.debug("start opt:", xBase, yBase, xSameSign, xSignNegative, ySameSign, ySignNegative);
8079 }
8180 if (xBase > 0){
8281 int notBetter = 0;
8483 xSameSign = false;
8584 for (int xTestBase = xBase-1; xTestBase >= 0; xTestBase--){
8685 BitWriter bstest = makeBitStream(minPointsRequired, xTestBase, yBase);
87 // System.out.println(xBase + " " + xTestBase + " -> " + bsBest.getBitPosition() + " " + bstest.getBitPosition());
8886 if (bstest.getBitPosition() >= bsBest.getBitPosition() ){
8987 if (++notBetter >= 2)
9088 break; // give up
102100 ySameSign = false;
103101 for (int yTestBase = yBase-1; yTestBase >= 0; yTestBase--){
104102 BitWriter bstest = makeBitStream(minPointsRequired, xBestBase, yTestBase);
105 // System.out.println(yBase + " " + yTestBase + " -> " + bsBest.getBitPosition() + " " + bstest.getBitPosition());
106103 if (bstest.getBitPosition() >= bsBest.getBitPosition()){
107104 if (++notBetter >= 2)
108105 break; // give up
114111 }
115112 ySameSign = ySameSignBak;
116113 }
117 if (xBase != xBestBase || yBestBase != yBase){
118 if (log.isInfoEnabled()){
119 if (bsSimple.getLength() > bsBest.getLength())
120 log.info("optimizer reduced bit stream byte length from",bsSimple.getLength(),"->",bsBest.getLength(),"(" + (bsSimple.getLength()-bsBest.getLength()), " byte(s)) for",polyline.getClass().getSimpleName(),"with",polyline.getPoints().size(),"points");
121 else
122 log.info("optimizer only reduced bit stream bit length from",bsSimple.getBitPosition(),"->",bsBest.getBitPosition(),"bits for",polyline.getClass().getSimpleName(),"with",polyline.getPoints().size(),"points, using original bit stream");
123 }
114 if (xBase != xBestBase || yBestBase != yBase && log.isInfoEnabled()) {
115 if (bsSimple.getLength() > bsBest.getLength())
116 log.info("optimizer reduced bit stream byte length from",bsSimple.getLength(),"->",bsBest.getLength(),"(" + (bsSimple.getLength()-bsBest.getLength()), " byte(s)) for",polyline.getClass().getSimpleName(),"with",polyline.getPoints().size(),"points");
117 else
118 log.info("optimizer only reduced bit stream bit length from",bsSimple.getBitPosition(),"->",bsBest.getBitPosition(),"bits for",polyline.getClass().getSimpleName(),"with",polyline.getPoints().size(),"points, using original bit stream");
124119 }
125120 if (bsSimple.getLength() == bsBest.getLength()){
126121 // if the (byte) length was not improved,
184179 for (int i = 0; i < deltas.length; i+=2) {
185180 int dx = deltas[i];
186181 int dy = deltas[i + 1];
187 if (dx == 0 && dy == 0){
188 if (extraBit && nodes[i/2+1] == false && i+2 != deltas.length) // don't skip CoordNode
182 if (dx == 0 && dy == 0) {
183 if (extraBit && !nodes[i/2+1] && i+2 != deltas.length) // don't skip CoordNode
189184 continue;
190185 }
191186 ++numPointsEncoded;
244239
245240 // Space to hold the deltas
246241 int numPointsToUse = points.size();
247 if (polyline instanceof Polygon){
248 if (points.get(0).equals(points.get(points.size()-1)))
249 --numPointsToUse; // no need to write the closing point
242 if (polyline instanceof Polygon && points.get(0).equals(points.get(points.size()-1))) {
243 --numPointsToUse; // no need to write the closing point
250244 }
251245 deltas = new int[2 * (numPointsToUse - 1)];
252246
123123
124124 public Coord getLocation() {
125125 int shift = getSubdiv().getShift();
126 Coord co = new Coord(getSubdiv().getLatitude() + (getDeltaLat() << shift),
127 getSubdiv().getLongitude() + (getDeltaLong() << shift) );
128 return co;
126 return new Coord(getSubdiv().getLatitude() + (getDeltaLat() << shift),
127 getSubdiv().getLongitude() + (getDeltaLong() << shift));
129128 }
130129 }
6161 private List<Label> refLabels;
6262
6363 // The actual points that make up the line.
64 private final List<Coord> points = new ArrayList<Coord>();
64 private final List<Coord> points = new ArrayList<>();
6565
6666 public Polyline(Subdivision div) {
6767 setSubdiv(div);
8181 try {
8282 // Prepare the information that we need.
8383 w = new LinePreparer(this);
84 }
85 catch (AssertionError ae) {
84 } catch (AssertionError ae) {
8685 log.error("Problem writing line (" + getClass() + ") of type 0x" + Integer.toHexString(getType()) + " containing " + points.size() + " points and starting at " + points.get(0).toOSMURL());
8786 log.error(" Subdivision shift is " + getSubdiv().getShift() +
8887 " and its centre is at " + getSubdiv().getCenter().toOSMURL());
9493
9594 int minPointsRequired = (this instanceof Polygon)? 3 : 2;
9695 BitWriter bw = w.makeShortestBitStream(minPointsRequired);
97 if(bw == null) {
96 if (bw == null) {
9897 log.error("Level " + getSubdiv().getZoom().getLevel() + " " + ((this instanceof Polygon)? "polygon" : "polyline") + " has less than " + minPointsRequired + " points, discarding");
9998 return;
10099 }
125124 FLAG_NETINFO | (loff & FLAG_EXTRABIT));
126125 // also add ref label(s) if present
127126 if(refLabels != null)
128 for(Label rl : refLabels)
129 roaddef.addLabel(rl);
127 refLabels.forEach(roaddef::addLabel);
130128 }
131129
132130 file.put3u(loff);
311309 }
312310
313311 public void addRefLabel(Label refLabel) {
314 if(refLabels == null)
315 refLabels = new ArrayList<Label>();
312 if (refLabels == null)
313 refLabels = new ArrayList<>();
316314 refLabels.add(refLabel);
317315 }
318316
185185 }
186186 }
187187
188 @Override
188189 public ImgFileWriter getWriter() {
189190 return super.getWriter();
190191 }
7171 * @return A list of all points for the subdiv.
7272 */
7373 public List<Point> pointsForSubdiv(Subdivision sd, boolean withExtType) {
74 ArrayList<Point> list = new ArrayList<Point>();
74 ArrayList<Point> list = new ArrayList<>();
7575 if (sd.hasIndPoints() || sd.hasPoints()){
7676
7777 RgnOffsets rgnOffsets = getOffsets(sd);
111111 if (record != null) {
112112 l = record.getNameLabel();
113113 p.setPOIRecord(record);
114 } else
114 } else {
115115 l = lblFile.fetchLabel(0);
116 }
116117 } else {
117118 l = lblFile.fetchLabel(labelOffset);
118119 }
160161 if (record != null) {
161162 l = record.getNameLabel();
162163 p.setPOIRecord(record);
163 } else
164 } else {
164165 l = lblFile.fetchLabel(0);
166 }
165167 } else {
166168 l = lblFile.fetchLabel(labelOffset);
167169 }
182184 * @return A list of lines.
183185 */
184186 public List<Polyline> linesForSubdiv(Subdivision div) {
185 ArrayList<Polyline> list = new ArrayList<Polyline>();
187 ArrayList<Polyline> list = new ArrayList<>();
186188
187189 if (div.hasPolylines()){
188190 RgnOffsets rgnOffsets = getOffsets(div);
215217 * @param witExtTypeData
216218 */
217219 public List<Polygon> shapesForSubdiv(Subdivision div, boolean witExtTypeData) {
218 ArrayList<Polygon> list = new ArrayList<Polygon>();
220 ArrayList<Polygon> list = new ArrayList<>();
219221 if (div.hasPolygons()){
220222
221223 RgnOffsets rgnOffsets = getOffsets(div);
232234 line.setNumber(list.size());
233235 }
234236 }
235 if (witExtTypeData) {
236 if (div.getExtTypeAreasSize() > 0){
237 int start = rgnHeader.getExtTypeAreasOffset() + div.getExtTypeAreasOffset();
238 int end = start + div.getExtTypeAreasSize();
239 position(start);
240 while (position() < end) {
241 Polygon line = new Polygon(div);
242 readLineCommonExtType(getReader(), div, line);
243 list.add(line);
244 }
237 if (witExtTypeData && div.getExtTypeAreasSize() > 0) {
238 int start = rgnHeader.getExtTypeAreasOffset() + div.getExtTypeAreasOffset();
239 int end = start + div.getExtTypeAreasSize();
240 position(start);
241 while (position() < end) {
242 Polygon line = new Polygon(div);
243 readLineCommonExtType(getReader(), div, line);
244 list.add(line);
245245 }
246246 }
247247 return list;
358358 void extractExtraBytes(ImgFileReader reader, MapObject o){
359359 long pos = reader.position();
360360 StringBuilder sb = new StringBuilder();
361 ArrayList<Byte> bytes = new ArrayList<Byte>();
361 ArrayList<Byte> bytes = new ArrayList<>();
362362 byte b1 = reader.get();
363363 bytes.add(b1);
364364 if ((b1 & 0xe0) != 0){
422422 boolean xsame = br.get1();
423423 if (xsame) {
424424 xneg = br.get1();
425 } else
425 } else {
426426 xbase++;
427 }
427428
428429 boolean ysame = br.get1();
429430 boolean yneg = false;
430431 if (ysame) {
431432 yneg = br.get1();
432 } else
433 } else {
433434 ybase++;
435 }
434436
435437 if(line.hasExtendedType()) {
436438 br.get1();
4545 private static final Logger log = Logger.getLogger(TREFile.class);
4646
4747 // Zoom levels for map
48 // private List<Zoom> mapLevels = new ArrayList<Zoom>();
4948 private final Zoom[] mapLevels = new Zoom[16];
5049
5150 private final List<Label> copyrights = new ArrayList<>();
5251
5352 // Information about polylines. eg roads etc.
54 private final List<PolylineOverview> polylineOverviews = new ArrayList<PolylineOverview>();
55
56 private final List<PolygonOverview> polygonOverviews = new ArrayList<PolygonOverview>();
57
58 private final List<PointOverview> pointOverviews = new ArrayList<PointOverview>();
53 private final List<PolylineOverview> polylineOverviews = new ArrayList<>();
54
55 private final List<PolygonOverview> polygonOverviews = new ArrayList<>();
56
57 private final List<PointOverview> pointOverviews = new ArrayList<>();
5958
6059 private int lastRgnPos;
6160
170170 int levelsSize = header.getMapLevelsSize();
171171 reader.position(levelsPos);
172172
173 List<Subdivision[]> levelDivs = new ArrayList<Subdivision[]>();
174 List<Zoom> mapLevels = new ArrayList<Zoom>();
173 List<Subdivision[]> levelDivsList = new ArrayList<>();
174 List<Zoom> mapLevelsList = new ArrayList<>();
175175 int end = levelsPos + levelsSize;
176176 while (reader.position() < end) {
177177 int level = reader.get1u();
179179 int ndivs = reader.get2u();
180180
181181 Subdivision[] divs = new Subdivision[ndivs];
182 levelDivs.add(divs);
182 levelDivsList.add(divs);
183183 level &= 0x7f;
184184
185185 Zoom z = new Zoom(level, nbits);
186 mapLevels.add(z);
187 }
188
189 this.levelDivs = levelDivs.toArray(new Subdivision[levelDivs.size()][]);
190 this.mapLevels = mapLevels.toArray(new Zoom[mapLevels.size()]);
186 mapLevelsList.add(z);
187 }
188
189 this.levelDivs = levelDivsList.toArray(new Subdivision[levelDivsList.size()][]);
190 this.mapLevels = mapLevelsList.toArray(new Zoom[mapLevelsList.size()]);
191191 }
192192
193193 public void config(EnhancedProperties props) {
3939
4040 private final int resolution;
4141
42 private final List<Subdivision> subdivs = new ArrayList<Subdivision>();
42 private final List<Subdivision> subdivs = new ArrayList<>();
4343
4444 /**
4545 * Create a new zoom level.
3838
3939 private boolean hasBitmap;
4040 private boolean hasBorder;
41 private final List<RgbWithTag> colours = new ArrayList<RgbWithTag>();
42 private final Map<String, Integer> indexMap = new HashMap<String, Integer>();
41 private final List<RgbWithTag> colours = new ArrayList<>();
42 private final Map<String, Integer> indexMap = new HashMap<>();
4343
4444 private char width;
4545 private char height;
2929 * @author Steve Ratcliffe
3030 */
3131 public class ShapeStacking {
32 private final SortedMap<Integer, DrawOrder> bar = new TreeMap<Integer, DrawOrder>();
32 private final SortedMap<Integer, DrawOrder> bar = new TreeMap<>();
3333
3434 public void addPolygon(int level, int type, int subtype) {
3535 int levelType = (level << 16) + type;
36 DrawOrder order = bar.get(levelType);
37 if (order == null) {
38 order = new DrawOrder(type);
39 bar.put(levelType, order);
40 }
41
42 order.addSubtype(subtype);
36 bar.computeIfAbsent(levelType, k -> new DrawOrder(type)).addSubtype(subtype);
4337 }
4438
4539 public void write(ImgFileWriter writer) {
4949
5050 private TypData data;
5151
52 private final Map<Integer, Integer> strToType = new TreeMap<Integer, Integer>();
53 private final Map<Integer, Integer> typeToStr = new TreeMap<Integer, Integer>();
52 private final Map<Integer, Integer> strToType = new TreeMap<>();
53 private final Map<Integer, Integer> typeToStr = new TreeMap<>();
5454
5555 public TYPFile(ImgChannel chan) {
5656 setHeader(header);
9090
9191 SectionWriter writer = header.getLabels().makeSectionWriter(in);
9292
93 List<SortKey<TypIconSet>> keys = new ArrayList<SortKey<TypIconSet>>();
93 List<SortKey<TypIconSet>> keys = new ArrayList<>();
9494 Sort sort = data.getSort();
9595 for (TypIconSet icon : data.getIcons()) {
9696 String label = icon.getLabel();
185185 zapZero(dataSection, indexSection);
186186 }
187187
188 private void zapZero(Section... sect) {
188 private static void zapZero(Section... sect) {
189189 for (Section s : sect) {
190190 if (s.getSize() == 0) {
191191 s.setPosition(0);
2828
2929 private final ShapeStacking stacking = new ShapeStacking();
3030 private final TypParam param = new TypParam();
31 private final List<TypPolygon> polygons = new ArrayList<TypPolygon>();
32 private final List<TypLine> lines = new ArrayList<TypLine>();
33 private final List<TypPoint> points = new ArrayList<TypPoint>();
34 private final List<TypIconSet> icons = new ArrayList<TypIconSet>();
31 private final List<TypPolygon> polygons = new ArrayList<>();
32 private final List<TypLine> lines = new ArrayList<>();
33 private final List<TypPoint> points = new ArrayList<>();
34 private final List<TypIconSet> icons = new ArrayList<>();
3535
3636 private Sort sort;
3737 private CharsetEncoder encoder;
3535 private int type;
3636 private int subType;
3737
38 protected final List<TypLabel> labels = new ArrayList<TypLabel>();
38 protected final List<TypLabel> labels = new ArrayList<>();
3939
4040 protected Xpm xpm;
4141
2424 * @author Steve Ratcliffe
2525 */
2626 public class TypIconSet extends TypElement {
27 private final List<Xpm> icons = new ArrayList<Xpm>();
27 private final List<Xpm> icons = new ArrayList<>();
2828
2929 public void write(ImgFileWriter writer, CharsetEncoder encoder) {
3030 offset = writer.position();
4343 }
4444 }
4545
46 private int calcBits(ColourInfo colourInfo) {
46 private static int calcBits(ColourInfo colourInfo) {
4747 int bits = 0;
4848 int bpp = colourInfo.getBitsPerPixel();
4949
6868 /**
6969 * Icon sets can have full colour pixmaps.
7070 */
71 @Override
7172 public boolean simpleBitmap() {
7273 return false;
7374 }
2828 private final ImgFileReader reader;
2929 private int numberOfMaps;
3030
31 private final List<MapInfo> maps = new ArrayList<MapInfo>();
31 private final List<MapInfo> maps = new ArrayList<>();
3232
3333 public MdxFileReader(ImgChannel chan) {
3434 this.reader = new BufferedImgFileReader(chan);
5050 private char[] currTable;
5151
5252 BlockTable() {
53 blocks = new ArrayList<char[]>(200);
53 blocks = new ArrayList<>(200);
5454 }
5555
5656 /**
6767 }
6868
6969 private byte[] writeToCommon(int codePage) throws IOException {
70 StructuredOutputStream output = getStructuredOutput(codePage);
71 writeBody(output);
70 writeBody(getStructuredOutput(codePage));
7271
7372 byte[] b = this.output.toByteArray();
7473
3535 public class Logger {
3636 private final java.util.logging.Logger log;
3737
38 private static final ThreadLocal<String> threadTags = new ThreadLocal<String>();
38 private static final ThreadLocal<String> threadTags = new ThreadLocal<>();
3939
4040 static {
4141 initLogging();
241241 log.log(type, tagMessage(msg));
242242 }
243243
244 private String tagMessage(String message) {
244 private static String tagMessage(String message) {
245245 String threadTag = threadTags.get();
246246 return (threadTag != null) ? threadTag + ": " + message : message;
247247 }
00 package uk.me.parabola.mkgmap;
11
22 import java.io.File;
3 import java.util.Arrays;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Properties;
7 import java.util.Set;
8 import java.util.TreeSet;
9 import java.util.regex.Pattern;
310
11 import uk.me.parabola.imgfmt.ExitException;
412 import uk.me.parabola.imgfmt.app.srt.Sort;
513 import uk.me.parabola.util.EnhancedProperties;
614
1321
1422 public CommandArgs(EnhancedProperties args) {
1523 currentOptions = new EnhancedProperties(args);
24 // verify options which expect a comma-separated list
25 for (String listOpt : Arrays.asList("mdr7-excl", "mdr7-del", "poi-excl-index", "location-autofill",
26 "overview-levels", "levels", "name-tag-list", "polygon-size-limits", "dem", "dem-dists", "drive-on",
27 "dead-ends", "add-pois-to-lines", "coastlinefile", "generate-sea", "nearby-poi-rules")) {
28 stringToList(get(listOpt, null), listOpt);
29 }
1630 }
1731
1832 public EnhancedProperties getProperties() {
7690 }
7791
7892 public String getOutputDir() {
79 String DEFAULT_DIR = ".";
93 final String DEFAULT_DIR = ".";
8094 String fileOutputDir = currentOptions.getProperty("output-dir", DEFAULT_DIR);
8195
8296 // Test if directory exists
115129 public boolean exists(String name) {
116130 return currentOptions.containsKey(name);
117131 }
132
133 public static List<String> getNameTags(Properties props) {
134 String s = props.getProperty("name-tag-list", "name");
135 return stringToList(s, "name-tag-list");
136 }
137
138 public Set<String> argToSet(String name, String def) {
139 String optVal = currentOptions.getProperty(name, def);
140 return stringToSet(optVal, name);
141 }
142
143 public List<String> argToList(String name, String def) {
144 String optVal = currentOptions.getProperty(name, def);
145 return stringToList(optVal, name);
146 }
147
148 public static Set<String> stringToSet(String opt, String optName) {
149 List<String> list = stringToList(opt, optName);
150 if (list.isEmpty())
151 return Collections.emptySet();
152 TreeSet<String> set = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
153 set.addAll(list);
154 return set;
155 }
156
157 private static final Pattern COMMA_OR_SPACE_PATTERN = Pattern.compile("[,\\s]+");
158
159 public static List<String> stringToList(String opt, String optName) {
160 if (opt == null)
161 return Collections.emptyList();
162 if (opt.startsWith("'") || opt.startsWith("\""))
163 opt = opt.substring(1);
164 if (opt.endsWith("'") || opt.endsWith("\""))
165 opt = opt.substring(0, opt.length() - 1);
166 if (opt.endsWith(",")) {
167 throw new ExitException("Option " + optName + " ends in a comma and hence has a missing value");
168 }
169 return Arrays.asList(COMMA_OR_SPACE_PATTERN.split(opt));
170 }
171
118172 }
9090 // This is a long style 'property' format option.
9191 addOption(arg.substring(2));
9292
93 } else if (arg.equals("-c")) {
93 } else if ("-c".equals(arg)) {
9494 // Config file
9595 readConfigFile(args[i++]);
9696
97 } else if (arg.equals("-n")) {
97 } else if ("-n".equals(arg)) {
9898 // Map name (should be an 8 digit number).
9999 addOption("mapname", args[i++]);
100100
101 } else if (arg.equals("-v")) {
101 } else if ("-v".equals(arg)) {
102102 // make commands more verbose
103103 addOption("verbose");
104104
255255 * filenames. The options take effect where they appear.
256256 */
257257 interface ArgType {
258 public abstract void processArg();
258 void processArg();
259259 }
260260
261261 /**
315315
316316 }
317317
318 private String extractMapName(String path) {
318 private static String extractMapName(String path) {
319319 File file = new File(path);
320320 String fname = file.getName();
321321 Pattern pat = Pattern.compile("([0-9]{8})");
4545 private final OptionProcessor proc;
4646
4747 // Used to prevent the same file being read more than once.
48 private final Collection<String> readFiles = new HashSet<String>();
48 private final Collection<String> readFiles = new HashSet<>();
4949
5050 public Options(OptionProcessor proc) {
5151 this.proc = proc;
8585 public void readOptionFile(Reader r, String filename) {
8686 BufferedReader br;
8787 if (r instanceof BufferedReader)
88 br = (BufferedReader)r;
88 br = (BufferedReader) r;
8989 else
9090 br = new BufferedReader(r);
9191 TokenScanner ts = new TokenScanner(filename, br);
112112
113113 String punc = ts.nextValue();
114114 String val;
115 if (punc.equals(":") || punc.equals("=")) {
115 if (":".equals(punc) || "=".equals(punc)) {
116116 val = ts.readLine();
117 } else if (punc.equals("{")) {
117 } else if ("{".equals(punc)) {
118118 ts.skipSpace();
119119 val = ts.readUntil(TokType.SYMBOL, "}");
120120 ts.nextToken(); // discard the closing brace
125125
126126 // Relative file names in the file are taken relative to the
127127 // location of the argument file.
128 if (key.equals("input-file") && !new File(val).isAbsolute())
128 if ("input-file".equals(key) && !new File(val).isAbsolute())
129129 val = new File(parent, val).getPath();
130130
131131 proc.processOption(new Option(key, val));
3434 private static final Logger log = Logger.getLogger(LayerFilterChain.class);
3535
3636 // The filters that will be applied to the element.
37 private List<MapFilter> filters = new ArrayList<MapFilter>();
37 private List<MapFilter> filters = new ArrayList<>();
3838
3939 // The position in the filter list.
4040 private int position;
3131 private static final Logger log = Logger.getLogger(Locator.class);
3232
3333 /** hash map to collect equally named MapPoints*/
34 private final MultiHashMap<String, MapPoint> cityMap = new MultiHashMap<String, MapPoint>();
34 private final MultiHashMap<String, MapPoint> cityMap = new MultiHashMap<>();
3535
3636 private final KdTree<MapPoint> cityFinder = new KdTree<>();
37 private final List<MapPoint> placesMap = new ArrayList<MapPoint>();
37 private final List<MapPoint> placesMap = new ArrayList<>();
3838
3939 private final NameFinder nameFinder;
4040
5050
5151 public Locator(EnhancedProperties props) {
5252 this.nameFinder = new NameFinder(props);
53 this.locationAutofill = new HashSet<String>(LocatorUtil.parseAutofillOption(props));
53 this.locationAutofill = new HashSet<>(LocatorUtil.parseAutofillOption(props));
5454 }
5555
5656 public void addCityOrPlace(MapPoint p)
5757 {
58 if (p.isCity() == false)
59 {
60 log.warn("MapPoint has no city type id: 0x"+Integer.toHexString(p.getType()));
58 if (!p.isCity()) {
59 log.warn("MapPoint has no city type id: 0x" + Integer.toHexString(p.getType()));
6160 return;
6261 }
6362
132131 }
133132 }
134133
135 public final static ShortArrayList PREFERRED_NAME_TAG_KEYS = TagDict.compileTags("name","name:en","int_name");
134 public static final ShortArrayList PREFERRED_NAME_TAG_KEYS = TagDict.compileTags("name","name:en","int_name");
136135
137136 public String getCountryISOCode(Tags tags) {
138137 for (short nameTagKey : PREFERRED_NAME_TAG_KEYS) {
173172 */
174173 private void resolveIsInInfo(MapPoint p)
175174 {
176 if (locationAutofill.contains("is_in") == false) {
175 if (!locationAutofill.contains("is_in")) {
177176 return;
178177 }
179178
281280
282281 private MapPoint findCityByIsIn(MapPoint place) {
283282
284 if (locationAutofill.contains("is_in") == false) {
283 if (!locationAutofill.contains("is_in")) {
285284 return null;
286285 }
287286
302301 cityCandidate = cityCandidate.trim();
303302
304303 Collection<MapPoint> candidateCityList = cityMap.get(cityCandidate);
305 if (candidateCityList.isEmpty() == false) {
304 if (!candidateCityList.isEmpty()) {
306305 if (nextCityList == null) {
307 nextCityList = new ArrayList<MapPoint>(candidateCityList.size());
306 nextCityList = new ArrayList<>(candidateCityList.size());
308307 }
309308 nextCityList.addAll(candidateCityList);
310309 }
338337 }
339338
340339 public void autofillCities() {
341 if (locationAutofill.contains("nearest") == false && locationAutofill.contains("is_in") == false) {
340 if (!locationAutofill.contains("nearest") && !locationAutofill.contains("is_in")) {
342341 return;
343342 }
344343
4545 private final Set<String> continents = new HashSet<>();
4646
4747 /** maps ISO => default country name */
48 private final Map<String, String> defaultCountryNames = new HashMap<String, String>();
48 private final Map<String, String> defaultCountryNames = new HashMap<>();
4949
5050 /** Maps 3 letter ISO code to all tags of a country */
51 private final Map<String, Tags> countryTagMap = new HashMap<String, Tags>();
52
53 private final static LocatorConfig instance = new LocatorConfig();
51 private final Map<String, Tags> countryTagMap = new HashMap<>();
52
53 private static final LocatorConfig instance = new LocatorConfig();
5454
5555 public static LocatorConfig get() {
5656 return instance;
8585
8686 Node rootNode = document.getDocumentElement();
8787
88 if(rootNode.getNodeName().equals("locator"))
88 if("locator".equals(rootNode.getNodeName()))
8989 {
9090 Node cNode = rootNode.getFirstChild();
9191
9292 while(cNode != null)
9393 {
94 if(cNode.getNodeName().equals("continent"))
94 if("continent".equals(cNode.getNodeName()))
9595 {
9696 NamedNodeMap attr = cNode.getAttributes();
9797
104104
105105 }
106106
107 if (cNode.getNodeName().equals("country")) {
107 if ("country".equals(cNode.getNodeName())) {
108108 NamedNodeMap attr = cNode.getAttributes();
109109 String iso = null;
110110 if (attr != null) {
162162 Node cEntryNode = cNode.getFirstChild();
163163 while(cEntryNode != null)
164164 {
165 if(cEntryNode.getNodeName().equals("variant"))
165 if("variant".equals(cEntryNode.getNodeName()))
166166 {
167167 Node nodeText = cEntryNode.getFirstChild();
168168
1313
1414 import java.util.Arrays;
1515 import java.util.Collections;
16 import java.util.HashSet;
1716 import java.util.List;
1817 import java.util.Set;
19 import java.util.regex.Pattern;
2018
19 import uk.me.parabola.mkgmap.CommandArgs;
2120 import uk.me.parabola.util.EnhancedProperties;
2221
2322 public class LocatorUtil {
24
25 private static final Pattern COMMA_OR_SPACE_PATTERN = Pattern.compile("[,\\s]+");
23 private LocatorUtil () {
24 // private constructor to hide the implicit one
25 }
2626
2727 /**
2828 * Parses the parameters of the location-autofill option. Establishes also downwards
3131 * @return the options
3232 */
3333 public static Set<String> parseAutofillOption(EnhancedProperties props) {
34 String optionStr = props.getProperty("location-autofill", null);
34 final String optName = "location-autofill";
35 final String IS_IN = "is_in";
36 final String NEAREST = "nearest";
37 String optionStr = props.getProperty(optName, null);
3538 if (optionStr == null) {
3639 return Collections.emptySet();
3740 }
38
39 Set<String> autofillOptions = new HashSet<String>(Arrays.asList(COMMA_OR_SPACE_PATTERN
40 .split(optionStr)));
41 Set<String> autofillOptions = CommandArgs.stringToSet(optionStr, optName);
4142
4243 // convert the old autofill options to the new parameters
4344 if (autofillOptions.contains("0")) {
44 autofillOptions.add("is_in");
45 autofillOptions.add(IS_IN);
4546 autofillOptions.remove("0");
4647 }
4748 if (autofillOptions.contains("1")) {
48 autofillOptions.add("is_in");
49 // PENDING: fuzzy search
49 autofillOptions.add(IS_IN);
5050 autofillOptions.remove("1");
5151 }
5252 if (autofillOptions.contains("2")) {
53 autofillOptions.add("is_in");
53 autofillOptions.add(IS_IN);
5454 // PENDING: fuzzy search
55 autofillOptions.add("nearest");
55 autofillOptions.add(NEAREST);
5656 autofillOptions.remove("2");
5757 }
5858 if (autofillOptions.contains("3")) {
59 autofillOptions.add("is_in");
59 autofillOptions.add(IS_IN);
6060 // PENDING: fuzzy search
61 autofillOptions.add("nearest");
61 autofillOptions.add(NEAREST);
6262 autofillOptions.remove("3");
6363 }
64 final List<String> knownOptions = Arrays.asList("bounds","is_in","nearest");
64 final List<String> knownOptions = Arrays.asList("bounds", IS_IN, NEAREST);
6565 for (String s : autofillOptions){
66 if (knownOptions.contains(s) == false){
66 if (!knownOptions.contains(s)) {
6767 throw new IllegalArgumentException(s + " is not a known sub option for option location-autofill: " + optionStr);
6868 }
6969 }
3030 import java.util.Set;
3131 import java.util.TreeMap;
3232 import java.util.function.UnaryOperator;
33 import java.util.stream.Collectors;
3334
3435 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
3536 import uk.me.parabola.imgfmt.ExitException;
7071 import uk.me.parabola.imgfmt.app.trergn.TREHeader;
7172 import uk.me.parabola.imgfmt.app.trergn.Zoom;
7273 import uk.me.parabola.log.Logger;
74 import uk.me.parabola.mkgmap.CommandArgs;
7375 import uk.me.parabola.mkgmap.Version;
7476 import uk.me.parabola.mkgmap.combiners.OverviewBuilder;
7577 import uk.me.parabola.mkgmap.filters.BaseFilter;
165167 private String licenseFileName;
166168
167169 private boolean orderByDecreasingArea;
168 private String pathToHGT;
170 private String pathsToHGT;
169171 private List<Integer> demDists;
170172 private short demOutsidePolygonHeight;
171173 private java.awt.geom.Area demPolygon;
214216 if ("right".equals(driveOn))
215217 driveOnLeft = false;
216218 orderByDecreasingArea = props.getProperty("order-by-decreasing-area", false);
217 pathToHGT = props.getProperty("dem", null);
219 pathsToHGT = props.getProperty("dem", null);
218220 demDists = parseDemDists(props.getProperty("dem-dists", "-1"));
219221 demOutsidePolygonHeight = (short) props.getProperty("dem-outside-polygon", HGTReader.UNDEF);
220222 String demPolygonFile = props.getProperty("dem-poly", null);
239241 }
240242
241243 private static List<Integer> parseDemDists(String demDists) {
242 List<Integer> dists = new ArrayList<>();
243 if (demDists == null)
244 dists.add(-1);
245 else {
246 String[] vals = demDists.split(",");
247 for( String val : vals) {
248 int dist = Integer.parseInt(val.trim());
249 dists.add(dist);
250 }
251 }
244 List<Integer> dists = CommandArgs.stringToList(demDists, "dem-dists")
245 .stream().map(Integer::parseInt).collect(Collectors.toList());
246 if (dists.isEmpty())
247 return Arrays.asList(-1);
252248 return dists;
253249 }
254250
348344 demArea = new java.awt.geom.Area(demPoly);
349345 }
350346 }
351 Area treArea = demFile.calc(src.getBounds(), demArea, pathToHGT, demDists, demOutsidePolygonHeight, demInterpolationMethod);
347 Area treArea = demFile.calc(src.getBounds(), demArea, pathsToHGT, demDists, demOutsidePolygonHeight, demInterpolationMethod);
352348 map.setBounds(treArea);
353349 long t2 = System.currentTimeMillis();
354350 log.info("DEM file calculation for", map.getFilename(), "took", (t2 - t1), "ms");
664660
665661 private void processExit(Map map, MapExitPoint mep) {
666662 LBLFile lbl = map.getLblFile();
663 String exitName = mep.getName();
667664 String ref = mep.getMotorwayRef();
668665 String osmId = mep.getOSMId();
669 if(ref != null) {
666 if (ref == null)
667 log.warn("Can't create exit", exitName, "(OSM id", osmId, ") doesn't have exit:road_ref tag");
668 else {
670669 Highway hw = highways.get(ref);
671 if(hw == null)
672 hw = makeHighway(map, ref);
673 if(hw == null) {
674 log.warn("Can't create exit", mep.getName(), "(OSM id", osmId, ") on unknown highway", ref);
675 return;
676 }
677 String exitName = mep.getName();
670 if (hw == null) {
671 String countryStr = mep.getCountry();
672 Country thisCountry = countryStr != null ? lbl.createCountry(locator.normalizeCountry(countryStr), locator.getCountryISOCode(countryStr)) : getDefaultCountry();
673 String regionStr = regionName != null ? regionName : mep.getRegion(); // use --region-name if set because highway will likely span regions
674 Region thisRegion = regionStr != null ? lbl.createRegion(thisCountry, regionStr, null) : getDefaultRegion(thisCountry);
675 hw = lbl.createHighway(thisRegion, ref);
676 log.info("creating highway", ref, "region:", regionStr, "country:", countryStr, "for exit:", exitName);
677 highways.put(ref, hw);
678 }
678679 String exitTo = mep.getTo();
679680 Exit exit = new Exit(hw);
680681 String facilityDescription = mep.getFacilityDescription();
12861287 }
12871288 }
12881289
1289 Highway makeHighway(Map map, String ref) {
1290 if (getDefaultRegion(null) == null) {
1291 log.warn("Highway " + ref + " has no region (define a default region to zap this warning)");
1292 }
1293 return highways.computeIfAbsent(ref, k-> {
1294 log.info("creating highway", ref);
1295 return map.getLblFile().createHighway(getDefaultRegion(null), ref);
1296 });
1297 }
1298
12991290 /**
13001291 * It is not possible to represent large maps at the 24 bit resolution. This
13011292 * gets the largest resolution that can still cover the whole area of the
3232 *
3333 * @param args The command line arguments.
3434 */
35 public void init(CommandArgs args);
35 void init(CommandArgs args);
3636
3737 /**
3838 * This is called when an individual map is complete.
3939 *
4040 * @param info An interface to read the map.
4141 */
42 public void onMapEnd(FileInfo info);
42 void onMapEnd(FileInfo info);
4343
4444 /**
4545 * The complete map set has been processed. Finish off anything that needs
4646 * doing.
4747 */
48 public void onFinish();
48 void onFinish();
4949
50 public default String getFilename() {
50 default String getFilename() {
5151 return null;
5252 }
5353 }
4444 * each .img file.
4545 */
4646 public class GmapiBuilder implements Combiner {
47 private final static String NS = "http://www.garmin.com/xmlschemas/MapProduct/v1";
47 private static final String NS = "http://www.garmin.com/xmlschemas/MapProduct/v1";
4848
4949 private final Map<String, Combiner> combinerMap;
5050
244244 return mb;
245245 }
246246
247 private ProductBlock makeProductBlock(FileInfo info) {
247 private static ProductBlock makeProductBlock(FileInfo info) {
248248 ProductBlock pb = new ProductBlock(info.getCodePage());
249249 pb.setFamilyId(info.getFamilyId());
250250 pb.setProductId(info.getProductId());
274274 }
275275 }
276276
277 private void addImg(FileSystem outfs, FileInfo info) {
277 private static void addImg(FileSystem outfs, FileInfo info) {
278278 FileCopier fc = new FileCopier(info.getFilename());
279279 List<SubFileInfo> subFiles = info.subFiles();
280280
290290 }
291291 }
292292
293 private void addFile(FileSystem outfs, FileInfo info) {
293 private static void addFile(FileSystem outfs, FileInfo info) {
294294 String filename = info.getFilename();
295295 FileCopier fc = new FileCopier(filename);
296296
333333 mpsFile.addProduct(makeProductBlock(info));
334334 }
335335
336 private MpsFile createMpsFile(FileSystem outfs) throws FileNotWritableException {
336 private static MpsFile createMpsFile(FileSystem outfs) throws FileNotWritableException {
337337 try {
338338 ImgChannel channel = outfs.create("MAKEGMAP.MPS");
339339 return new MpsFile(channel);
357357 * @return The filename part, will be restricted to 8+3 characters and all
358358 * in upper case.
359359 */
360 private String createImgFilename(String pathname) {
360 private static String createImgFilename(String pathname) {
361361 File f = new File(pathname);
362362 String name = f.getName().toUpperCase(Locale.ENGLISH);
363363 int dot = name.lastIndexOf('.');
457457 }
458458 }
459459
460 private void copyFile(ImgChannel fin, ImgChannel fout) throws IOException {
460 private static void copyFile(ImgChannel fin, ImgChannel fout) throws IOException {
461461 ByteBuffer buf = ByteBuffer.allocate(1024);
462462 while (fin.read(buf) > 0) {
463463 buf.flip();
1414 import java.io.File;
1515 import java.io.FileNotFoundException;
1616 import java.io.IOException;
17 import java.nio.file.Files;
18 import java.nio.file.Paths;
19 import java.nio.file.StandardCopyOption;
1720 import java.util.ArrayList;
1821 import java.util.HashMap;
1922 import java.util.List;
179182 for (Country c : countries) {
180183 if (c != null) {
181184 Mdr14Record record = mdrFile.addCountry(c);
182 countryMap.put((int) c.getIndex(), record);
185 countryMap.put(c.getIndex(), record);
183186 }
184187 }
185188 return countryMap;
191194 List<Region> regions = mr.getRegions();
192195 for (Region region : regions) {
193196 if (region != null) {
194 Mdr14Record mdr14 = maps.countries.get((int) region.getCountry().getIndex());
197 Mdr14Record mdr14 = maps.countries.get(region.getCountry().getIndex());
195198 Mdr13Record record = mdrFile.addRegion(region, mdr14);
196 regionMap.put((int) region.getIndex(), record);
199 regionMap.put(region.getIndex(), record);
197200 }
198201 }
199202 return regionMap;
334337
335338 // Rename from the temporary file to the proper name. On windows the target file must
336339 // not exist for rename to work, so we are forced to remove it first.
337 File outputName = new File(this.outputName);
338 outputName.delete();
339 boolean ok = tmpName.renameTo(outputName);
340 if (!ok)
340 try {
341 Files.move(tmpName.toPath(), Paths.get(outputName), StandardCopyOption.REPLACE_EXISTING);
342 } catch (IOException e) {
341343 throw new MapFailedException("Could not create mdr.img file");
344 }
342345 }
343346
344347 /**
352355 mdrFile.write();
353356 }
354357
358 @Override
355359 public String getFilename() {
356360 return outputName;
357361 }
5555 }
5656 }
5757
58 @Override
5859 public String getFilename() {
5960 return Utils.joinPath(outputDir, mdxFilename);
6061 }
124124 }
125125
126126 private void writeDefines(PrintWriter pw) {
127 pw.format(Locale.ROOT, "!define DEFAULT_DIR \"C:\\Garmin\\Maps\\%s\"\n", familyName);
128 pw.format(Locale.ROOT, "!define INSTALLER_DESCRIPTION \"%s\"\n", familyName);
129 pw.format(Locale.ROOT, "!define INSTALLER_NAME \"%s\"\n", familyName);
130 pw.format(Locale.ROOT, "!define MAPNAME \"%s\"\n", baseFilename);
131 pw.format(Locale.ROOT, "!define PRODUCT_ID \"%s\"\n", productId);
132 pw.format(Locale.ROOT, "!define REG_KEY \"%s\"\n", familyName);
133 if (hasIndex)
134 pw.format(Locale.ROOT, "!define INDEX\n");
135 if (hasTyp)
136 pw.format(Locale.ROOT, "!define TYPNAME \"%s\"\n", typName);
127 pw.format(Locale.ROOT, "!define DEFAULT_DIR \"C:\\Garmin\\Maps\\%s\"\n", familyName);
128 pw.format(Locale.ROOT, "!define INSTALLER_DESCRIPTION \"%s\"\n", familyName);
129 pw.format(Locale.ROOT, "!define INSTALLER_NAME \"%s\"\n", familyName);
130 pw.format(Locale.ROOT, "!define MAPNAME \"%s\"\n", baseFilename);
131 pw.format(Locale.ROOT, "!define PRODUCT_ID \"%s\"\n", productId);
132 pw.format(Locale.ROOT, "!define REG_KEY \"%s\"\n", familyName);
133 if (hasIndex)
134 pw.append("!define INDEX\n");
135 if (hasTyp)
136 pw.format(Locale.ROOT, "!define TYPNAME \"%s\"\n", typName);
137137 }
138138
139139 private void writeRegBin(PrintWriter pw) {
142142 }
143143
144144 private void writeAddedFiles(PrintWriter pw) {
145 pw.format(Locale.ROOT, " File \"${MAPNAME}.img\"\n");
146 if (hasIndex) {
147 pw.format(Locale.ROOT, " File \"${MAPNAME}_mdr.img\"\n");
148 pw.format(Locale.ROOT, " File \"${MAPNAME}.mdx\"\n");
149 }
150 if (hasTyp)
151 pw.format(Locale.ROOT, " File \"%s\"\n", typName);
152
153 pw.format(Locale.ROOT, " File \"${MAPNAME}.tdb\"\n");
154 for (String file : mapList)
155 pw.format(Locale.ROOT, " File \"%s.img\"\n", file);
156 }
157
158
145 pw.append(" File \"${MAPNAME}.img\"\n");
146 if (hasIndex) {
147 pw.append(" File \"${MAPNAME}_mdr.img\"\n");
148 pw.append(" File \"${MAPNAME}.mdx\"\n");
149 }
150 if (hasTyp)
151 pw.format(Locale.ROOT, " File \"%s\"\n", typName);
152
153 pw.append(" File \"${MAPNAME}.tdb\"\n");
154 for (String file : mapList)
155 pw.format(Locale.ROOT, " File \"%s.img\"\n", file);
156 }
159157
160158 private void writeRemovedFiles(PrintWriter pw) {
161 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\${MAPNAME}.img\"\n");
162 if (hasIndex) {
163 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\${MAPNAME}_mdr.img\"\n");
164 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\${MAPNAME}.mdx\"\n");
165 }
166 if (hasTyp)
167 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\%s\"\n", typName);
168 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\${MAPNAME}.tdb\"\n");
169 for (String file : mapList) {
170 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\%s.img\"\n", file);
171 }
172 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\Uninstall.exe\"\n");
159 pw.append(" Delete \"$INSTDIR\\${MAPNAME}.img\"\n");
160 if (hasIndex) {
161 pw.append(" Delete \"$INSTDIR\\${MAPNAME}_mdr.img\"\n");
162 pw.append(" Delete \"$INSTDIR\\${MAPNAME}.mdx\"\n");
163 }
164 if (hasTyp)
165 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\%s\"\n", typName);
166 pw.append(" Delete \"$INSTDIR\\${MAPNAME}.tdb\"\n");
167 for (String file : mapList) {
168 pw.format(Locale.ROOT, " Delete \"$INSTDIR\\%s.img\"\n", file);
169 }
170 pw.append(" Delete \"$INSTDIR\\Uninstall.exe\"\n");
173171 }
174172
175173
5959 private String outputDir;
6060 private Integer codepage;
6161 private Integer encodingType;
62 private List<String[]> copyrightMsgs = new ArrayList<String[]>();
63 private List<String[]> licenseInfos = new ArrayList<String[]>();
62 private List<String[]> copyrightMsgs = new ArrayList<>();
63 private List<String[]> licenseInfos = new ArrayList<>();
6464 private LevelInfo[] wantedLevels;
6565 private Area bounds;
6666 private boolean hasBackground;
7878 outputDir = args.getOutputDir();
7979 String demDist = args.getProperties().getProperty("overview-dem-dist");
8080 String hgtPath = args.getProperties().getProperty("dem");
81 if (hgtPath != null && demDist != null && "0".equals(demDist.trim()) == false) {
81 if (hgtPath != null && demDist != null && !"0".equals(demDist.trim())) {
8282 demProps = new EnhancedProperties(args.getProperties());
8383 demProps.setProperty("dem-dists", demDist);
8484 }
111111 }
112112
113113 private void calcLevels() {
114 List<MapShape> shapes = overviewSource.getShapes();
115114 int maxRes = 16; // we can write a 0x4a polygon for planet in res 16
116115 if (wantedLevels != null)
117116 maxRes = wantedLevels[wantedLevels.length-1].getBits();
118117 int maxSize = 0xffff << (24 - maxRes);
119 for (MapShape s : shapes){
118 for (MapShape s : overviewSource.getShapes()){
120119 if (s.getType() != 0x4a)
121120 continue;
122121 int maxDimPoly = s.getBounds().getMaxDimension();
126125 maxRes--;
127126 maxSize = 0xffff << (24 - maxRes);
128127 }
129 String[] name = s.getName().split("\u001d");
130 String msg = "Tile selection (0x4a) polygon for ";
131 if (name != null && name.length == 2)
132 msg += "tile " + name[1].trim();
128 final String[] name = s.getName().split("\u001d");
129 final String msg = "Tile selection (0x4a) polygon for";
130 final String msg2;
131 if (name.length == 2)
132 msg2 = "tile " + name[1].trim();
133133 else
134 msg += s.getBounds();
135 log.error(msg,"cannot be written in level 0 resolution",oldMaxRes + ", using",maxRes,"instead");
134 msg2 = s.getBounds().toString();
135 log.error(msg, msg2, "cannot be written in level 0 resolution", oldMaxRes + ", using", maxRes, "instead");
136136
137137 }
138138 }
152152 }
153153 wantedLevels = Arrays.copyOfRange(wantedLevels, 0, l);
154154 overviewSource.setMapLevels(wantedLevels);
155 } else
156 setRes(maxRes);
155 } else {
156 setRes(maxRes);
157 }
157158 }
158159 }
159160
288289 for (Point point: pointList) {
289290 if (log.isDebugEnabled())
290291 log.debug("got point", point);
291 if (bounds.contains(point.getLocation()) == false){
292 if (!bounds.contains(point.getLocation())){
292293 if (log.isDebugEnabled())
293294 log.debug(point, "dropped, is outside of tile boundary");
294295 continue;
438439 else return name;
439440 }
440441
441 private List<String> creMsgList(List<String[]> msgs){
442 ArrayList< String> list = new ArrayList<String>();
442 private static List<String> creMsgList(List<String[]> msgs){
443 ArrayList< String> list = new ArrayList<>();
443444 for (int i = 0; i < msgs.size(); i++){
444445 String[] block = msgs.get(i);
445 for (String s : block){
446 list.add(s);
447 }
448 if (i < msgs.size()-1){
449 // separate blocks
446 list.addAll(Arrays.asList(block));
447 if (i < msgs.size() - 1) {
448 // separate blocks
450449 list.add("");
451450 }
452451 }
3232 *
3333 * @param cw The string to add.
3434 */
35 public void addCopyright(String cw);
35 void addCopyright(String cw);
3636
37 public void setMapLevels(LevelInfo[] levels);
37 void setMapLevels(LevelInfo[] levels);
3838 }
177177 writeTdbFile();
178178 }
179179
180 @Override
180181 public String getFilename() {
181182 return Utils.joinPath(outputDir, overviewMapname, "tdb");
182183 }
2525 */
2626 public class BaseFilter implements MapFilter {
2727 /**
28 * Empty implementation of the init function.
29 *
30 * @param config Configuration information, giving parameters of the map
31 * level that is being produced through this filter.
32 */
33 public void init(FilterConfig config) {
34 }
35
36 /**
3728 * Empty implementation.
3829 *
3930 * @param element A map element.
4031 * @param next This is used to pass the possibly transformed element onward.
4132 */
33 @Override
4234 public void doFilter(MapElement element, MapFilterChain next) {
4335 throw new UnsupportedOperationException();
4436 }
3636 this.filterDistance = filterDistance;
3737 }
3838
39 @Override
3940 public void init(FilterConfig config) {
4041 this.resolution = config.getResolution();
4142 this.maxErrorDistance = filterDistance * (1<< config.getShift());
4849 * @param element A map element that will be a line or a polygon.
4950 * @param next This is used to pass the possibly transformed element onward.
5051 */
52 @Override
5153 public void doFilter(MapElement element, MapFilterChain next) {
5254 // First off we don't touch things if at the highest level of detail
5355 if (resolution == 24) {
1313 private static final Logger log = Logger.getLogger(LineMergeFilter.class);
1414
1515 private List<MapLine> linesMerged;
16 private final MultiHashMap<Coord, MapLine> startPoints = new MultiHashMap<Coord, MapLine>();
17 private final MultiHashMap<Coord, MapLine> endPoints = new MultiHashMap<Coord, MapLine>();
16 private final MultiHashMap<Coord, MapLine> startPoints = new MultiHashMap<>();
17 private final MultiHashMap<Coord, MapLine> endPoints = new MultiHashMap<>();
1818
1919 private void addLine(MapLine line) {
2020 linesMerged.add(line);
5555 //TODO: This routine has a side effect: it modifies some of the MapLine instances
5656 // instead of creating copies. It seems that this has no bad effect, but it is not clean
5757 public List<MapLine> merge(List<MapLine> lines, int res) {
58 linesMerged = new ArrayList<MapLine>(lines.size()); //better use LinkedList??
58 linesMerged = new ArrayList<>(lines.size()); //better use LinkedList??
5959 for (MapLine line : lines) {
6060 if (line.getMinResolution() > res || line.getMaxResolution() < res)
6161 continue;
107107
108108 // No matching, create a copy of line
109109 MapLine l = line.copy();
110 List<Coord> p = new ArrayList<Coord>(line.getPoints()); //use better LinkedList for performance?
110 List<Coord> p = new ArrayList<>(line.getPoints()); //use better LinkedList for performance?
111111 l.setPoints(p);
112112 addLine(l);
113113 }
3838 this.subdiv = subdiv;
3939 }
4040
41 @Override
4142 public void init(FilterConfig config) {
4243 shift = config.getShift();
4344 }
4647 * @param element A map element that will be a line or a polygon.
4748 * @param next This is used to pass the element onward.
4849 */
50 @Override
4951 public void doFilter(MapElement element, MapFilterChain next) {
5052 MapLine line = (MapLine) element;
5153
128130 rotation = maxBitsPos[k];
129131 }
130132 }
131 /*
132 int savedBits = (numPoints-1 * maxReduction);
133 if (savedBits > 100){
134 System.out.println("rotation of shape saves " + savedBits + " bits");
135 }
136 */
137133 if (rotation != 0){
138134 List<Coord> points = line.getPoints();
139135 if (minPointsRequired == 4)
3939
4040 private int maxSize;
4141
42 @Override
4243 public void init(FilterConfig config) {
4344 int shift = config.getShift();
4445 if (shift > 15)
5051 // divided by the maximum allowed size - so if the height and
5152 // width are not too large, the result will be <= 1.0
5253 public static double testDims(int height, int width) {
53 return (double)Math.max(Math.abs(height), Math.abs(width)) / MAX_SIZE;
54 return (double) Math.max(Math.abs(height), Math.abs(width)) / MAX_SIZE;
5455 }
5556
5657 /**
6061 * @param element A map element.
6162 * @param next This is used to pass the possibly transformed element onward.
6263 */
64 @Override
6365 public void doFilter(MapElement element, MapFilterChain next) {
6466 // We do not deal with shapes.
6567 assert !(element instanceof MapShape) && element instanceof MapLine;
8587
8688 MapLine l = line.copy();
8789
88 List<Coord> coords = new ArrayList<Coord>();
90 List<Coord> coords = new ArrayList<>();
8991 boolean first = true;
9092
9193 /**
135137 // Add points while not too big and then start again with a fresh line.
136138 for (Coord co: points){
137139 dim.addToBounds(co);
138 if (dim.getMaxDim() > maxSize) {
140 if (prev != null && dim.getMaxDim() > maxSize) {
139141 if (first)
140142 log.debug("bigness saving first part");
141143 else
147149
148150 first = false;
149151 dim.reset();
150 coords = new ArrayList<Coord>();
152 coords = new ArrayList<>();
151153 coords.add(prev);
152154 dim.addToBounds(prev);
153155 dim.addToBounds(co);
172174 * @return a reference to a new list of points
173175 */
174176 private static List<Coord> splitLinesToMaxSize(List<Coord> coords, int maxSize){
175 List<Coord> testedCoords = new ArrayList<Coord>(coords);
177 List<Coord> testedCoords = new ArrayList<>(coords);
176178 int posToTest = coords.size() -2;
177179 while (posToTest >= 0){
178180 Coord p1 = testedCoords.get(posToTest);
4242
4343 private int level;
4444 private boolean isRoutable;
45
46 @Override
4547 public void init(FilterConfig config) {
4648 this.level = config.getLevel();
4749 this.isRoutable = config.hasNet();
5658 * @param element A map element.
5759 * @param next This is used to pass the possibly transformed element onward.
5860 */
61 @Override
5962 public void doFilter(MapElement element, MapFilterChain next) {
6063 // We do not deal with shapes.
6164 assert !(element instanceof MapShape) && element instanceof MapLine;
104107 if (remaining <= MAX_POINTS_IN_LINE) {
105108 last = true;
106109 wantedSize = remaining;
107 } else if (remaining < 2 * MAX_POINTS_IN_LINE)
110 } else if (remaining < 2 * MAX_POINTS_IN_LINE) {
108111 wantedSize = remaining / 2 + 1;
112 }
109113 }
110114 }
111115 }
3333 * @param config Configuration information, giving parameters of the map
3434 * level that is being produced through this filter.
3535 */
36 public void init(FilterConfig config);
36 default void init(FilterConfig config){}
3737
3838 /**
3939 * Filter an element. The filter looks at the element and can simply
5151 * @param element A map element.
5252 * @param next This is used to pass the possibly transformed element onward.
5353 */
54 public void doFilter(MapElement element, MapFilterChain next);
54 void doFilter(MapElement element, MapFilterChain next);
5555 }
3434 *
3535 * @param element The map element.
3636 */
37 public void doFilter(MapElement element);
37 void doFilter(MapElement element);
3838
3939 }
3131 protected int shift;
3232 protected int resolution;
3333
34 @Override
3435 public void init(FilterConfig config) {
3536 shift = config.getShift();
3637 resolution = config.getResolution();
3030
3131 public static final int MAX_POINT_IN_ELEMENT = 250;
3232
33 // public PolygonSplitterFilter() {
34 // }
35
3633 /**
3734 * This filter splits a polygon if any of the subsequent filters throws a
3835 * {@link MustSplitException}.
4138 * @param element A map element, only polygons will be processed.
4239 * @param next This is used to pass the possibly transformed element onward.
4340 */
41 @Override
4442 public void doFilter(MapElement element, MapFilterChain next) {
4543 assert element instanceof MapShape;
4644 MapShape shape = (MapShape) element;
4846 try {
4947 next.doFilter(shape);
5048 } catch (MustSplitException e) {
51 List<MapShape> outputs = new ArrayList<MapShape>();
49 List<MapShape> outputs = new ArrayList<>();
5250 split(shape, outputs); // split in half
5351 for (MapShape s : outputs) {
5452 doFilter(s, next); // recurse as components could still be too big
3737 * @param config configuration information, giving parameters of the map level
3838 * that is being produced through this filter.
3939 */
40 @Override
4041 public void init(FilterConfig config) {
4142 int shift = config.getShift();
4243 if (shift > 15)
5152 * @param element A map element, only polygons will be processed.
5253 * @param next This is used to pass the possibly transformed element onward.
5354 */
55 @Override
5456 public void doFilter(MapElement element, MapFilterChain next) {
5557 assert element instanceof MapShape;
5658 MapShape shape = (MapShape) element;
6163 return;
6264 }
6365
64 List<MapShape> outputs = new ArrayList<MapShape>();
66 List<MapShape> outputs = new ArrayList<>();
6567
6668 // Do an initial split
6769 split(shape, outputs);
2929 public class RemoveEmpty implements MapFilter {
3030 private static final Logger log = Logger.getLogger(RemoveEmpty.class);
3131
32 public void init(FilterConfig config) {
33 }
34
3532 /**
3633 * If this is a line (or a shape, which extends a line) then we check
3734 * to see if it is empty or only a single point. If it is then it
4037 * @param element A map element.
4138 * @param next This is used to pass the possibly transformed element onward.
4239 */
40 @Override
4341 public void doFilter(MapElement element, MapFilterChain next) {
4442 if (element instanceof MapShape) {
4543 MapShape mapShape = (MapShape) element;
3232 final Coord[] areaTest = new Coord[3];
3333
3434 private boolean checkPreserved;
35
36 @Override
3537 public void init(FilterConfig config) {
3638 checkPreserved = config.getLevel() == 0 && config.hasNet();
3739 }
4042 * @param element A map element that will be a line or a polygon.
4143 * @param next This is used to pass the possibly transformed element onward.
4244 */
45 @Override
4346 public void doFilter(MapElement element, MapFilterChain next) {
4447 MapLine line = (MapLine) element;
4548 List<Coord> points = line.getPoints();
2727 private boolean keepNodes;
2828 private int level;
2929
30 @Override
3031 public void init(FilterConfig config) {
3132 shift = config.getShift();
3233 keepNodes = config.getLevel() == 0 && config.hasNet();
3738 * @param element A map element that will be a line or a polygon.
3839 * @param next This is used to pass the possibly transformed element onward.
3940 */
41 @Override
4042 public void doFilter(MapElement element, MapFilterChain next) {
4143 MapLine line = (MapLine) element;
4244 if(shift == 0) {
3939 public class ShapeMergeFilter{
4040 private static final Logger log = Logger.getLogger(ShapeMergeFilter.class);
4141 private final int resolution;
42 private final static ShapeHelper DUP_SHAPE = new ShapeHelper(new ArrayList<Coord>(0));
42 private static final ShapeHelper DUP_SHAPE = new ShapeHelper(new ArrayList<>(0));
4343 private final boolean orderByDecreasingArea;
4444
4545 public ShapeMergeFilter(int resolution, boolean orderByDecreasingArea) {
159159 sharedPoints.add(c);
160160 }
161161 }
162 if (sharedPoints.size() == 0 || sh0.getPoints().size() - sharedPoints.size()> PolygonSplitterFilter.MAX_POINT_IN_ELEMENT) {
162 if (sharedPoints.isEmpty() || sh0.getPoints().size() - sharedPoints.size()> PolygonSplitterFilter.MAX_POINT_IN_ELEMENT) {
163163 // merge will not work
164164 noMerge.add(sh0);
165165 continue;
286286 * @return merged shape or 1st shape if no common point found or {@code dupShape}
287287 * if both shapes describe the same area.
288288 */
289 private ShapeHelper tryMerge(ShapeHelper sh1, ShapeHelper sh2) {
289 private static ShapeHelper tryMerge(ShapeHelper sh1, ShapeHelper sh2) {
290290
291291 // both clockwise or both ccw ?
292292 boolean sameDir = sh1.areaTestVal > 0 && sh2.areaTestVal > 0 || sh1.areaTestVal < 0 && sh2.areaTestVal < 0;
397397 }
398398 pos++;
399399 }
400 return;
401400 }
402401
403402 /**
483482 }
484483
485484 private static class ShapeHelper {
486 final private List<Coord> points;
485 private final List<Coord> points;
487486 long id;
488487 long areaTestVal;
489488
503502 }
504503 }
505504
506 public final static long SINGLE_POINT_AREA = 1L << Coord.DELTA_SHIFT * 1L << Coord.DELTA_SHIFT;
505 public static final long SINGLE_POINT_AREA = 1L << Coord.DELTA_SHIFT * 1L << Coord.DELTA_SHIFT;
507506
508507 /**
509508 * Calculate the high precision area size test value.
514513 public static long calcAreaSizeTestVal(List<Coord> points){
515514 if (points.size() < 4)
516515 return 0; // straight line cannot enclose an area
517 if (points.get(0).highPrecEquals(points.get(points.size()-1)) == false){
516 if (!points.get(0).highPrecEquals(points.get(points.size()-1))){
518517 log.error("shape is not closed");
519518 return 0;
520519 }
527526 signedAreaSize += (long) (c2.getHighPrecLon() + c1.getHighPrecLon())
528527 * (c1.getHighPrecLat() - c2.getHighPrecLat());
529528 }
530 if (Math.abs(signedAreaSize) < SINGLE_POINT_AREA){
531 if (log.isDebugEnabled()) {
532 log.debug("very small shape near", points.get(0).toOSMURL(), "signed area in high prec map units:", signedAreaSize );
533 }
529 if (Math.abs(signedAreaSize) < SINGLE_POINT_AREA && log.isDebugEnabled()) {
530 log.debug("very small shape near", points.get(0).toOSMURL(), "signed area in high prec map units:", signedAreaSize );
534531 }
535532 return signedAreaSize;
536533 }
552549 }
553550 String n1 = o1.getName();
554551 String n2 = o2.getName();
555 if (n1 == n2)
556 return 0;
552
557553 if (n1 == null) {
558554 return (n2 == null) ? 0 : 1;
559555 }
3232 size = s;
3333 }
3434
35 @Override
3536 public void init(FilterConfig config) {
3637 minSize = size * (1 << config.getShift());
3738 // don't remove roads on level 0
4546 * @param element A map element that will be a line or a polygon.
4647 * @param next This is used to pass the possibly transformed element onward.
4748 */
49 @Override
4850 public void doFilter(MapElement element, MapFilterChain next) {
4951 MapLine line = (MapLine) element;
5052
2323 import uk.me.parabola.mkgmap.general.MapLine;
2424
2525 /**
26 * This is a filter that smooths out lines at low resolutions. If the element
26 * This is a filter that smoothes out lines at low resolutions. If the element
2727 * has no size at all at the given resolution, then it is not passed on down
2828 * the chain at all is excluded from the map at that resolution.
2929 *
3535
3636 private int shift;
3737
38 @Override
3839 public void init(FilterConfig config) {
3940 this.shift = config.getShift();
4041 }
5758 * @param element A map element that will be a line or a polygon.
5859 * @param next This is used to pass the possibly transformed element onward.
5960 */
61 @Override
6062 public void doFilter(MapElement element, MapFilterChain next) {
6163 MapLine line = (MapLine) element;
6264
7678 }
7779
7880 // Create a new list to rewrite the points into.
79 List<Coord> coords = new ArrayList<Coord>(n);
81 List<Coord> coords = new ArrayList<>(n);
8082
8183 // Get the step size, we want to place a point every time the
8284 // average exceeds this size.
4646 return null;
4747
4848 class LineCollector {
49 private final List<List<Coord>> ret = new ArrayList<List<Coord>>(4);
49 private final List<List<Coord>> ret = new ArrayList<>(4);
5050 private List<Coord> currentLine;
5151 private Coord last;
5252
5757 // we start a new line if there isn't a current one, or if the first
5858 // point of the segment is not equal to the last one in the line.
5959 if (currentLine == null || !segment[0].equals(last)) {
60 currentLine = new ArrayList<Coord>(5);
60 currentLine = new ArrayList<>(5);
6161 currentLine.add(segment[0]);
6262 currentLine.add(segment[1]);
6363 ret.add(currentLine);
3636 * @param p The coordinates of the point to add. The type here
3737 * will change to Node.
3838 */
39 public void addToBounds(Coord p);
39 void addToBounds(Coord p);
4040
4141 /**
4242 * Add a point to the map.
4343 *
4444 * @param point The point to add.
4545 */
46 public void addPoint(MapPoint point);
46 void addPoint(MapPoint point);
4747
4848 /**
4949 * Add a line to the map.
5050 *
5151 * @param line The line information.
5252 */
53 public void addLine(MapLine line);
53 void addLine(MapLine line);
5454
5555 /**
5656 * Add the given shape (polygon) to the map. A shape is very similar to
5959 *
6060 * @param shape The polygon to add.
6161 */
62 public void addShape(MapShape shape);
62 void addShape(MapShape shape);
6363
6464
6565 /**
6767 * differently so that we can join up roads that are split into several
6868 * segments and to do routing etc.
6969 */
70 public void addRoad(MapRoad road);
70 void addRoad(MapRoad road);
7171
7272 /**
7373 * Add a routing restriction to the map. This is something such as
7474 * no left turn.
7575 * @param exceptMask For exceptions eg. no-left-turn except for buses.
7676 */
77 public int addRestriction(GeneralRouteRestriction grr);
77 int addRestriction(GeneralRouteRestriction grr);
7878 }
7979
4141 public class MapDetails implements MapCollector, MapDataSource {
4242 private static final Logger log = Logger.getLogger(MapDetails.class);
4343
44 private final List<MapLine> lines = new ArrayList<MapLine>();
45 private final List<MapShape> shapes = new ArrayList<MapShape>();
46 private final List<MapPoint> points = new ArrayList<MapPoint>();
44 private final List<MapLine> lines = new ArrayList<>();
45 private final List<MapShape> shapes = new ArrayList<>();
46 private final List<MapPoint> points = new ArrayList<>();
4747
4848 private int minLatHp = Integer.MAX_VALUE;
4949 private int minLonHp = Integer.MAX_VALUE;
5151 private int maxLonHp = Integer.MIN_VALUE;
5252
5353 // Keep lists of all items that were used.
54 private final Map<Integer, Integer> pointOverviews = new HashMap<Integer, Integer>();
55 private final Map<Integer, Integer> lineOverviews = new HashMap<Integer, Integer>();
56 private final Map<Integer, Integer> shapeOverviews = new HashMap<Integer, Integer>();
54 private final Map<Integer, Integer> pointOverviews = new HashMap<>();
55 private final Map<Integer, Integer> lineOverviews = new HashMap<>();
56 private final Map<Integer, Integer> shapeOverviews = new HashMap<>();
5757
5858 private final RoadNetwork roadNetwork = new RoadNetwork();
5959
196196 * @return A list of overviews.
197197 */
198198 public List<Overview> getOverviews() {
199 List<Overview> ovlist = new ArrayList<Overview>();
199 List<Overview> ovlist = new ArrayList<>();
200200
201201 for (Map.Entry<Integer, Integer> ent : pointOverviews.entrySet()) {
202202 Overview ov = new PointOverview(ent.getKey(), ent.getValue());
1515 package uk.me.parabola.mkgmap.general;
1616
1717 import java.util.Arrays;
18 import java.util.Objects;
1819
1920 import uk.me.parabola.imgfmt.app.Coord;
2021 import uk.me.parabola.imgfmt.app.trergn.ExtTypeAttributes;
174175 }
175176
176177 public void setIsIn(String isIn) {
177 if(isIn != null)
178 this.isIn = isIn.toUpperCase();
179 }
180
178 if (isIn != null) {
179 this.isIn = isIn.toUpperCase();
180 }
181 }
181182
182183 /**
183184 * This is the type code that goes in the .img file so that the GPS device
201202 if (this.type != other.type)
202203 return false;
203204
204 String thisName = getName();
205 String otherName = other.getName();
206
207 if (thisName == null && otherName == null)
208 return true;
209 if (thisName!=null && otherName!=null && thisName.equals(otherName))
210 return true;
211 return false;
205 return Objects.equals(getName(), other.getName());
212206 }
213207
214208 public boolean hasExtendedType() {
6767 this.points = points;
6868 // preserve first and last point, so that points which are shared by
6969 // different ways are kept
70 if (points.size() > 0 && this instanceof MapShape == false){
70 if (points.size() > 0 && !(this instanceof MapShape)) {
7171 points.get(0).preserved(true);
7272 points.get(points.size()-1).preserved(true);
7373 }
2828 import java.time.Instant;
2929 import java.util.ArrayList;
3030 import java.util.Arrays;
31 import java.util.Collections;
3132 import java.util.Date;
3233 import java.util.HashMap;
3334 import java.util.HashSet;
222223 }
223224
224225 private static Set<String> getValidOptions(PrintStream err) {
225 String path = "/help/en/options";
226 try (InputStream stream = Main.class.getResourceAsStream(path)) {
226 try (InputStream stream = Main.class.getResourceAsStream("/help/en/options")) {
227227 if (stream == null)
228 return null;
229
230 Set<String> result = new HashSet<>();
228 return Collections.emptySet();
229
230 Set<String> knownOptions = new HashSet<>();
231231 BufferedReader r = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
232232
233233 Pattern p = Pattern.compile("^--?([a-zA-Z0-9-]*).*$");
235235 while ((line = r.readLine()) != null) {
236236 Matcher matcher = p.matcher(line);
237237 if (matcher.matches()) {
238 String opt = matcher.group(1);
239 result.add(opt);
238 knownOptions.add(matcher.group(1));
240239 }
241240 }
242 return result;
241 return knownOptions;
243242
244243 } catch (IOException e) {
245244 err.println("Could not read valid options");
246 return null;
245 return Collections.emptySet();
247246 }
248247 }
249248
283282 args.setSort(getSort(args));
284283
285284 log.info("Submitting job " + filename);
286 FilenameTask task = new FilenameTask(new Callable<String>() {
287 public String call() {
288 log.threadTag(filename);
289 if (filename.startsWith("test-map:") || new File(filename).exists()){
290 String output = mp.makeMap(args, filename);
291 log.debug("adding output name", output);
292 log.threadTag(null);
293 return output;
294 } else {
295 log.error("file " + filename + " doesn't exist");
296 return null;
297 }
285 FilenameTask task = new FilenameTask(() -> {
286 log.threadTag(filename);
287 if (filename.startsWith("test-map:") || new File(filename).exists()){
288 String output = mp.makeMap(args, filename);
289 log.debug("adding output name", output);
290 log.threadTag(null);
291 return output;
292 } else {
293 log.error("input file '" + filename + "' doesn't exist");
294 return null;
298295 }
299296 });
300297 task.setArgs(args);
317314 // This option always appears first. We use it to turn on/off
318315 // generation of the overview files if there is only one file
319316 // to process.
320 int n = Integer.valueOf(val);
317 int n = Integer.parseInt(val);
321318 if (n > 0) // TODO temporary, this option will become properly default of on.
322319 createTdbFiles = true;
323320
361358 }
362359
363360 public void removeOption(String opt) {
364 if (Objects.equals("tdbfile", opt))
361 if ("tdbfile".equals(opt))
365362 createTdbFiles = false;
366363 }
367364
438435 }
439436 ++checked;
440437 boolean performChecks = true;
441 if (Objects.equals("classpath:styles", styleFile) && !Objects.equals("default", name)){
442 performChecks = false;
438 if ("classpath:styles".equals(styleFile) && !"default".equals(name)) {
439 performChecks = false;
443440 }
444441 Style style = readOneStyle(name, performChecks);
445442 if (style == null){
547544 // save the result for later use
548545 future.setFilename(future.get());
549546 filenames.add(future);
550 } else
547 } else {
551548 Thread.sleep(100);
549 }
552550 } catch (ExecutionException e) {
553551 // Re throw the underlying exception
554552 Throwable cause = e.getCause();
691689 }
692690 }
693691
692
694693 private void fileOptions(CommandArgs args) {
695694 boolean indexOpt = args.exists("index");
696695 boolean gmapsuppOpt = args.exists("gmapsupp");
4444 drawStreetnames(map, div, lat, lng);
4545 }
4646
47 private void drawStreetnames(Map map, Subdivision div, double slat, double slon) {
47 private static void drawStreetnames(Map map, Subdivision div, double slat, double slon) {
4848
4949 char[] hexChars = "0123456789ABCDEF".toCharArray();
5050
6363 String fname = OverviewBuilder.getOverviewImgName(args.getMapname());
6464
6565 File f = new File(args.getOutputDir(), fname);
66 if (f.exists() && f.isFile()) {
67 try {
68 Files.delete(f.toPath());
69 log.warn("removed " + f);
70 } catch (IOException e) {
71 log.warn("removing " + f + "failed with " + e.getMessage());
72 }
73 }
66 tryRemove(f);
7467 }
7568 }
7669 return makeMap(args, src, "");
8174 } catch (FileNotFoundException e) {
8275 System.err.println("Could not open file: " + filename);
8376 return filename;
77 }
78 }
79
80 private static void tryRemove(File f) {
81 if (f.exists() && f.isFile()) {
82 try {
83 Files.delete(f.toPath());
84 log.warn("removed " + f);
85 } catch (IOException e) {
86 log.warn("removing " + f + "failed with " + e.getMessage());
87 }
8488 }
8589 }
8690
148152 * @throws FileNotFoundException For non existing files.
149153 * @throws FormatException When the file format is not valid.
150154 */
151 private static LoadableMapDataSource loadFromFile(CommandArgs args, String name) throws
152 FileNotFoundException, FormatException
153 {
155 private static LoadableMapDataSource loadFromFile(CommandArgs args, String name) throws FileNotFoundException {
154156 LoadableMapDataSource src = MapReader.createMapReader(name);
155157 src.config(args.getProperties());
156158 log.info("Started loading", name);
157 src.load(name, args.getProperties().getProperty("transparent", false) == false);
159 src.load(name, !args.getProperties().getProperty("transparent", false));
158160 log.info("Finished loading", name);
159161 return src;
160162 }
3232 * @param filename The input filename.
3333 * @return The output filename; the name of the file that was created.
3434 */
35 public String makeMap(CommandArgs args, String filename);
35 String makeMap(CommandArgs args, String filename);
3636 }
1313 package uk.me.parabola.mkgmap.main;
1414
1515 import java.io.BufferedReader;
16 import java.io.FileInputStream;
1617 import java.io.FileNotFoundException;
17 import java.io.FileInputStream;
18 import java.io.InputStreamReader;
19 import java.nio.charset.StandardCharsets;
2018 import java.io.FileWriter;
2119 import java.io.IOException;
2220 import java.io.InputStream;
21 import java.io.InputStreamReader;
2322 import java.io.PrintStream;
2423 import java.io.PrintWriter;
2524 import java.io.Reader;
2625 import java.lang.reflect.Field;
26 import java.nio.charset.StandardCharsets;
2727 import java.util.ArrayList;
2828 import java.util.Arrays;
2929 import java.util.Collections;
30 import java.util.Formatter;
3130 import java.util.List;
3231 import java.util.Locale;
3332 import java.util.Objects;
168167 showMatches = true;
169168 } else if (s.startsWith("--no-print")) {
170169 print = false;
171 } else
170 } else {
172171 a.add(s);
172 }
173173 }
174174 return a.toArray(new String[a.size()]);
175175 }
238238 List<String> actual = new ArrayList<>();
239239 List<String> expected = new ArrayList<>();
240240 for (Way w : ways) {
241 OsmConverter normal = new StyleTester("styletester.style", new LocalMapCollector(results), false);
241 OsmConverter normal = new StyleTester(STYLETESTER_STYLE, new LocalMapCollector(results), false);
242242
243243 String prefix = "WAY " + w.getId() + ": ";
244244 normal.convertWay(w.copy());
247247 results.clear();
248248
249249 if (!noStrict) {
250 OsmConverter strict = new StyleTester("styletester.style", new LocalMapCollector(strictResults), true);
250 OsmConverter strict = new StyleTester(STYLETESTER_STYLE, new LocalMapCollector(strictResults), true);
251251 strict.convertWay(w.copy());
252252 strict.end();
253253 expected.addAll(formatResults(prefix, strictResults));
294294 return givenResults;
295295 }
296296
297 @Override
297298 public void convertWay(Way way) {
298299 converter.convertWay(way);
299300 }
300301
302 @Override
301303 public void convertNode(Node node) {
302304 converter.convertNode(node);
303305 }
304306
307 @Override
305308 public void convertRelation(Relation relation) {
306309 converter.convertRelation(relation);
307310 }
308311
312 @Override
309313 public void setBoundingBox(Area bbox) {
310314 converter.setBoundingBox(bbox);
311315 }
312316
317 @Override
313318 public void end() {
314319 converter.end();
315 }
316
317 @Override
318 public Boolean getDriveOnLeft() {
319 return null; // unknown
320320 }
321321
322322 private static void printResult(String prefix, List<String> results) {
342342 // read the rest of the file
343343 readStyles(br, line);
344344 }
345 /*else if ("".equals(line) || line.startsWith("#")) {
346 // ignore blank lines.
347 }*/
348345 }
349346
350347 return ways;
408405 * toString methods on MapLine and MapRoad.
409406 */
410407 private static String lineToString(MapLine el) {
411 Formatter fmt = new Formatter();
412 fmt.format("Line 0x%x, labels=%s, res=%d-%d",
408 StringBuilder sb = new StringBuilder();
409 sb.append(String.format("Line 0x%x, labels=%s, res=%d-%d",
413410 el.getType(), Arrays.toString(el.getLabels()),
414 el.getMinResolution(), el.getMaxResolution());
411 el.getMinResolution(), el.getMaxResolution()));
415412 if (el.isDirection())
416 fmt.format(" oneway");
417
418 fmt.format(" ");
413 sb.append(" oneway");
414
415 sb.append(' ');
419416 for (Coord co : el.getPoints())
420 fmt.format("(%s),", co);
421
422 return fmt.toString();
417 sb.append(String.format("(%s),", co));
418
419 return sb.toString();
423420 }
424421
425422 /**
427424 * toString methods on MapLine and MapRoad.
428425 */
429426 private static String roadToString(MapRoad el) {
430 StringBuffer sb = new StringBuffer(lineToString(el));
431 sb.delete(0, 4);
432 sb.insert(0, "Road");
433 Formatter fmt = new Formatter(sb);
434 fmt.format(" road class=%d speed=%d", el.getRoadDef().getRoadClass(),
435 getRoadSpeed(el.getRoadDef()));
436 return fmt.toString();
427 StringBuilder sb = new StringBuilder(lineToString(el));
428 sb.replace(0, 4, "Road");
429 sb.append(String.format(" road class=%d speed=%d", el.getRoadDef().getRoadClass(),
430 getRoadSpeed(el.getRoadDef())));
431 return sb.toString();
437432 }
438433
439434 /**
483478 * @param coll A map collector to receive the created elements.
484479
485480 */
486 private StyledConverter makeStyleConverter(String styleFile, MapCollector coll) throws FileNotFoundException {
481 private static StyledConverter makeStyleConverter(String styleFile, MapCollector coll) throws FileNotFoundException {
487482 Style style = new StyleImpl(styleFile, null);
488483 return new StyledConverter(style, coll, new EnhancedProperties());
489484 }
545540 * @return A simple list of rules with a resolving method that applies
546541 * each rule in turn to the element until there is match.
547542 */
543 @Override
548544 public Rule getWayRules() {
549545 ReferenceRuleSet r = new ReferenceRuleSet();
550546 r.addAll((ReferenceRuleSet) getLineRules());
559555 *
560556 * @return A Reference rule set of the lines.
561557 */
558 @Override
562559 public Rule getLineRules() {
563560 ReferenceRuleSet r = new ReferenceRuleSet();
564561
579576 *
580577 * @return A Reference rule set of the polygons.
581578 */
579 @Override
582580 public Rule getPolygonRules() {
583581 ReferenceRuleSet r = new ReferenceRuleSet();
584582
592590 return r;
593591 }
594592
593 @Override
595594 public Rule getRelationRules() {
596595 ReferenceRuleSet r = new ReferenceRuleSet();
597596
605604 return r;
606605 }
607606
607 @Override
608608 public Set<String> getUsedTags() {
609609 return null;
610610 }
646646 if (showMatches) {
647647 if (a.isFound()) {
648648 out.println("# Matched: " + rule);
649 } else if (a.isActionsOnly())
649 } else if (a.isActionsOnly()) {
650650 out.println("# Matched for actions: " + rule);
651 }
651652 }
652653
653654 if (a.isResolved())
693694 if (rule.containsExpression(exp))
694695 return true;
695696 }
696 if (getFinalizeRule()!= null && getFinalizeRule().containsExpression(exp))
697 return true;
698 return false;
697 return getFinalizeRule()!= null && getFinalizeRule().containsExpression(exp);
699698 }
700699 }
701700
7474 * @param n The number of characters in the first block. The minimum size of the TYP file is
7575 * less than the buffer size.
7676 */
77 private void writeAlteredTyp(String outFilename, FileInputStream in, byte[] buf, int n) {
77 private static void writeAlteredTyp(String outFilename, FileInputStream in, byte[] buf, int n) {
7878 try (FileOutputStream out = new FileOutputStream(outFilename)) {
7979 do {
8080 out.write(buf, 0, n);
9696 * @param path The original name
9797 * @return The modified name.
9898 */
99 private String makeOutName(String path) {
99 private static String makeOutName(String path) {
100100 File f = new File(path);
101101 File dir = f.getParentFile();
102102
8282 }
8383
8484 // an action will be performed, so we may have to invalidate the cache
85 boolean invalidate_cache = false;
86 for (Action a : actions){
87 if (a.perform(element)){
88 invalidate_cache = true;
89 }
85 boolean invalidateCache = false;
86 for (Action a : actions) {
87 invalidateCache |= a.perform(element);
9088 }
91 if (invalidate_cache)
89 if (invalidateCache)
9290 cacheId++;
9391
9492 if (type != null && finalizeRule != null) {
142140 }
143141
144142 public String toString() {
145 StringBuilder fmt = new StringBuilder();
143 StringBuilder sb = new StringBuilder();
146144 if (expression != null)
147 fmt.append(expression);
145 sb.append(expression);
148146
149 fmt.append(" {");
147 sb.append(" {");
150148 for (Action a : actions)
151 fmt.append(a);
152 fmt.append("}");
149 sb.append(a);
150 sb.append("}");
153151
154152 if (type != null) {
155 fmt.append(' ');
156 fmt.append(type);
153 sb.append(' ');
154 sb.append(type);
157155 }
158156
159 return fmt.toString();
157 return sb.toString();
160158 }
161159
162160 public void setFinalizeRule(Rule finalizeRule) {
5050 public class CombinedStyleFileLoader extends StyleFileLoader {
5151 private static final Logger log = Logger.getLogger(CombinedStyleFileLoader.class);
5252
53 private final Map<String, String> files = new HashMap<String, String>();
53 private final Map<String, String> files = new HashMap<>();
5454 private final String styleName;
5555 private static final Pattern STYLE_SUFFIX = Pattern.compile("\\.style$");
5656 private static final Pattern FILENAME_START_MARK = Pattern.compile("<<<");
7171 private void loadFiles(Reader in) {
7272 BufferedReader r = new BufferedReader(in);
7373
74 StringBuffer currentFile = new StringBuffer();
74 StringBuilder currentFile = new StringBuilder();
7575 try {
7676 String line;
7777 String currentName = null;
8686 line = FILENAME_END_MARK.matcher(line).replaceFirst("");
8787 log.debug("reading file", line);
8888 currentName = line;
89 currentFile = new StringBuffer();
89 currentFile = new StringBuilder();
9090 } else {
91 currentFile.append(line);
92 currentFile.append('\n');
91 currentFile.append(line).append('\n');
9392 }
9493 }
9594 if (currentName == null) {
169168 }
170169
171170 private static void convertToDirectory(String name, String dirname) throws IOException {
172 CombinedStyleFileLoader loader = new CombinedStyleFileLoader(name);
173 File dir = new File(dirname);
174 dir.mkdir();
175 for (String s : loader.files.keySet()) {
176 File ent = new File(dir, s);
177 ent.getParentFile().mkdirs();
178 FileWriter writer = new FileWriter(ent);
179 BufferedReader r = null;
180 try {
181 r = new BufferedReader(loader.open(s));
182 String line;
183 while ((line = r.readLine()) != null) {
184 writer.write(line);
185 writer.write('\n');
186 }
187 } finally {
188 if (r != null) r.close();
189 writer.close();
190 }
191 }
192 loader.close();
171 try (CombinedStyleFileLoader loader = new CombinedStyleFileLoader(name)) {
172 File dir = new File(dirname);
173 dir.mkdir();
174 for (String s : loader.files.keySet()) {
175 File ent = new File(dir, s);
176 ent.getParentFile().mkdirs();
177 try (FileWriter writer = new FileWriter(ent);
178 BufferedReader r = new BufferedReader(loader.open(s))) {
179 String line;
180 while ((line = r.readLine()) != null) {
181 writer.write(line);
182 writer.write('\n');
183 }
184 }
185 }
186 }
193187 }
194188
195189 private static void convertToFile(File file, PrintStream out) throws IOException {
208202 out.print(entry.getName());
209203 out.println(">>>");
210204
211 BufferedReader r = new BufferedReader(new FileReader(entry));
212 String line;
213 while ((line = r.readLine()) != null)
214 out.println(line);
215 r.close();
205 try (BufferedReader r = new BufferedReader(new FileReader(entry))) {
206 String line;
207 while ((line = r.readLine()) != null)
208 out.println(line);
209 }
216210 } else {
217211 convertToFile(out, entry.listFiles(new NoHiddenFilter()), entry.getName());
218212 }
5050 this.way = way;
5151 this.gt = type;
5252 // note that the gt.getType() may not be a routable type when overlays are used
53 if (type.isRoad() && MapObject.hasExtendedType(gt.getType()) == false) {
53 if (type.isRoad() && !MapObject.hasExtendedType(gt.getType())) {
5454 this.roadClass = (byte) gt.getRoadClass();
5555 this.roadSpeed = (byte) gt.getRoadSpeed();
5656 recalcRoadClass(way);
121121 * @param el an element
122122 * @return {@code true} the road class has been changed, else {@code false}
123123 */
124 private final static short roadClassTagKey = TagDict.getInstance().xlate("mkgmap:road-class");
124 private static final short TKM_ROAD_CLASS = TagDict.getInstance().xlate("mkgmap:road-class");
125125 public boolean recalcRoadClass(Element el) {
126126 // save the original road class value
127127 byte oldRoadClass = roadClass;
128128
129 String val = el.getTag(roadClassTagKey);
129 String val = el.getTag(TKM_ROAD_CLASS);
130130 if (val != null) {
131131 if (val.startsWith("-")) {
132132 roadClass -= Byte.decode(val.substring(1));
166166 * @param el an element
167167 * @return {@code true} the road speed has been changed, else {@code false}
168168 */
169 private final static short roadSpeedTagKey = TagDict.getInstance().xlate("mkgmap:road-speed");
170 private final static short roadSpeedClassTagKey = TagDict.getInstance().xlate("mkgmap:road-speed-class");
169 private static final short TKM_ROAD_SPEED = TagDict.getInstance().xlate("mkgmap:road-speed");
170 private static final short TKM_ROAD_SPEED_CLASS = TagDict.getInstance().xlate("mkgmap:road-speed-class");
171171 public boolean recalcRoadSpeed(Element el) {
172172 // save the original road speed value
173173 byte oldRoadSpeed = roadSpeed;
174174
175175 // check if the road speed is modified
176 String roadSpeedOverride = el.getTag(roadSpeedClassTagKey);
176 String roadSpeedOverride = el.getTag(TKM_ROAD_SPEED_CLASS);
177177 if (roadSpeedOverride != null) {
178178 try {
179179 byte rs = Byte.decode(roadSpeedOverride);
193193 }
194194
195195 // check if the road speed should be modified more
196 String val = el.getTag(roadSpeedTagKey);
196 String val = el.getTag(TKM_ROAD_SPEED);
197197 if(val != null) {
198198 if(val.startsWith("-")) {
199199 roadSpeed -= Byte.decode(val.substring(1));
227227 }
228228
229229 public boolean isValid() {
230 if (way == null)
231 return false;
232 if (way.getPoints() == null || way.getPoints().size()<2)
233 return false;
234 return true;
230 return way != null && way.getPoints().size() >= 2;
235231 }
236232
237233 public String toString(){
6060 * Nothing needs doing in this case.
6161 */
6262 public void close() {
63 // Nothing to do
6364 }
6465
6566 public String[] list() {
6667 log.debug("dir list", dir);
67 List<String> res = new ArrayList<String>();
68 List<String> res = new ArrayList<>();
6869
6970 File[] allFiles = dir.listFiles();
7071 if (allFiles != null) {
145145 *
146146 * The output is (a & c) | (b & c) | ...
147147 */
148 private Op distribute(Op op) {
148 private static Op distribute(Op op) {
149149 Op ab = op.getFirst();
150150 Op a = ab.getFirst();
151151 Op b = ab.getSecond();
179179 /**
180180 * Order the child nodes so that the 'best' one is on the left (first).
181181 */
182 private void orderBest(Op op) {
182 private static void orderBest(Op op) {
183183 assert OPERATORS.contains(op.getType());
184184
185185 if (leftNodeWeight(op.getFirst()) > leftNodeWeight(op.getSecond())) {
192192 *
193193 * We prefer AND to OR and prefer everything else to AND.
194194 */
195 private int leftNodeWeight(Op op) {
195 private static int leftNodeWeight(Op op) {
196196 switch (op.getType()) {
197197 case AND: return 10;
198198 case OR: return 20;
205205 *
206206 * Each node that is preceded by NOT is inverted.
207207 */
208 private Op removeAllNot(Op expr) {
208 private static Op removeAllNot(Op expr) {
209209 if (expr == null)
210210 return null;
211211
231231 * @param op This will be a NOT node.
232232 * @return A new expression, could be the same as given.
233233 */
234 private Op removeNot(Op op) {
234 private static Op removeNot(Op op) {
235235 return invert(op.getFirst());
236236 }
237237
238238 /**
239239 * Invert an expression, ie apply NOT to it.
240240 */
241 private Op invert(Op op) {
241 private static Op invert(Op op) {
242242 switch (op.getType()) {
243243 case NOT:
244244 Op f = op.getFirst();
282282 case FUNCTION:
283283 case OPEN_PAREN:
284284 case CLOSE_PAREN:
285 default:
285286 throw new ExitException("Programming error, tried to invert invalid node " + op);
286287 }
287 return null;
288288 }
289289
290290 /**
293293 * This is used when inverting nodes because !(a>0) is (a<=0 | a!=*) because
294294 * the statement is true when the tag does not exist.
295295 */
296 private Op neWith(Op op) {
296 private static Op neWith(Op op) {
297297 return new OrOp().set(
298298 new NotExistsOp().setFirst(op.getFirst()),
299299 op
305305 *
306306 * Eg: given (A&B)&(C&D) we return (A&(B&(C&D)))
307307 */
308 private void reAssociate(Op op, NodeType kind) {
308 private static void reAssociate(Op op, NodeType kind) {
309309 assert op.isType(kind);
310310 assert kind == OR || kind == AND;
311311
331331 * If any of A,B.. happen to be AND terms, then these are merged into the
332332 * chain first. If there is an OR on the left it will float to the back.
333333 */
334 private void arrangeAndChain(Op op) {
334 private static void arrangeAndChain(Op op) {
335335 Op last = op;
336336 List<Op> terms = new ArrayList<>();
337337 terms.add(op.getFirst());
352352 }
353353
354354 if (terms.size() > 1)
355 terms.sort(Comparator.comparingInt(this::selectivity));
355 terms.sort(Comparator.comparingInt(ExpressionArranger::selectivity));
356356
357357 Op current = op;
358358 for (Op o : terms) {
375375 * Ideally you might want to consider tag frequencies, since highway is a very
376376 * common tag, it would be better to push it behind other tags.
377377 */
378 private int selectivity(Op op) {
378 private static int selectivity(Op op) {
379379 // Operations that involve a non-indexable function must always go to the back.
380380 if (op.getFirst().isType(FUNCTION)) {
381381 StyleFunction func = (StyleFunction) op.getFirst();
1616 package uk.me.parabola.mkgmap.osmstyle;
1717
1818 import java.io.BufferedReader;
19 import java.io.Closeable;
1920 import java.io.FileNotFoundException;
2021 import java.io.IOException;
2122 import java.io.InputStream;
4546 *
4647 * @author Steve Ratcliffe
4748 */
48 public class JarFileLoader extends StyleFileLoader {
49 public class JarFileLoader extends StyleFileLoader implements Closeable {
4950 private static final Logger log = Logger.getLogger(JarFileLoader.class);
5051 private JarFile jarFile;
5152 private String prefix;
6263 }
6364 }
6465
65 private String makeJarUrl(String url) {
66 private static String makeJarUrl(String url) {
6667 if (url.toLowerCase().startsWith("jar:"))
6768 return url;
6869 else
9293 * @param style a style name or null to find any version file
9394 * @return return prefix of (first) entry that contains file version
9495 */
95 private String searchVersion(JarFile file, String style) {
96 private static String searchVersion(JarFile file, String style) {
9697 Enumeration<JarEntry> en = file.entries();
9798 String flatEnd = style==null ? "version" : style + "/version";
9899 String end = "/" + flatEnd;
142143 }
143144 }
144145
145 protected void finalize() throws Throwable {
146 super.finalize();
147 close();
148 }
149
150146 public String[] list() {
151147 Enumeration<JarEntry> en = jarFile.entries();
152 List<String> list = new ArrayList<String>();
148 List<String> list = new ArrayList<>();
153149 while (en.hasMoreElements()) {
154150 JarEntry entry = en.nextElement();
155151
1111 */
1212 package uk.me.parabola.mkgmap.osmstyle;
1313
14 import java.util.Arrays;
1514 import java.util.List;
1615 import java.util.Properties;
17 import java.util.regex.Pattern;
1816
1917 import it.unimi.dsi.fastutil.shorts.ShortArrayList;
18 import uk.me.parabola.mkgmap.CommandArgs;
2019 import uk.me.parabola.mkgmap.reader.osm.Element;
2120 import uk.me.parabola.mkgmap.reader.osm.TagDict;
2221 import uk.me.parabola.mkgmap.reader.osm.Tags;
2928 public class NameFinder {
3029 private final ShortArrayList compiledNameTagList;
3130
32 private static final Pattern COMMA_OR_SPACE_PATTERN = Pattern.compile("[,\\s]+");
33 private static final short nameTagKey = TagDict.getInstance().xlate("name");
31 private static final short TK_NAME = TagDict.getInstance().xlate("name");
3432
3533 public NameFinder(Properties props) {
3634 this.compiledNameTagList = computeCompiledNameTags(props);
37 }
38
39 public static List<String> getNameTags(Properties props) {
40 String nameTagProp = props.getProperty("name-tag-list", "name");
41 return Arrays.asList(COMMA_OR_SPACE_PATTERN.split(nameTagProp));
4235 }
4336
4437 /**
4942 private static ShortArrayList computeCompiledNameTags(Properties props) {
5043 if (props == null)
5144 return null;
52 String nameTagProp = props.getProperty("name-tag-list", "name");
53 if ("name".equals(nameTagProp))
45 List<String> nametags = CommandArgs.getNameTags(props);
46 if (nametags.size() == 1 && "name".equals(nametags.get(0)))
5447 return null;
55 return TagDict.compileTags(COMMA_OR_SPACE_PATTERN.split(nameTagProp));
48 return TagDict.compileTags(nametags.toArray(new String[0]));
5649 }
5750
5851
6356 */
6457 public String getName(Element el) {
6558 if (compiledNameTagList == null)
66 return el.getTag(nameTagKey);
59 return el.getTag(TK_NAME);
6760
6861 for (short tagKey : compiledNameTagList) {
6962 String val = el.getTag(tagKey);
8174 */
8275 public String getName(Tags tags) {
8376 if (compiledNameTagList == null)
84 return tags.get(nameTagKey);
77 return tags.get(TK_NAME);
8578
8679 for (short tagKey : compiledNameTagList) {
8780 String val = tags.get(tagKey);
10396 for (short tagKey : compiledNameTagList) {
10497 String val = el.getTag(tagKey);
10598 if (val != null) {
106 if (tagKey != nameTagKey) {
99 if (tagKey != TK_NAME) {
107100 // add or replace name
108 el.addTag(nameTagKey, val);
101 el.addTag(TK_NAME, val);
109102 }
110103 break;
111104 }
3131 import java.util.Set;
3232 import java.util.stream.Collectors;
3333
34 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
35 import uk.me.parabola.imgfmt.Utils;
3436 import uk.me.parabola.imgfmt.app.Coord;
3537 import uk.me.parabola.log.Logger;
3638 import uk.me.parabola.mkgmap.general.MapPoint;
209211 default:
210212 valid = false;
211213 log.error("Invalid Action value", ruleParts[2], "in nearby poi rule", i + 1, rule,
212 "- 'delete-poi', 'delete-name' or'merge-at-mid-point' expected.");
214 "- 'delete-poi', or 'delete-name' expected.");
213215 break;
214216 }
215217 }
325327 if (biggestCloud == null || biggestCloud.isEmpty())
326328 break;
327329
328 final Coord middle = calcMiddle(biggestCloud);
330 final Coord middle = calcMiddle(biggestCloud).getDisplayedCoord();
329331 final Set<MapPoint> done = new HashSet<>(biggestCloud);
330332 removeSimpleDuplicates(biggestCloud);
331333
332334 // select point that is closest to the middle
333335 MapPoint bestPoint = biggestCloud.stream()
334 .min(Comparator.comparingDouble(mp -> middle.distance(mp.getLocation())))
336 .min(Comparator.comparingDouble(mp -> middle.distance(mp.getLocation().getDisplayedCoord())))
335337 .orElse(biggestCloud.iterator().next()); // should not happen, stream is not empty
336338
337339 performAction(rule.action, bestPoint, biggestCloud, toKeep);
345347 }
346348
347349 private void removeSimpleDuplicates(Set<MapPoint> biggestCloud) {
348 Set<Coord> locations = new HashSet<>();
350 Long2ObjectOpenHashMap<MapPoint> locations = new Long2ObjectOpenHashMap<>();
349351 Iterator<MapPoint> iter = biggestCloud.iterator();
350352 while (iter.hasNext()) {
351353 MapPoint mp = iter.next();
352 if (!locations.add(mp.getLocation())) {
354 if (locations.put(Utils.coord2Long(mp.getLocation()), mp) != null) {
353355 if (log.isInfoEnabled()) {
354356 log.info("Removed duplicate", getLogInfo(mp));
355357 }
360362
361363 private static Map<MapPoint, Set<MapPoint>> buildGroups(List<MapPoint> points, int maxDistance, List<MapPoint> toKeep) {
362364 final KdTree<MapPoint> kdTree = new KdTree<>();
363 points.forEach(kdTree::add);
365 points.forEach(kdTree::add); // should better use getDisplayedCoord()
364366 Map<MapPoint, Set<MapPoint>> groupsMap = new LinkedHashMap<>();
365367 for (MapPoint mp : points) {
366368 Set<MapPoint> set = kdTree.findClosePoints(mp, maxDistance);
380382 final MapPoint midPoint = bestPoint;
381383 biggestCloud.stream().filter(mp -> mp != midPoint).forEach(mp -> {
382384 if (log.isInfoEnabled()) {
383 double dist = mp.getLocation().distance(bestPoint.getLocation());
384 log.info(String.format("Removed name from nearby(<%d m)", (long) Math.ceil(dist)), getLogInfo(mp));
385 double dist = mp.getLocation().getDisplayedCoord().distance(bestPoint.getLocation().getDisplayedCoord());
386 log.info(String.format("Removed name from nearby(<= %d m)", (long) Math.ceil(dist)), getLogInfo(mp));
385387 }
386388 mp.setName(null);
387389 });
397399 private void logRemoval(Set<MapPoint> biggestCloud, MapPoint bestPoint) {
398400 for (MapPoint mp : biggestCloud) {
399401 if (mp != bestPoint) {
400 double dist = mp.getLocation().distance(bestPoint.getLocation());
401 log.info(String.format("Removed nearby (<%d m)", (long) Math.ceil(dist)), getLogInfo(mp));
402 double dist = mp.getLocation().getDisplayedCoord().distance(bestPoint.getLocation().getDisplayedCoord());
403 log.info(String.format("Removed nearby (<= %d m)", (long) Math.ceil(dist)), getLogInfo(mp));
402404 }
403405 }
404406 }
409411 double lon = 0;
410412 final int n = points.size();
411413 for (MapPoint mp : points) {
412 Coord p = mp.getLocation();
414 Coord p = mp.getLocation().getDisplayedCoord();
413415 lat += (double) p.getHighPrecLat() / n;
414416 lon += (double) p.getHighPrecLon() / n;
415417 }
428430 if (n != null) {
429431 sb.append(" for element ");
430432 if (FakeIdGenerator.isFakeId(n.getId())) {
431 sb.append("generated from ").append(n.getOriginalId());
433 sb.append("generated from ").append(n.getOrigElement()).append(' ').append(n.getOriginalId());
432434 } else {
433435 sb.append(n.toBrowseURL()).append(" at ").append(n.getLocation().toOSMURL());
434436 }
4545 * it is replaced by three lines with the type 0x12, 0x14, 0x15.
4646 *
4747 * @author Steve Ratcliffe
48 * @see <a href="TODO: find url">Example of the technique</a>
4948 *
5049 */
5150 public class OverlayReader {
52 private final Map<Integer, List<Integer>> overlays = new HashMap<Integer, List<Integer>>();
51 private final Map<Integer, List<Integer>> overlays = new HashMap<>();
5352 private final Reader reader;
5453 private final String filename;
5554
6463 String line = ts.readLine();
6564
6665 // Remove comments before parsing
67 int commentstart = line.indexOf("#");
66 int commentstart = line.indexOf('#');
6867 if (commentstart != -1)
6968 line = line.substring(0, commentstart);
7069
8281 /**
8382 * Read the line of replacements.
8483 */
85 private List<Integer> readReplacements(TokenScanner ts, String line) {
86 List<Integer> l = new ArrayList<Integer>();
84 private static List<Integer> readReplacements(TokenScanner ts, String line) {
85 List<Integer> l = new ArrayList<>();
8786
8887 String[] nums = line.split("[ ,]");
8988 for (String n : nums) {
114113 for (ListIterator<Integer> t=integerList.listIterator(1); t.hasNext(); ) {
115114 newline = new MapLine(line);
116115 newline.setType(t.next());
117 newline.setPoints(new ArrayList<Coord>(points));
116 newline.setPoints(new ArrayList<>(points));
118117 adder.add(newline);
119118 }
120119 } else {
2020 import java.util.Arrays;
2121 import java.util.Collections;
2222 import java.util.HashMap;
23 import java.util.HashSet;
2423 import java.util.LinkedHashSet;
2524 import java.util.List;
2625 import java.util.Map;
2726 import java.util.Set;
27 import java.util.stream.Collectors;
2828
2929 import uk.me.parabola.imgfmt.app.mdr.Mdr7;
3030 import uk.me.parabola.log.Logger;
110110
111111 if (tok.getType() == TokType.SYMBOL) {
112112
113 String punc = ts.nextValue();
114 String val;
115 if (punc.equals(":") || punc.equals("=")) {
116 val = ts.readLine();
117 } else {
113 switch (ts.nextValue()) {
114 case ":":
115 case "=":
116 processOption(key, ts.readLine());
117 break;
118 default:
118119 ts.skipLine();
119 continue;
120120 }
121 processOption(key, val);
121
122122 } else if (key != null){
123123 throw new IllegalArgumentException("don't understand line with " + key );
124124 } else {
125125 ts.skipLine();
126126 }
127127 }
128
128129 /**
129130 * process lines starting with prefix1 or prefix2.
130131 */
133134 if (prefix1 == null)
134135 continue;
135136 String prefix2 = options.getProperty("prefix2:" + lang, null);
136 List<String> p1 = prefix1 != null ? Arrays.asList(prefix1.split(",")) : Collections.emptyList();
137 List<String> p1 = Arrays.asList(prefix1.split(","));
137138 List<String> p2 = prefix2 != null ? Arrays.asList(prefix2.split(",")) : Collections.emptyList();
138139 langPrefixMap.put(lang, genPrefix(p1, p2));
139140 }
166167 }
167168 countryLanguageMap .put(iso, langs);
168169 languages.addAll(langs);
169 default:
170170 break;
171171 }
172172 }
177177 * @param prefix2 list of prepositions
178178 * @return all combinations
179179 */
180 private List<String> genPrefix (List<String> prefix1, List<String> prefix2) {
180 private static List<String> genPrefix (List<String> prefix1, List<String> prefix2) {
181181 List<String> prefixes = new ArrayList<>();
182182 for (String p1 : prefix1) {
183183 p1 = stripBlanksAndQuotes(p1);
187187 }
188188 prefixes.add(p1 + " ");
189189 }
190 sortByLength(prefixes);
190191 return prefixes;
191192 }
192193
195196 * @param s the string
196197 * @return the modified string
197198 */
198 private String stripBlanksAndQuotes(String s) {
199 private static String stripBlanksAndQuotes(String s) {
199200 s = s.trim();
200201 if (s.startsWith("'") && s.endsWith("'") || s.startsWith("\"") && s.endsWith("\"")) {
201202 return s.substring(1, s.length()-1);
220221 if (country == null)
221222 return;
222223
223 List<String> prefixesCountry = getSearchStrings(country, MODE_PREFIX);
224 List<String> suffixesCountry = getSearchStrings(country, MODE_SUFFIX);
224 final List<String> prefixesCountry = getSearchStrings(country, MODE_PREFIX);
225 final List<String> suffixesCountry = getSearchStrings(country, MODE_SUFFIX);
225226
226 // perform brute force search, seems to be fast enough
227227 String[] labels = road.getLabels();
228228 for (int i = 0; i < labels.length; i++) {
229229 String label = labels[i];
230 if (label == null || label.length() == 0)
230 if (label == null || label.isEmpty())
231231 continue;
232 boolean modified = false;
233 for (String prefix : prefixesCountry) {
234 if (label.charAt(0) < 7)
235 break; // label starts with shield code
236 if (label.length() < prefix.length())
237 continue;
238 if (prefix.equalsIgnoreCase(label.substring(0, prefix.length()))) {
239 if (prefix.endsWith(" ")) {
240 label = prefix.substring(0, prefix.length() - 1) + (char) 0x1e
241 + label.substring(prefix.length());
242 } else {
243 label = prefix + (char) 0x1b + label.substring(prefix.length());
244 }
245 modified = true;
246 break;
232 label = applyPrefixes(label, prefixesCountry);
233 label = applySuffixes(label, suffixesCountry);
234 if (!label.equals(labels[i])) {
235 labels[i] = label;
236 log.debug("modified", label, country, road.getRoadDef());
237 }
238 }
239 }
240
241 static String applyPrefixes(String label, List<String> prefixesCountry) {
242 if (label.charAt(0) < 7)
243 return label; // label starts with shield code
244 // perform brute force search, seems to be fast enough
245 for (String prefix : prefixesCountry) {
246 if (label.length() >= prefix.length() && prefix.equalsIgnoreCase(label.substring(0, prefix.length()))) {
247 if (prefix.endsWith(" ")) {
248 return prefix.substring(0, prefix.length() - 1) + (char) 0x1e + label.substring(prefix.length());
247249 }
248 }
249 for (String suffix : suffixesCountry) {
250 int len = label.length();
251 if (len < suffix.length())
252 continue;
253 int pos = len - suffix.length();
254 if (suffix.equalsIgnoreCase(label.substring(pos, len))) {
255 if (suffix.startsWith(" "))
256 label = label.substring(0, pos) + (char) 0x1f + suffix.substring(1);
257 else
258 label = label.substring(0, pos) + (char) 0x1c + suffix;
259 modified = true;
260 break;
250 return prefix + (char) 0x1b + label.substring(prefix.length());
251 }
252 }
253 return label;
254 }
255
256 private static String applySuffixes(String label, List<String> suffixesCountry) {
257 // perform brute force search, seems to be fast enough
258 for (String suffix : suffixesCountry) {
259 int len = label.length();
260 int pos = len - suffix.length();
261 if (pos >= 0 && suffix.equalsIgnoreCase(label.substring(pos, len))) {
262 if (suffix.startsWith(" ")) {
263 return label.substring(0, pos) + (char) 0x1f + suffix.substring(1);
261264 }
262 }
263 if (modified) {
264 labels[i] = label;
265 log.debug("modified",label,country,road.getRoadDef());
266 }
267 }
268 }
269
265 return label.substring(0, pos) + (char) 0x1c + suffix;
266 }
267 }
268 return label;
269 }
270
270271 /**
271272 * Build list of prefixes or suffixes for a given country.
272273 * @param country String with 3 letter ISO code
275276 */
276277 private List<String> getSearchStrings(String country, int mode) {
277278 Map<String, List<String>> cache = (mode == MODE_PREFIX) ? countryPrefixMap : countrySuffixMap;
278 List<String> res = cache.get(country);
279 if (res == null) {
279 return cache.computeIfAbsent(country, k-> {
280280 // compile the list
281 List<String> languages = countryLanguageMap.get(country);
282 if (languages == null)
283 res = Collections.emptyList();
284 else {
285 List<List<String>> all = new ArrayList<>();
286 for (String lang : languages) {
287 List<String> prefixes = mode == MODE_PREFIX ? langPrefixMap.get(lang) : langSuffixMap.get(lang);
288 if(prefixes != null)
289 all.add(prefixes);
290 }
291 if(all.isEmpty())
292 res = Collections.emptyList();
293 else if (all.size() == 1) {
294 res = all.get(0);
295 }
296 else {
297 Set<String> allPrefixesSet = new HashSet<>();
298 for (List<String> prefOneLang : all)
299 allPrefixesSet.addAll(prefOneLang);
300 res = new ArrayList<>(allPrefixesSet);
301 sortByLength(res);
302
303 }
304 }
305 // cache the result
306 cache.put(country, res);
307 }
308 return res;
281 List<String> languageList = countryLanguageMap.get(country);
282 if (languageList == null)
283 return Collections.emptyList();
284 final Map<String, List<String>> map = mode == MODE_PREFIX ? langPrefixMap : langSuffixMap;
285 List<String> res = languageList.stream()
286 .map(lang -> map.getOrDefault(lang, Collections.emptyList()))
287 .flatMap(List::stream)
288 .distinct()
289 .collect(Collectors.toList());
290 if (res.isEmpty())
291 return Collections.emptyList();
292 sortByLength(res);
293 return res;
294 });
309295 }
310296
311297 /**
312298 * Sort by string length so that longest string comes first.
313299 * @param strings
314300 */
315 private void sortByLength(List<String> strings) {
301 private static void sortByLength(List<String> strings) {
316302 strings.sort((o1, o2) -> Integer.compare(o2.length(), o1.length()));
317303 }
318304 }
315315 return false;
316316 for (String tagKey : evaluated) {
317317 for (String s : actionList.getChangeableTags()) {
318 int pos = s.indexOf("=");
318 int pos = s.indexOf('=');
319319 String key = pos > 0 ? s.substring(0, pos) : s;
320320 if (tagKey.equals(key)) {
321321 return true;
5252 int cacheId;
5353 boolean compiled = false;
5454
55 private static final short executeFinalizeRulesTagKey = TagDict.getInstance().xlate("mkgmap:execute_finalize_rules");
55 private static final short TKM_EXECUTE_FINALIZE_RULES = TagDict.getInstance().xlate("mkgmap:execute_finalize_rules");
5656
5757 private RuleIndex index = new RuleIndex();
5858 private final Set<String> usedTags = new HashSet<>();
9898 return cacheId;
9999 }
100100 if (lastRule != null && lastRule.getFinalizeRule() != null
101 && "true".equals(el.getTag(executeFinalizeRulesTagKey))) {
101 && "true".equals(el.getTag(TKM_EXECUTE_FINALIZE_RULES))) {
102102 cacheId = lastRule.getFinalizeRule().resolveType(cacheId, el, a);
103103 }
104104 return cacheId;
5757 File dir = file;
5858 if (name != null) {
5959 dir = new File(file, name);
60 if (dir.exists() == false)
60 if (!dir.exists())
6161 throw new FileNotFoundException("style " + name + " not found in " + dir);
6262 if (!dir.isDirectory())
6363 dir = file;
145145 }
146146
147147 String proto = url.getProtocol().toLowerCase();
148 if (proto.equals("jar")) {
148 if ("jar".equals(proto)) {
149149 log.debug("classpath loading from jar with url", url);
150150 return new JarFileLoader(url);
151 } else if (proto.equals("file")) {
151 } else if ("file".equals(proto)) {
152152 log.debug("classpath loading from directory", url.getPath());
153153 return new DirectoryFileLoader(new File(url.getPath()));
154154 }
3535 import java.util.Map;
3636 import java.util.Map.Entry;
3737 import java.util.Set;
38 import java.util.regex.Pattern;
3938
4039 import uk.me.parabola.imgfmt.ExitException;
4140 import uk.me.parabola.log.Logger;
41 import uk.me.parabola.mkgmap.CommandArgs;
4242 import uk.me.parabola.mkgmap.Options;
4343 import uk.me.parabola.mkgmap.general.LevelInfo;
4444 import uk.me.parabola.mkgmap.general.LineAdder;
7979 private static final String FILE_OPTIONS = "options";
8080 private static final String FILE_OVERLAYS = "overlays";
8181
82 // Patterns
83 private static final Pattern COMMA_OR_SPACE_PATTERN = Pattern.compile("[,\\s]+");
84
8582 // A handle on the style directory or file.
8683 private final StyleFileLoader fileLoader;
8784 private final String location;
162159 ListIterator<StyleImpl> listIterator = baseStyles.listIterator(baseStyles.size());
163160 while (listIterator.hasPrevious())
164161 mergeRules(listIterator.previous());
165
166 // OR: other way
167 //for (StyleImpl s : baseStyles)
168 // mergeRules(s);
169 }
170
162 }
163
164 @Override
171165 public String getOption(String name) {
172166 return generalOptions.get(name);
173167 }
174168
169 @Override
175170 public StyleInfo getInfo() {
176171 return info;
177172 }
178173
174 @Override
179175 public Rule getNodeRules() {
180176 nodes.prepare();
181177 return nodes;
182178 }
183179
180 @Override
184181 public Rule getWayRules() {
185182 RuleSet r = new RuleSet();
186183 r.addAll(lines);
189186 return r;
190187 }
191188
189 @Override
192190 public Rule getLineRules() {
193191 lines.prepare();
194192 return lines;
195193 }
196194
195 @Override
197196 public Rule getPolygonRules() {
198197 polygons.prepare();
199198 return polygons;
200199 }
201200
201 @Override
202202 public Rule getRelationRules() {
203203 relations.prepare();
204204 return relations;
205205 }
206206
207 @Override
207208 public LineAdder getOverlays(final LineAdder lineAdder) {
208209 LineAdder adder = null;
209210
212213 }
213214 return adder;
214215 }
215
216
217 @Override
218 public Set<String> getUsedTagsPOI() {
219 return new HashSet<>(nodes.getUsedTags());
220 }
221
222 @Override
216223 public Set<String> getUsedTags() {
217224 Set<String> set = new HashSet<>();
218225 set.addAll(relations.getUsedTags());
225232 // if they are not found in the style file. This is mostly to work
226233 // around situations that we haven't thought of - the style is expected
227234 // to get it right for itself.
228 String s = getOption("extra-used-tags");
229 if (s != null && !s.trim().isEmpty())
230 set.addAll(Arrays.asList(COMMA_OR_SPACE_PATTERN.split(s)));
235 set.addAll(CommandArgs.stringToList(getOption("extra-used-tags"), "extra-used-tags"));
231236
232237 // There are a lot of tags that are used within mkgmap that
233238 try (InputStream is = this.getClass().getResourceAsStream("/styles/builtin-tag-list");) {
328333 Options opts = new Options(opt -> {
329334 String key = opt.getOption();
330335 String val = opt.getValue();
331 if (key.equals("name-tag-list")) {
336 if ("name-tag-list".equals(key)) {
332337 if (!"name".equals(val)) {
333338 System.err.println("Warning: option name-tag-list used in the style options is ignored. "
334339 + "Please use only the command line option to specify this value.");
358363 Options opts = new Options(opt -> {
359364 String word = opt.getOption();
360365 String value = opt.getValue();
361 if (word.equals("summary"))
366 if ("summary".equals(word))
362367 info.setSummary(value);
363 else if (word.equals("version")) {
368 else if ("version".equals(word)) {
364369 info.setVersion(value);
365 } else if (word.equals("base-style")) {
370 } else if ("base-style".equals(word)) {
366371 info.addBaseStyleName(value);
367 } else if (word.equals("description")) {
372 } else if ("description".equals(word)) {
368373 info.setLongDescription(value);
369374 }
370375 });
295295 }
296296
297297 boolean wasReversed = false;
298 String oneWay = way.getTag(onewayTagKey);
298 String oneWay = way.getTag(TK_ONEWAY);
299299 if (oneWay != null){
300300 if("-1".equals(oneWay) || "reverse".equals(oneWay)) {
301301 // it's a oneway street in the reverse direction
303303 // the oneway tag to "yes"
304304 way.reverse();
305305 wasReversed = true;
306 way.addTag(onewayTagKey, "yes");
307 }
308
309 if (way.tagIsLikeYes(onewayTagKey)) {
310 way.addTag(onewayTagKey, "yes");
306 way.addTag(TK_ONEWAY, "yes");
307 }
308
309 if (way.tagIsLikeYes(TK_ONEWAY)) {
310 way.addTag(TK_ONEWAY, "yes");
311311 if (foundType.isRoad() && hasSkipDeadEndCheckNode(way))
312312 way.addTag("mkgmap:dead-end-check", "false");
313313 } else {
314 way.deleteTag(onewayTagKey);
314 way.deleteTag(TK_ONEWAY);
315315 }
316316 }
317317 ConvertedWay cw = new ConvertedWay(lineIndex++, way, foundType);
320320 roads.add(cw);
321321 numRoads++;
322322 if (!cw.isFerry()) {
323 String countryIso = LocatorConfig.get().getCountryISOCode(way.getTag(countryTagKey));
323 String countryIso = LocatorConfig.get().getCountryISOCode(way.getTag(TKM_COUNTRY));
324324 if (countryIso != null) {
325325 boolean drivingSideIsLeft = LocatorConfig.get().getDriveOnLeftFlag(countryIso);
326326 if (drivingSideIsLeft)
361361 shape.setPoints(way.getPoints());
362362
363363 long areaVal = 0;
364 String tagStringVal = way.getTag(drawLevelTagKey);
364 String tagStringVal = way.getTag(TKM_DRAW_LEVEL);
365365 if (tagStringVal != null) {
366366 try {
367367 areaVal = Integer.parseInt(tagStringVal);
413413 *
414414 * @param way The OSM way.
415415 */
416 private static final short styleFilterTagKey = TagDict.getInstance().xlate("mkgmap:stylefilter");
417 private static final short makeCycleWayTagKey = TagDict.getInstance().xlate("mkgmap:make-cycle-way");
416 private static final short TKM_STYLEFILTER = TagDict.getInstance().xlate("mkgmap:stylefilter");
417 private static final short TKM_MAKE_CYCLE_WAY = TagDict.getInstance().xlate("mkgmap:make-cycle-way");
418418 private long lastRoadId = 0;
419419 private int lineCacheId = 0;
420420 private BitSet routingWarningWasPrinted = new BitSet();
433433
434434 preConvertRules(way);
435435
436 String styleFilterTag = way.getTag(styleFilterTagKey);
436 String styleFilterTag = way.getTag(TKM_STYLEFILTER);
437437 Rule rules;
438438 if ("polyline".equals(styleFilterTag))
439439 rules = lineRules;
449449 rules = wayRules;
450450 }
451451 Way cycleWay = null;
452 String cycleWayTag = way.getTag(makeCycleWayTagKey);
452 String cycleWayTag = way.getTag(TKM_MAKE_CYCLE_WAY);
453453 if ("yes".equals(cycleWayTag)){
454 way.deleteTag("mkgmap:make-cycle-way");
454 way.deleteTag(TKM_MAKE_CYCLE_WAY);
455455 cycleWay = makeCycleWay(way);
456456 way.addTag("bicycle", "no"); // make sure that bicycles are using the added bicycle way
457457 }
510510 }
511511
512512 private int lineIndex = 0;
513 private static final short onewayTagKey = TagDict.getInstance().xlate("oneway");
513 private static final short TK_ONEWAY = TagDict.getInstance().xlate("oneway");
514514
515515 /** One type result for nodes to avoid recreating one for each node. */
516516 private NodeTypeResult nodeTypeResult = new NodeTypeResult();
634634 cycleWay.addTag("access", "no");
635635 cycleWay.addTag("bicycle", "yes");
636636 cycleWay.addTag("mkgmap:synthesised", "yes");
637 cycleWay.addTag(onewayTagKey, "no");
637 cycleWay.addTag(TK_ONEWAY, "no");
638638 // remove explicit access tags
639639 cycleWay.deleteTag("foot");
640640 cycleWay.deleteTag("motorcar");
660660 @Override
661661 public void augmentWith(uk.me.parabola.mkgmap.reader.osm.ElementSaver elementSaver) {
662662 // wayRules doesn't need to be done (or must be done first) because is concat. of line & polygon rules
663 //wayRules.augmentWith(elementSaver);
664663 nodeRules.augmentWith(elementSaver);
665664 lineRules.augmentWith(elementSaver);
666665 polygonRules.augmentWith(elementSaver);
825824 while (pos < points.size()) {
826825 int right = Math.min(points.size(), pos + max);
827826 Way w = new Way(orig.getId(), points.subList(pos, right));
828 w.setFakeId();
827 w.markAsGeneratedFrom(orig);
829828 clippedBorders.add(w);
830829 pos += max - 1;
831830 if (pos + 1 == points.size())
10591058 + points.get(0).toOSMURL() + ")");
10601059 else {
10611060 boolean clockwise = dir > 0;
1061 boolean dirIsWrong = (Boolean.TRUE.equals(driveOnLeft) && !clockwise || Boolean.FALSE.equals(driveOnLeft) && clockwise);
10621062 if (points.get(0) == points.get(points.size() - 1)) {
10631063 // roundabout is a loop
1064 if (driveOnLeft && !clockwise || !driveOnLeft && clockwise) {
1064 if (dirIsWrong) {
10651065 log.warn("Roundabout " + way.getId() + " direction is wrong - reversing it (see "
10661066 + centre.toOSMURL() + ")");
10671067 way.reverse();
10681068 }
1069 } else if (driveOnLeft && !clockwise || !driveOnLeft && clockwise) {
1069 } else if (dirIsWrong) {
10701070 // roundabout is a line
10711071 log.warn("Roundabout segment " + way.getId() + " direction looks wrong (see "
10721072 + points.get(0).toOSMURL() + ")");
12201220 line.setPoints(points);
12211221
12221222
1223 if (way.tagIsLikeYes(onewayTagKey))
1223 if (way.tagIsLikeYes(TK_ONEWAY))
12241224 line.setDirection(true);
12251225
12261226 clipper.clipLine(line, lineAdder);
12321232 TagDict.getInstance().xlate("mkgmap:label:3"),
12331233 TagDict.getInstance().xlate("mkgmap:label:4"),
12341234 };
1235 private static final short highResOnlyTagKey = TagDict.getInstance().xlate("mkgmap:highest-resolution-only");
1236 private static final short skipSizeFilterTagKey = TagDict.getInstance().xlate("mkgmap:skipSizeFilter");
1237 private static final short drawLevelTagKey = TagDict.getInstance().xlate("mkgmap:drawLevel");
1238
1239 private static final short countryTagKey = TagDict.getInstance().xlate("mkgmap:country");
1240 private static final short regionTagKey = TagDict.getInstance().xlate("mkgmap:region");
1241 private static final short cityTagKey = TagDict.getInstance().xlate("mkgmap:city");
1242 private static final short postal_codeTagKey = TagDict.getInstance().xlate("mkgmap:postal_code");
1243 private static final short streetTagKey = TagDict.getInstance().xlate("mkgmap:street");
1244 private static final short housenumberTagKey = TagDict.getInstance().xlate("mkgmap:housenumber");
1245 private static final short phoneTagKey = TagDict.getInstance().xlate("mkgmap:phone");
1246 private static final short is_inTagKey = TagDict.getInstance().xlate("mkgmap:is_in");
1235 private static final short TKM_HIGHEST_RES_ONLY = TagDict.getInstance().xlate("mkgmap:highest-resolution-only");
1236 private static final short TKM_SKIP_SIZE_FILTER = TagDict.getInstance().xlate("mkgmap:skipSizeFilter");
1237 private static final short TKM_DRAW_LEVEL = TagDict.getInstance().xlate("mkgmap:drawLevel");
1238
1239 private static final short TKM_COUNTRY = TagDict.getInstance().xlate("mkgmap:country");
1240 private static final short TKM_REGION = TagDict.getInstance().xlate("mkgmap:region");
1241 private static final short TKM_CITY = TagDict.getInstance().xlate("mkgmap:city");
1242 private static final short TKM_POSTAL_CODE = TagDict.getInstance().xlate("mkgmap:postal_code");
1243 private static final short TKM_STREET = TagDict.getInstance().xlate("mkgmap:street");
1244 private static final short TKM_HOUSENUMBER = TagDict.getInstance().xlate("mkgmap:housenumber");
1245 private static final short TKM_PHONE = TagDict.getInstance().xlate("mkgmap:phone");
1246 private static final short TKM_IS_IN = TagDict.getInstance().xlate("mkgmap:is_in");
12471247
12481248 private void elementSetup(MapElement ms, GType gt, Element element) {
12491249 String[] labels = new String[4];
12641264 ms.setMinResolution(gt.getMinResolution());
12651265 ms.setMaxResolution(gt.getMaxResolution());
12661266
1267 if (element.tagIsLikeYes(highResOnlyTagKey)){
1267 if (element.tagIsLikeYes(TKM_HIGHEST_RES_ONLY)){
12681268 ms.setMinResolution(ms.getMaxResolution());
12691269 }
12701270
1271 if (ms instanceof MapLine && element.tagIsLikeYes(skipSizeFilterTagKey)){
1271 if (ms instanceof MapLine && element.tagIsLikeYes(TKM_SKIP_SIZE_FILTER)){
12721272 ((MapLine)ms).setSkipSizeFilter(true);
12731273 }
12741274
12751275 // Now try to get some address info for POIs
12761276
1277 String country = element.getTag(countryTagKey);
1278 String region = element.getTag(regionTagKey);
1279 String city = element.getTag(cityTagKey);
1280 String zip = element.getTag(postal_codeTagKey);
1281 String street = element.getTag(streetTagKey);
1282 String houseNumber = element.getTag(housenumberTagKey);
1283 String phone = element.getTag(phoneTagKey);
1284 String isIn = element.getTag(is_inTagKey);
1277 String country = element.getTag(TKM_COUNTRY);
1278 String region = element.getTag(TKM_REGION);
1279 String city = element.getTag(TKM_CITY);
1280 String zip = element.getTag(TKM_POSTAL_CODE);
1281 String street = element.getTag(TKM_STREET);
1282 String houseNumber = element.getTag(TKM_HOUSENUMBER);
1283 String phone = element.getTag(TKM_PHONE);
1284 String isIn = element.getTag(TKM_IS_IN);
12851285
12861286 if(country != null)
12871287 ms.setCountry(country);
17531753 if (wayPOI != null && wayPOI.contains("[" + cp.getNode().getId() + "]")){
17541754 byte nodeAccess = AccessTagsAndBits.evalAccessTags(cp.getNode());
17551755 if (nodeAccess != cw.getAccess()){
1756 List<Way> wayList = poiRestrictions.get(cp.getNode());
1757 if (wayList == null){
1758 wayList = new ArrayList<>();
1759 poiRestrictions.put(cp.getNode(), wayList);
1760 }
1761 wayList.add(way);
1756 poiRestrictions.computeIfAbsent(cp.getNode(), k -> new ArrayList<>()).add(way);
17621757 }
17631758 }
17641759 }
21932188 List<Coord> otherPoints = connectedWay.getPoints();
21942189 Coord otherFirst = otherPoints.get(0);
21952190 Coord otherLast = otherPoints.get(otherPoints.size() - 1);
2196 if (otherFirst == otherLast || !connectedWay.tagIsLikeYes(onewayTagKey))
2191 if (otherFirst == otherLast || !connectedWay.tagIsLikeYes(TK_ONEWAY))
21972192 isDeadEnd = false;
21982193 else {
21992194 Coord pOther;
22302225 continue;
22312226 Way way = cw.getWay();
22322227 if ("true".equals(way.getTag("mkgmap:way-has-pois"))) {
2233 String wayPOI = "";
2228 StringBuilder wayPOI = new StringBuilder();
22342229
22352230 List<Coord> points = way.getPoints();
22362231 int numPoints = points.size();
22642259 }
22652260 if (usedInThisWay) {
22662261 cp.setUsed(true);
2267 wayPOI += "[" + node.getId() + "]";
2262 wayPOI.append('[').append(node.getId()).append(']');
22682263 }
22692264 }
22702265 }
2271 if (wayPOI.isEmpty()) {
2266 if (wayPOI.length() == 0) {
22722267 way.deleteTag("mkgmap:way-has-pois");
22732268 log.info("ignoring CoordPOI(s) for way", way.toBrowseURL(), "because routing is not affected.");
22742269 } else {
2275 way.addTag(WAY_POI_NODE_IDS, wayPOI);
2270 way.addTag(WAY_POI_NODE_IDS, wayPOI.toString());
22762271 }
22772272 }
22782273 }
3838 if (t == null || t.getType() == TokType.EOF)
3939 throw new SyntaxException(ts, "No garmin type information given");
4040
41 if (!t.getValue().equals("[")) {
41 if (!"[".equals(t.getValue())) {
4242 throw new SyntaxException(ts, "No type definition");
4343 }
4444
5959 while (!ts.isEndOfFile()) {
6060 ts.skipSpace();
6161 String w = ts.nextValue();
62 if (w.equals("]"))
62 if ("]".equals(w))
6363 break;
6464
65 if (w.equals("level")) {
65 if ("level".equals(w)) {
6666 setLevel(ts, gt);
67 } else if (w.equals("resolution")) {
67 } else if ("resolution".equals(w)) {
6868 setResolution(ts, gt);
69 } else if (w.equals("default_name")) {
69 } else if ("default_name".equals(w)) {
7070 gt.setDefaultName(nextValue(ts));
71 } else if (w.equals("road_class")) {
71 } else if ("road_class".equals(w)) {
7272 gt.setRoadClass(nextIntValue(ts));
73 } else if (w.equals("road_speed")) {
73 } else if ("road_speed".equals(w)) {
7474 gt.setRoadSpeed(nextIntValue(ts));
75 } else if (w.equals("copy")) {
75 } else if ("copy".equals(w)) {
7676 // Reserved
77 } else if (w.equals("continue")) {
77 } else if ("continue".equals(w)) {
7878 gt.setContinueSearch(true);
7979 // By default no propagate of actions on continue
8080 gt.propagateActions(false);
81 } else if (w.equals("propagate") || w.equals("with_actions") || w.equals("withactions")) {
81 } else if ("propagate".equals(w) || "with_actions".equals(w) || "withactions".equals(w)) {
8282 gt.propagateActions(true);
83 } else if (w.equals("no_propagate")) {
83 } else if ("no_propagate".equals(w)) {
8484 gt.propagateActions(false);
85 } else if (w.equals("oneway")) {
85 } else if ("oneway".equals(w)) {
8686 // reserved
87 } else if (w.equals("access")) {
87 } else if ("access".equals(w)) {
8888 // reserved
8989 } else {
9090 throw new SyntaxException(ts, "Unrecognised type command '" + w + '\'');
127127 for (int i = 0; i < usedTypes.size(); i++){
128128 int usedType = usedTypes.get(i);
129129 String typeOverlaidMsg = ". Type is overlaid with " + GType.formatType(usedType);
130 if (GType.checkType(kind, usedType) == false){
130 if (!GType.checkType(kind, usedType)) {
131131 String msg = "Warning: invalid type " + type + " for " + kind + " in style file " + ts.getFileName() + ", line " + ts.getLinenumber();
132132 if (fromOverlays)
133133 msg += typeOverlaidMsg;
135135 }
136136 if (kind == FeatureKind.POLYLINE && gt.getMinLevel() == 0 && gt.getMaxLevel() >= 0){
137137 if (GType.isSpecialRoutableLineType(usedType)){
138 if (gt.hasRoadAttribute() == false){
138 if (!gt.hasRoadAttribute()) {
139139 String msg = "Warning: routable type " + type + " is used for non-routable line with level 0. This may break routing. Style file "+ ts.getFileName() + ", line " + ts.getLinenumber();
140140 if (fromOverlays)
141141 msg += typeOverlaidMsg;
153153 foundRoutableType = true;
154154 }
155155 }
156 if (gt.hasRoadAttribute() && foundRoutableType == false && gt.getMinLevel() == 0 && gt.getMaxLevel() >= 0){
156 if (gt.hasRoadAttribute() && !foundRoutableType && gt.getMinLevel() == 0 && gt.getMaxLevel() >= 0){
157157 String msg = "Warning: non-routable type " + type + " is used in combination with road_class/road_speed. Line will not be routable. Style file "+ ts.getFileName() + ", line " + ts.getLinenumber();
158158 if (fromOverlays)
159159 msg += ". Type is overlaid, but not with a routable type";
3030 import uk.me.parabola.mkgmap.filters.DouglasPeuckerFilter;
3131 import uk.me.parabola.mkgmap.general.MapPoint;
3232 import uk.me.parabola.mkgmap.reader.osm.CoordPOI;
33 import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
3433 import uk.me.parabola.mkgmap.reader.osm.Node;
3534 import uk.me.parabola.mkgmap.reader.osm.RestrictionRelation;
3635 import uk.me.parabola.mkgmap.reader.osm.Way;
422421 Coord p = points.get(i);
423422 if (p.isToRemove()) {
424423 if (pass >= maxPass - 1) {
425 log.warn("removed point in last pass. Way", getUsableId(way), p.toDegreeString());
424 log.warn("removed point in last pass. Way", way.getBasicLogInformation(), p.toDegreeString());
426425 }
427426 points.remove(i);
428427 anotherPassRequired = true;
446445 }
447446 p = replacement;
448447 if (pass >= maxPass - 1) {
449 log.warn("changed point in last pass. Way", getUsableId(way), p.toDegreeString());
448 log.warn("changed point in last pass. Way", way.getBasicLogInformation(), p.toDegreeString());
450449 }
451450 // replace point in way
452451 points.set(i, p);
14591458 way.getLastPoint().setEndOfWay(b);
14601459 }
14611460 }
1462
1463
1464 private static String getUsableId(Way w) {
1465 return "Way " + (FakeIdGenerator.isFakeId(w.getId()) ? " generated from " : " ") + w.getOriginalId();
1466 }
14671461 }
3333 * @param el
3434 * @return true if one or more tags of the element were changed.
3535 */
36 public boolean perform(Element el);
36 boolean perform(Element el);
3737 }
3434 * @author Steve Ratcliffe
3535 */
3636 public class ActionReader {
37 Set<String> VALID_ACCESS = new HashSet<>(Arrays.asList("yes", "no", "true", "false", "1", "0"));
37 private static final Set<String> VALID_ACCESS = new HashSet<>(Arrays.asList("yes", "no", "true", "false", "1", "0"));
3838
3939 private final TokenScanner scanner;
4040
1111 */
1212 package uk.me.parabola.mkgmap.osmstyle.actions;
1313
14 import java.util.List;
14 import static uk.me.parabola.imgfmt.app.net.AccessTagsAndBits.ACCESS_TAGS_COMPILED;
1515
1616 import uk.me.parabola.mkgmap.reader.osm.Element;
17 import static uk.me.parabola.imgfmt.app.net.AccessTagsAndBits.*;
1817
1918 /**
2019 * Add one value to all mkgmap access tags, optionally changing them if they already exist.
4039
4140 public boolean perform(Element el) {
4241 // 1st build the value
43 Element tags = valueTags!=null? valueTags: el;
42 Element tags = valueTags != null ? valueTags : el;
4443 String accessValue = null;
4544 for (ValueBuilder value : getValueBuilder()) {
4645 accessValue = value.build(tags, el);
6665 * @param value the value to be set
6766 */
6867 private void setTag(Element el, Short tagKey, String value) {
69 String tv = el.getTag(tagKey);
70 if (tv != null && !modify)
71 return;
72
73 el.addTag(tagKey, value);
68 if (modify) {
69 el.addTag(tagKey, value);
70 } else {
71 String tv = el.getTag(tagKey);
72 if (tv == null) {
73 el.addTag(tagKey, value);
74 }
75 }
7476 }
7577
7678 public void setValueTags(Element valueTags) {
7880 }
7981
8082 public String toString() {
81 StringBuilder sb = new StringBuilder();
82 sb.append(modify ? "setaccess " : "addaccess ");
83 List<ValueBuilder> values = getValueBuilder();
84 for (int i = 0; i < values.size(); i++) {
85 sb.append(values.get(i));
86 if (i < values.size() - 1)
87 sb.append(" | ");
88 }
89 sb.append(';');
90 return sb.toString();
83 return modify ? "setaccess " : "addaccess " + calcValueBuildersString() + ";";
9184 }
9285 }
1111 */
1212 package uk.me.parabola.mkgmap.osmstyle.actions;
1313
14 import java.util.ArrayList;
15 import java.util.List;
16
1417 import uk.me.parabola.mkgmap.reader.osm.Element;
1518
1619 /**
2023 * We have a list of possible substitutions.
2124 */
2225 public class AddLabelAction extends ValueBuildedAction {
26 private final List<String> labels = new ArrayList<>(4);
2327
2428 /**
2529 * Search for the first matching pattern and set the first unset element label
3135 * @return
3236 */
3337 public boolean perform(Element el) {
34 for (int index = 1; index <=4; index++) {
38 labels.clear();
39 for (int index = 1; index <= 4; index++) {
40 String tag = "mkgmap:label:" + index;
41 String label = el.getTag(tag);
3542 // find the first unset label and set it
36 if (el.getTag("mkgmap:label:"+index) == null) {
37 for (ValueBuilder vb : getValueBuilder()) {
38 String s = vb.build(el, el);
39 if (s != null) {
40 // now check if the new label is different to all other labels
41 for (int n = index-1; n>= 1; n--) {
42 if (s.equals(el.getTag("mkgmap:label:"+n))) {
43 // value is equal to a previous label
44 // do not use it
45 return false;
46 }
47 }
48
49 // set the label
50 el.addTag("mkgmap:label:"+index, s);
51 return true;
52 }
43 if (label != null) {
44 labels.add(label);
45 continue;
46 }
47 for (ValueBuilder vb : getValueBuilder()) {
48 String s = vb.build(el, el);
49 if (s != null) {
50 // now check if the new label is different to all other labels
51 if (labels.contains(s))
52 return false;
53 // set the label
54 el.addTag(tag, s);
55 return true;
5356 }
54 return false;
5557 }
58 return false;
5659 }
5760 return false;
5861 }
5962
6063 public String toString() {
61 StringBuilder sb = new StringBuilder();
62 sb.append("addlabel ");
63 for (ValueBuilder vb : getValueBuilder()) {
64 sb.append(vb);
65 sb.append(" | ");
66 }
67 sb.setLength(sb.length() - 3);
68 return sb.toString();
64 return "addlabel " + calcValueBuildersString();
6965 }
7066 }
1414 * Create date: 15-Nov-2008
1515 */
1616 package uk.me.parabola.mkgmap.osmstyle.actions;
17
18 import java.util.List;
1917
2018 import uk.me.parabola.mkgmap.reader.osm.Element;
2119 import uk.me.parabola.mkgmap.reader.osm.TagDict;
4745 }
4846
4947 public boolean perform(Element el) {
50 if (!modify){
51 String tv = el.getTag(tagKey);
52 if (tv != null)
53 return false;
54 }
55 Element tags = valueTags!=null? valueTags: el;
48 if (!modify && el.getTag(tagKey) != null)
49 return false;
50
51 Element tags = valueTags != null ? valueTags : el;
5652
5753 for (ValueBuilder value : getValueBuilder()) {
5854 String newval = value.build(tags, el);
7066 }
7167
7268 public String toString() {
73 StringBuilder sb = new StringBuilder();
74 sb.append(modify ? "set " : "add ");
75 sb.append(tag);
76 sb.append("=");
77 List<ValueBuilder> values = getValueBuilder();
78 for (int i = 0; i < values.size(); i++) {
79 sb.append(values.get(i));
80 if (i < values.size() - 1)
81 sb.append(" | ");
82 }
83 sb.append(';');
84 return sb.toString();
69 return modify ? "set " : "add " + tag + "=" + calcValueBuildersString() + ";";
8570 }
8671 }
2222 */
2323 public class CountryISOFilter extends ValueFilter {
2424
25 public CountryISOFilter() {
26 }
27
2825 protected String doFilter(String value, Element el) {
2926 if (value == null)
3027 return value;
1111 */
1212 package uk.me.parabola.mkgmap.osmstyle.actions;
1313
14 import uk.me.parabola.imgfmt.app.Coord;
1514 import uk.me.parabola.mkgmap.reader.osm.Element;
16 import uk.me.parabola.mkgmap.reader.osm.Node;
1715
1816 /**
1917 * Delete all tags from an element. This is useful to stop its processing.
2220 */
2321 public class DeleteAllTagsAction implements Action {
2422
25 // as long as there is not deleteAll method copy the tags of element without tags
26 private final Element noTagElement;
2723
28 public DeleteAllTagsAction() {
29 this.noTagElement = new Node(0, new Coord(0,0));
30 }
31
3224 public boolean perform(Element el) {
33 // remove all tags by copying the tags from a no tag element
34 el.copyTags(noTagElement);
25 el.removeAllTags();
3526 return true;
3627 }
3728
1515 package uk.me.parabola.mkgmap.osmstyle.actions;
1616
1717 import uk.me.parabola.mkgmap.reader.osm.Element;
18 import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
1918
2019 /**
2120 * Sends a message to the console.
3029 }
3130
3231 public boolean perform(Element el) {
33 String e = value.build(el, el);
34 String className = el.getClass().getSimpleName();
35 if (className.equals("GeneralRelation"))
36 className = "Relation";
37 System.err.println(className + (FakeIdGenerator.isFakeId(el.getId()) ? " generated from " : " ") + el.getOriginalId() + " " + e);
32 System.err.println(el.getBasicLogInformation() + " " + value.build(el, el));
3833 return false;
3934 }
4035 }
1313 package uk.me.parabola.mkgmap.osmstyle.actions;
1414
1515 import uk.me.parabola.mkgmap.reader.osm.Element;
16 import uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator;
1716
1817 /**
1918 * Sends a message including the tags of an element to System.err.
2827 }
2928
3029 public boolean perform(Element el) {
31 String e = value.build(el, el);
32 String className = el.getClass().getSimpleName();
33 if (className.equals("GeneralRelation"))
34 className = "Relation";
35 System.err.println(className + (FakeIdGenerator.isFakeId(el.getId()) ? " generated from " : " ") + el.getOriginalId() + " " + el.toTagString() + " " + e);
30 System.err.println(el.getBasicLogInformation() + " " + el.toTagString() + " " + value.build(el, el));
3631 return false;
3732 }
3833
3030 throw new SyntaxException(String.format("height filter reqires ft (feet) as target unit: '%s'", s));
3131 }
3232
33 @Override
3334 public String doFilter(String value, Element el) {
3435 String s = super.doFilter(value, el);
35 if (s != null)
36 s = "\u001f" + s;
37 return s;
36 return s != null ? "\u001f" + s : null;
3837 }
3938 }
2222
2323 /**
2424 * Prepend a Garmin magic-character to the value.
25 * TODO: symbolic names?
2625 *
2726 * @author Toby Speight
2827 */
2928 public class HighwaySymbolFilter extends ValueFilter {
3029 private final String prefix;
3130
32 private static final Map<String, String>symbols = new HashMap<String, String>();
31 private static final Map<String, String> symbols = new HashMap<>();
3332 private static final int MAX_REF_LENGTH = 8; // enough for "A6144(M)" (RIP)
3433
3534 private int maxAlphaNum = MAX_REF_LENGTH; // Max. length for alphanumeric (e.g., 'A67')
3837 private static final Pattern spacePattern = Pattern.compile(" ", Pattern.LITERAL);
3938 private static final Pattern semicolonPattern = Pattern.compile(";", Pattern.LITERAL);
4039 static {
41 //symbols.put("ele", "\u001f"); // name.height separator
42
43 // Now add other symbols
4440 symbols.put("interstate", "\u0001"); // US Interstate
4541 symbols.put("shield", "\u0002"); // US Highway shield
4642 symbols.put("round", "\u0003"); // US Highway round
7773 public String doFilter(String value, Element el) {
7874 if (value == null) return value;
7975
80 // is it mostly alphabetic?
81 /* int alpha_balance = 0;
82 for (char c : value.toCharArray()) {
83 alpha_balance += (Character.isLetter(c)) ? 1 : -1;
84 }
85 if (alpha_balance > 0) return value;
86
87 // remove space if there is exactly one
88 int first_space = value.indexOf(" ");
89 if (first_space >= 0 && value.indexOf(" ", first_space + 1) < 0) {
90 value = value.replace(" ", "");
91 } */
92
93
9476 // Nuke all spaces
95 // String shieldText = value.replace(" ", "");
9677 String shieldText = spacePattern.matcher(value).replaceAll(Matcher.quoteReplacement(""));
9778 // Also replace ";" with "/", to change B3;B4 to B3/B4
98 //shieldText = shieldText.replace(";", "/");
9979 shieldText = semicolonPattern.matcher(shieldText).replaceAll(Matcher.quoteReplacement("/"));
10080
10181 // Check if value is alphanumeric
2727 * @author Steve Ratcliffe
2828 */
2929 public class NameAction extends ValueBuildedAction {
30 private final short label1TagKey = TagDict.getInstance().xlate("mkgmap:label:1");
30 private static final short TKM_LABEL_1 = TagDict.getInstance().xlate("mkgmap:label:1");
3131 /**
3232 * search for the first matching name pattern and set the element name
3333 * to it.
3838 * @return
3939 */
4040 public boolean perform(Element el) {
41 if (el.getTag(label1TagKey) != null)
41 if (el.getTag(TKM_LABEL_1) != null)
4242 return false;
4343
4444 for (ValueBuilder vb : getValueBuilder()) {
4545 String s = vb.build(el, el);
4646 if (s != null) {
47 el.addTag(label1TagKey, s);
47 el.addTag(TKM_LABEL_1, s);
4848 return true;
4949 }
5050 }
5252 }
5353
5454 public String toString() {
55 StringBuilder sb = new StringBuilder();
56 sb.append("name ");
57 for (ValueBuilder vb : getValueBuilder()) {
58 sb.append(vb);
59 sb.append(" | ");
60 }
61 sb.setLength(sb.length() - 3);
62 return sb.toString();
55 return "name " + calcValueBuildersString();
6356 }
6457 }
3939 * @author Maxim Duester
4040 */
4141 public class NotContainedFilter extends ValueFilter {
42 private String separator;
43 private short tagKey;
42 private final String quotedSeparator;
43 private final short tagKey;
4444
4545 public NotContainedFilter(String arg) {
4646 String[] temp = arg.split(":");
5252
5353 // set the separator (default to ;)
5454 if (temp[0].length() > 0)
55 separator = temp[0];
55 quotedSeparator = Pattern.quote(temp[0]);
5656 else
57 separator = ";";
57 quotedSeparator = Pattern.quote(";");
5858 // set the tag short value
5959 tagKey = TagDict.getInstance().xlate(temp[1]);
6060 }
6969 return value;
7070
7171 // split uses a regex we need to replace special characters
72 String[] temp = tagValue.split(Pattern.quote(separator));
73
72 String[] temp = tagValue.split(quotedSeparator);
7473 for (String s : temp)
7574 if (s.equals(value))
7675 return null;
2828 private final short tagKey;
2929
3030 public NotEqualFilter(String s) {
31
3231 tagKey = TagDict.getInstance().xlate(s);
33
3432 }
3533
3634 public String doFilter(String value, Element el) {
3836
3937 String tagValue = el.getTag(tagKey);
4038
41 if (tagValue == null)
42 return value;
43
44 if (value.equals(tagValue))
39 if (tagValue != null && value.equals(tagValue))
4540 return null; // Return nothing if value is identical to the tag's value
46 else
47 return value;
41 return value;
4842
4943 }
5044 }
9999 if ( !isLt && !isGt ) {
100100 return temp[idx].trim();
101101 } else {
102 StringBuffer returnValue= new StringBuffer();
102 StringBuilder returnValue= new StringBuilder();
103103
104104 // operator "<": collate all the parts before the partnumber
105 if ( isLt ) {
106 for (int i=0;i<idx;i++) {
105 if (isLt) {
106 for (int i = 0; i < idx; i++) {
107107 returnValue.append(temp[i]).append(separator);
108108 }
109109 }
110110
111111 // operator ">": collate all the parts after the partnumber
112 if ( isGt ) {
113 for (int i=idx+1;i<temp.length;i++) {
112 if (isGt) {
113 for (int i = idx + 1; i < temp.length; i++) {
114114 returnValue.append(temp[i]).append(separator);
115115 }
116116 }
3232
3333 static {
3434 // Firstly, the symbols common to both encodings.
35 symbols_6bit = new HashMap<String, String>();
35 symbols_6bit = new HashMap<>();
3636 symbols_6bit.put("ele", "\u001f"); // name.height separator
3737
3838 // Copy to other encoding
39 symbols_8bit = new HashMap<String, String>(symbols_6bit);
39 symbols_8bit = new HashMap<>(symbols_6bit);
4040
4141 // Now add other symbols
4242 symbols_6bit.put("interstate", "\u002a"); // US Interstate
1616 package uk.me.parabola.mkgmap.osmstyle.actions;
1717
1818 import java.util.ArrayList;
19 import java.util.Formatter;
2019 import java.util.HashSet;
21 import java.util.Iterator;
2220 import java.util.List;
2321 import java.util.Map;
22 import java.util.stream.Collectors;
2423
2524 import uk.me.parabola.mkgmap.reader.osm.Element;
2625 import uk.me.parabola.mkgmap.reader.osm.Relation;
2726
2827 /**
29 * This is an action that contains sub-actions. It is used for Relations
30 * where you want to apply the commands to the elements that are contained
31 * in the relation and not on the relation itself.
28 * This is an action that contains sub-actions. It is used for Relations where
29 * you want to apply the commands to the elements that are contained in the
30 * relation and not on the relation itself.
3231 *
3332 * @author Steve Ratcliffe
3433 */
3534 public class SubAction implements Action {
36 private final List<Action> actionList = new ArrayList<Action>();
35 private final List<Action> actionList = new ArrayList<>();
3736 private final String role;
3837 private final String selector;
3938
4544 public boolean perform(Element el) {
4645 if (el instanceof Relation)
4746 performOnSubElements((Relation) el);
48 return true; // probably false, but relation may contain itself in complex recursive structures
47 return true; // probably false, but relation may contain itself in
48 // complex recursive structures
4949 }
5050
5151 private void performOnSubElements(Relation rel) {
52 List<Map.Entry<String,Element>> elements = rel.getElements();
52 List<Map.Entry<String, Element>> elements = rel.getElements();
5353
54 for (Action a : actionList)
54 for (Action a : actionList) {
5555 if (a instanceof AddTagAction)
5656 ((AddTagAction) a).setValueTags(rel);
5757 else if (a instanceof AddAccessAction)
5858 ((AddAccessAction) a).setValueTags(rel);
59 }
5960
6061 boolean once = "once".equals(selector);
61 boolean first_only = "first".equals(selector);
62 HashSet<Element> elems = once ? new HashSet<Element>() : null;
62 boolean onlyFirst = "first".equals(selector);
63 HashSet<Element> elems = once ? new HashSet<>() : null;
6364
64 for (Map.Entry<String,Element> r_el : elements) {
65 if ((role == null || role.equals(r_el.getKey())) &&
66 (!once || elems.add(r_el.getValue()))) {
67
68 for (Action a : actionList)
69 a.perform(r_el.getValue());
65 for (Map.Entry<String, Element> r_el : elements) {
66 if ((role == null || role.equals(r_el.getKey())) && (!once || elems.add(r_el.getValue()))) {
67 actionList.forEach(a -> a.perform(r_el.getValue()));
7068 }
71 if (first_only)
69 if (onlyFirst)
7270 break;
7371 }
7472 }
7876 }
7977
8078 public String toString() {
81 Formatter fmt = new Formatter();
82 fmt.format("apply");
79 StringBuilder sb = new StringBuilder();
80 sb.append("apply");
8381 if (selector != null) {
84 fmt.format("_%s",selector);
82 sb.append(String.format("_%s", selector));
8583 }
8684 if (role != null)
87 fmt.format(" role=%s ", role);
88
89 fmt.format(" {");
85 sb.append(String.format(" role=%s ", role));
9086
91 for (Iterator<Action> it = actionList.iterator(); it.hasNext();) {
92 Action a = it.next();
93 fmt.format(a.toString());
94 if (it.hasNext())
95 fmt.format(" ");
96 }
97
98 fmt.format("}");
99 return fmt.toString();
87 sb.append(actionList.stream().map(Action::toString).collect(Collectors.joining(" ", "{", "}")));
88 return sb.toString();
10089 }
10190 }
3636
3737 if (i == -1) { // no occurrences of =>, let's try with ~>
3838 i = arg.indexOf("~>");
39 if ( i >= 0 ) isRegexp = true;
39 if (i >= 0)
40 isRegexp = true;
4041 }
4142
4243 if (i >= 0) {
5556 public String doFilter(String value, Element el) {
5657 if (value == null) return null;
5758 return pattern.matcher(value).replaceAll(isRegexp ? to : Matcher.quoteReplacement(to));
58 // replaceAll expects a regexp as 1st argument
59 // return (isRegexp ? value.replaceAll(from, to) : value.replace(from, to) );
6059 }
6160 }
1313 package uk.me.parabola.mkgmap.osmstyle.actions;
1414
1515 import java.util.ArrayList;
16 import java.util.HashSet;
1716 import java.util.List;
1817 import java.util.Set;
18 import java.util.stream.Collectors;
1919
2020 public abstract class ValueBuildedAction implements Action {
2121
22 private final List<ValueBuilder> valueBuilder = new ArrayList<ValueBuilder>();
22 private final List<ValueBuilder> valueBuilder = new ArrayList<>();
2323
2424 /**
2525 * Adds a value building rule.
3434 * @return all required tags
3535 */
3636 public Set<String> getUsedTags() {
37 Set<String> set = new HashSet<String>();
38 for (ValueBuilder vb : valueBuilder) {
39 set.addAll(vb.getUsedTags());
40 }
41 return set;
37 return valueBuilder.stream().flatMap(vb -> vb.getUsedTags().stream()).collect(Collectors.toSet());
4238 }
4339
4440 /**
4844 protected List<ValueBuilder> getValueBuilder() {
4945 return valueBuilder;
5046 }
47
48 protected String calcValueBuildersString() {
49 return valueBuilder.stream().map(ValueBuilder::toString).collect(Collectors.joining(" | "));
50 }
5151 }
1616 package uk.me.parabola.mkgmap.osmstyle.actions;
1717
1818 import java.util.ArrayList;
19 import java.util.HashSet;
2019 import java.util.List;
20 import java.util.Objects;
2121 import java.util.Set;
2222 import java.util.regex.Matcher;
2323 import java.util.regex.Pattern;
24 import java.util.stream.Collectors;
2425
2526 import uk.me.parabola.mkgmap.reader.osm.Element;
2627 import uk.me.parabola.mkgmap.scan.SyntaxException;
115116 case '\0':
116117 if (c == '$') {
117118 state = '$';
118 } else
119 } else {
119120 text.append(c);
121 }
120122 break;
121123 case '$':
122124 switch (c) {
152154 }
153155 }
154156
155 if (text.length() > 0)
157 if (text.length() > 0)
156158 items.add(new ValueItem(text.toString()));
157159 }
158160
159 private void addTagValue(String tagname, boolean is_local) {
161 private void addTagValue(String tagname, boolean isLocal) {
160162 ValueItem item = new ValueItem();
161163 if (tagname.contains("|")) {
162164 String[] parts = tagname.split("[ \t]*\\|", 2);
163165 assert parts.length > 1;
164166
165 item.setTagname(parts[0], is_local);
167 item.setTagname(parts[0], isLocal);
166168
167169 String s = parts[1];
168170
186188 }
187189 }
188190 } else {
189 item.setTagname(tagname, is_local);
191 item.setTagname(tagname, isLocal);
190192 }
191193 items.add(item);
192194 }
193195
194 private void addFilter(ValueItem item, String expr) {
196 private static void addFilter(ValueItem item, String expr) {
195197 Matcher matcher = NAME_ARG_SPLIT.matcher(expr);
196198
197199 matcher.matches();
244246 }
245247
246248 public String toString() {
247 StringBuilder sb = new StringBuilder("'");
248 for (ValueItem v : items) {
249 sb.append(v);
250 }
251 sb.append("'");
252 return sb.toString();
249 return items.stream().map(ValueItem::toString).collect(Collectors.joining(" | "));
253250 }
254251
255252 public Set<String> getUsedTags() {
256 Set<String> set = new HashSet<>();
257 for (ValueItem v : items) {
258 String tagname = v.getTagname();
259 if (tagname != null)
260 set.add(tagname);
261 }
262 return set;
253 return items.stream().map(ValueItem::getTagname).filter(Objects::nonNull).collect(Collectors.toSet());
263254 }
264255 }
2929 private short tagKey;
3030 private ValueFilter filter;
3131 private String value;
32 private boolean tagname_is_local;
32 private boolean tagnameIsLocal;
3333
3434 public ValueItem() {
3535 }
3838 this.value = value;
3939 }
4040
41 public String getValue(Element el, Element local_el) {
41 public String getValue(Element el, Element localElement) {
4242 if (tagname == null && value != null)
4343 return value; // already known
4444
4545 if (tagname != null) {
46 Element e = tagname_is_local ? local_el : el;
46 Element e = tagnameIsLocal ? localElement : el;
4747 String tagval = e.getTag(tagKey);
4848 if (filter != null)
49 value = filter.filter(tagval, local_el);
49 value = filter.filter(tagval, localElement);
5050 else
5151 value = tagval;
5252 }
6767
6868 public void setTagname(String tagname, boolean local) {
6969 this.tagname = tagname;
70 this.tagname_is_local = local;
70 this.tagnameIsLocal = local;
7171 this.tagKey = TagDict.getInstance().xlate(tagname);
7272 }
7373
7474 public String toString() {
75 // TODO: don't ignore filter.
7576 if (tagname == null)
7677 return value;
77 if (tagname_is_local) {
78 // TODO: don't ignore filter.
78 if (tagnameIsLocal) {
7979 return "$(" + tagname + ")";
8080 } else {
81 // TODO: don't ignore filter.
8281 return "${" + tagname + "}";
8382 }
8483 }
5454 case '(': op = new OpenOp(); break;
5555 case ')': op = new CloseOp(); break;
5656 case '>':
57 if (value.equals(">="))
57 if (">=".equals(value))
5858 op = new GTEOp();
5959 else
6060 op = new GTOp();
6161 break;
6262 case '<':
63 if (value.equals("<="))
63 if ("<=".equals(value))
6464 op = new LTEOp();
6565 else
6666 op = new LTOp();
6767 break;
6868 case '!':
69 if (value.equals("!="))
69 if ("!=".equals(value))
7070 op = new NotEqualOp();
7171 else
7272 op = new NotOp();
206206 set.addAll(getSecond().getEvaluatedTagKeys());
207207 } else if (this instanceof NumericOp) {
208208 set.addAll(getFirst().getEvaluatedTagKeys());
209 }
210 else if (this.isType(NodeType.EXISTS) || this.isType(NodeType.NOT_EXISTS) || this.isType(NodeType.NOT)) {
209 } else if (this.isType(NodeType.EXISTS) || this.isType(NodeType.NOT_EXISTS) || this.isType(NodeType.NOT)) {
211210 set.addAll(getFirst().getEvaluatedTagKeys());
212 } else if (this instanceof GetTagFunction) {
213 set.add(getKeyValue());
214211 } else if (this.getFirst() != null) {
215212 System.err.println("Unhandled type of Op");
216213 }
3939 if (lastCachedId > cacheId){
4040 throw new ExitException("fatal error: cache id invalid");
4141 }
42 lastRes = getFirst().eval(cacheId, el);
43 if (lastRes == true)
44 lastRes = getSecond().eval(cacheId, el);
42 lastRes = getFirst().eval(cacheId, el) && getSecond().eval(cacheId, el);
4543 lastCachedId = cacheId;
4644 }
47 //else System.out.println("cached: " + cacheId + " " + toString());
4845 return lastRes;
4946 }
50
51
5247
5348 public int priority() {
5449 return 5;
2121 /**
2222 * Get the second operand.
2323 */
24 public Op getSecond();
24 Op getSecond();
2525
2626 /**
2727 * Set the second operand.
2828 */
29 public void setSecond(Op second);
29 @Override
30 void setSecond(Op second);
3031 }
3737 return -100;
3838 }
3939
40 @Override
4041 public boolean hasHigherPriority(Op other) {
4142 return other.isType(OPEN_PAREN);
4243 }
3939 return 10;
4040 }
4141
42 @Override
4243 public String toString() {
43 return getFirst().toString() + "=" + getSecond();
44 return getFirst() + "=" + getSecond();
4445 }
4546 }
2929 setType(NodeType.EXISTS);
3030 }
3131
32 public <T extends Op> T setFirst(Op first) {
33 return super.setFirst(first);
34 }
35
3632 public boolean eval(Element el) {
3733 return first.value(el) != null;
3834 }
3838
3939 private final String symbol;
4040
41 private NodeType(String symbol) {
41 NodeType(String symbol) {
4242 this.symbol = symbol;
4343 }
4444
2727 setType(NodeType.NOT_EQUALS);
2828 }
2929
30 @Override
3031 public boolean eval(Element el) {
3132 return !super.eval(el);
3233 }
3334
35 @Override
3436 public String toString() {
35 return getFirst().toString() + "!=" + getSecond();
37 return getFirst() + "!=" + getSecond();
3638 }
3739 }
3838 public String toString() {
3939 if (this.hasHigherPriority(getFirst()))
4040 return "!(" + first + ")";
41 else
42 return "!" + first;
41 return "!" + first;
4342 }
4443 }
1818 setType(NodeType.NOT_REGEX);
1919 }
2020
21 @Override
2122 public boolean eval(Element el) {
2223 return !super.eval(el);
2324 }
2425
26 @Override
2527 public String toString() {
26 return getFirst().toString() + "!~" + getSecond();
28 return getFirst() + "!~" + getSecond();
2729 }
2830 }
5959 return doesCompare(inter);
6060 }
6161
62 @Override
6263 public String toString() {
6364 return first + getType().toSymbol() + getSecond();
6465 }
131131 if (t == AND || t == OR) {
132132 return AbstractOp.createOp(getType())
133133 .set(getFirst().copy(), getSecond().copy());
134 } else
135 return this;
134 }
135 return this;
136136 }
137137
138138 default void augmentWith(uk.me.parabola.mkgmap.reader.osm.ElementSaver elementSaver) {}
4747 * @param other The other operation.
4848 * @return Always returns false.
4949 */
50 @Override
5051 public boolean hasHigherPriority(Op other) {
5152 return false;
5253 }
3939 throw new ExitException("fatal error: cache id invalid");
4040 }
4141 lastRes = getFirst().eval(cacheId, el);
42 if (lastRes == false)
42 if (!lastRes)
4343 lastRes = getSecond().eval(cacheId, el);
4444 lastCachedId = cacheId;
4545 }
46 //else System.out.println("cached: " + cacheId + " " + toString());
4746 return lastRes;
4847 }
4948
4444 return 10;
4545 }
4646
47 @Override
4748 public void setSecond(Op second) {
4849 assert second.isType(VALUE);
4950 super.setSecond(second);
5051 pattern = Pattern.compile(second.getKeyValue());
5152 }
5253
54 @Override
5355 public String toString() {
54 return getFirst().toString() + "~" + getSecond();
56 return getFirst() + "~" + getSecond();
5557 }
5658
5759 }
1515 */
1616 package uk.me.parabola.mkgmap.osmstyle.eval;
1717
18 import java.util.EnumMap;
1819 import java.util.HashMap;
1920 import java.util.Map;
2021 import java.util.regex.Matcher;
3031 public class UnitConversions {
3132 private static final Pattern CODE_RE = Pattern.compile("(.*)=>(.*)");
3233
33 private static final Map<UnitType, Map<String, Double>> CONVERSIONS = new HashMap<>();
34 private static final EnumMap<UnitType, Map<String, Double>> CONVERSIONS = new EnumMap<>(UnitType.class);
3435
3536 private static final Map<String, Double> LENGTH_FACTORS = new HashMap<>();
3637 private static final Map<String, Double> SPEED_FACTORS = new HashMap<>();
150151 return null;
151152 }
152153
153 private double getFactor(String unit) {
154 assert isValid();
155
156 Double d = CONVERSIONS.get(unitType).get(unit);
157 return d == null? 0: d;
158 }
159
160154 private Double getConversion(String source) {
161155 Double in = getInFactor(unitType, source);
162156 if (in == null)
184178 return defaultFactor;
185179 }
186180
187 public static enum UnitType {
181 public enum UnitType {
188182 LENGTH,
189183 SPEED,
190184 WEIGHT,
1414 * Create date: 03-Nov-2008
1515 */
1616 package uk.me.parabola.mkgmap.osmstyle.eval;
17
18 import java.util.EnumMap;
1719
1820 import uk.me.parabola.mkgmap.reader.osm.Element;
1921
3840 return 0;
3941 }
4042
43 @Override
4144 public String value(Element el) {
4245 return value;
4346 }
2929 super(value);
3030 }
3131
32 @Override
3233 public final String value(Element el) {
3334 // check if the element type is supported by this function
3435 if (el instanceof Node ) {
35 if (supportsNode() == false) {
36 if (!supportsNode()) {
3637 return null;
3738 }
3839 } else if (el instanceof Way) {
39 if (supportsWay() == false) {
40 if (!supportsWay()) {
4041 return null;
4142 }
4243 } else if (el instanceof Relation) {
43 if (supportsRelation() == false) {
44 if (!supportsRelation()) {
4445 return null;
4546 }
4647 }
2020 */
2121 public class FunctionFactory {
2222
23 private FunctionFactory() {
24 // hide default public constructor
25 }
26
2327 /**
2428 * Returns a new instance of a style function with the given name.
2529 *
2731 * @return the style function instance or {@code null} if there is no such function
2832 */
2933 public static StyleFunction createFunction(String name) {
30 if ("length".equals(name))
34 switch (name) {
35 case "length":
3136 return new LengthFunction();
32 //} else if ("get_tag".equals(name))
33 // return new GetTagFunction(tag);
34 if ("is_closed".equals(name)) {
37 case "is_closed":
3538 return new IsClosedFunction();
39 case "is_complete":
40 return new IsCompleteFunction();
41 case "area_size":
42 return new AreaSizeFunction();
43 case "maxspeedkmh":
44 return new MaxSpeedFunction(SpeedUnit.KMH);
45 case "maxspeedmph":
46 return new MaxSpeedFunction(SpeedUnit.MPH);
47 case "type":
48 return new TypeFunction();
49 case "osmid":
50 return new OsmIdFunction();
51 case "is_in":
52 return new IsInFunction();
53 case "is_drive_on_left":
54 return new IsDriveOnLeftFunction();
55 default:
56 return null;
3657 }
37 if ("is_complete".equals(name)) {
38 return new IsCompleteFunction();
39 }
40 if ("area_size".equals(name))
41 return new AreaSizeFunction();
42 if ("maxspeedkmh".equals(name))
43 return new MaxSpeedFunction(SpeedUnit.KMH);
44 if ("maxspeedmph".equals(name))
45 return new MaxSpeedFunction(SpeedUnit.MPH);
46 if ("type".equals(name))
47 return new TypeFunction();
48 if ("osmid".equals(name))
49 return new OsmIdFunction();
50 if ("is_in".equals(name))
51 return new IsInFunction();
52
53 return null;
5458 }
5559 }
0 package uk.me.parabola.mkgmap.osmstyle.function;
1
2 import uk.me.parabola.log.Logger;
3 import uk.me.parabola.mkgmap.build.LocatorConfig;
4 import uk.me.parabola.mkgmap.reader.osm.Element;
5 import uk.me.parabola.mkgmap.reader.osm.TagDict;
6
7 /**
8 * Returns the drive-on-left information for an element based on the information
9 * stored in tag mkgmap:country and the LocatorConfig.xml. Returns "true" if
10 * mkgmap:country is set and contains an iso code that has the driveOnLeft attribute in the LocatorConfig.xml.
11 */
12 public class IsDriveOnLeftFunction extends CachedFunction {
13 private static final Logger log = Logger.getLogger(IsDriveOnLeftFunction.class);
14 private static final short TKM_ADM_LVL2 = TagDict.getInstance().xlate("mkgmap:admin_level2");
15 private static final short TKM_COUNTRY = TagDict.getInstance().xlate("mkgmap:country");
16
17 public IsDriveOnLeftFunction() {
18 super(null);
19 }
20
21 protected String calcImpl(Element el) {
22 String iso = el.getTag(TKM_ADM_LVL2);
23 if (iso == null)
24 iso = el.getTag(TKM_COUNTRY);
25 if (iso == null && log.isInfoEnabled()) {
26 log.info(getName(), el.getBasicLogInformation(), "Neither mkgmap:admin_level2 nor mkgmap:country is set, assuming this element is not in a drive-on-left country");
27 }
28 return Boolean.toString(LocatorConfig.get().getDriveOnLeftFlag(iso));
29 }
30
31 @Override
32 public String getName() {
33 return "is_drive_on_left";
34 }
35
36 @Override
37 public boolean supportsNode() {
38 return true;
39 }
40
41 @Override
42 public boolean supportsWay() {
43 return true;
44 }
45
46 }
170170 int assignedNumbers = 0;
171171 startInRoad = startSegment;
172172 endInRoad = endSegment;
173 if (housenumbers.isEmpty() == false) {
173 if (!housenumbers.isEmpty()) {
174174 RoadSide rs = new RoadSide();
175175 if (left)
176176 leftSide = rs;
215215 private void fillNumbers(boolean left) {
216216 NumberStyle style = NumberStyle.NONE;
217217 List<HousenumberMatch> houses = getHouses(left);
218 if (houses.isEmpty() == false) {
218 if (!houses.isEmpty()) {
219219 Set<CityInfo> cityInfos = new HashSet<>();
220220 Set<ZipCodeInfo> zipCodes = new HashSet<>();
221221 // get the sublist of house numbers
231231 HousenumberMatch pred = null;
232232 for (int i = 0; i< numHouses; i++) {
233233 HousenumberMatch house = houses.get(i);
234 if (house.getCityInfo() != null && house.getCityInfo().isEmpty() == false)
234 if (house.getCityInfo() != null && !house.getCityInfo().isEmpty())
235235 cityInfos.add(house.getCityInfo());
236236 if (house.getZipCode() != null && house.getZipCode().getZipCode() != null)
237237 zipCodes.add(house.getZipCode());
315315 rs.multipleZipCodes = (zipCodes.size() > 1);
316316 if (cityInfos.size() == 1){
317317 CityInfo ci = cityInfos.iterator().next();
318 if (ci.isEmpty() == false){
319 if (ci.equals(housenumberRoad.getRoadCityInfo()) == false)
320 numbers.setCityInfo(left, ci);
318 if (!ci.isEmpty() && !ci.equals(housenumberRoad.getRoadCityInfo())) {
319 numbers.setCityInfo(left, ci);
321320 }
322321 }
323322
324323 if (zipCodes.size() == 1){
325324 ZipCodeInfo zipCodeInfo = zipCodes.iterator().next();
326 if (zipCodeInfo.getZipCode() != null){
327 if (zipCodeInfo.equals(housenumberRoad.getRoadZipCode()) == false){
325 if (zipCodeInfo.getZipCode() != null) {
326 if (!zipCodeInfo.equals(housenumberRoad.getRoadZipCode())) {
328327 // we found a zip code and the road doesn't yet have one, use it for the whole road
329328 if (housenumberRoad.getRoadZipCode() == null){
330329 housenumberRoad.setZipCodeInfo(zipCodeInfo);
356355 list.add(cn);
357356 }
358357 if (log.isInfoEnabled()) {
359 if (headerWasReported == false) {
358 if (!headerWasReported) {
360359 MapRoad road = curr.getRoad();
361360 if (road.getStreet() == null && road.getName() == null)
362361 log.info("final numbers for", road, curr.housenumberRoad.getName(), "in", road.getCity());
405404 }
406405 }
407406 for (curr = head; curr != null; curr = curr.next){
408 while (curr.isPlausible() == false){
407 while (!curr.isPlausible()) {
409408 // this happens in the following cases:
410409 // 1. correct OSM data, multiple houses build a block. Standing on the road
411410 // you probably see a small service road which leads to the houses.
471470 public ExtNumbers tryChange(int reason){
472471 ExtNumbers en = this;
473472 if (reason == SR_FIX_ERROR){
474 if (notInOrder(Numbers.LEFT) == false && notInOrder(Numbers.RIGHT) == false){
473 if (!notInOrder(Numbers.LEFT) && !notInOrder(Numbers.RIGHT)) {
475474 if (badNum < 0 && worstHouse != null)
476475 badNum = worstHouse.getHousenumber();
477476 if (badNum > 0){
491490 changedInterval = true;
492491 } else {
493492 ExtNumbers test = en.hasNumbers() ? en : en.next;
494 if (test.getNumbers().isSimilar(this.getNumbers()) == false)
493 if (!test.getNumbers().isSimilar(this.getNumbers()))
495494 changedInterval = true;
496495 }
497496 if (changedInterval)
823822 toAdd = null;
824823 else if (usedFraction > minFraction0To1 && wantedFraction < minFraction0To1 || usedFraction < maxFraction0To1 && wantedFraction > maxFraction0To1){
825824 toAdd = null;
826 } else if (allowSplitBetween == false && usedFraction > minFraction0To1 && usedFraction < maxFraction0To1){
825 } else if (!allowSplitBetween && usedFraction > minFraction0To1 && usedFraction < maxFraction0To1){
827826 toAdd = null;
828827 } else {
829828 if (bestDist > 0.2){
969968 this.endInRoad = addAsNumberNode(splitSegment, toAdd);
970969
971970 // distribute the houses to the new intervals
972 List<ArrayList<HousenumberMatch>> leftTargets = Arrays.asList(new ArrayList<HousenumberMatch>(),new ArrayList<HousenumberMatch>());
973 List<ArrayList<HousenumberMatch>> rightTargets = Arrays.asList(new ArrayList<HousenumberMatch>(),new ArrayList<HousenumberMatch>());
971 List<ArrayList<HousenumberMatch>> leftTargets = Arrays.asList(new ArrayList<>(),new ArrayList<>());
972 List<ArrayList<HousenumberMatch>> rightTargets = Arrays.asList(new ArrayList<>(),new ArrayList<>());
974973 int target;
975974 if (reason == SR_SPLIT_ROAD_END || reason == SR_OPT_LEN){
976975 for (int side = 0; side < 2; side++){
10041003 else target = (atStart) ? 0 : 1;
10051004 targets.get(target).add(house);
10061005 }
1007 } else if (multipleZipOrCity(left) == false){
1006 } else if (!multipleZipOrCity(left)) {
10081007 if (atStart)
10091008 targets.get(1).addAll(getHouses(left));
10101009 else
12281227 for (ExtNumbers en1 = head; en1 != null; en1 = en1.next){
12291228 if (anyChanges)
12301229 break;
1231 if (en1.hasNumbers() == false)
1230 if (!en1.hasNumbers())
12321231 continue;
12331232 for (ExtNumbers en2 = en1.next; en2 != null; en2 = en2.next){
12341233 if (anyChanges)
12351234 break;
1236 if (en2.hasNumbers() == false)
1235 if (!en2.hasNumbers())
12371236 continue;
12381237
12391238 int res = checkIntervals(streetName, en1, en2);
13221321 ok = checkIntervalBoundaries(s1, e1, s2, e2, left1 == left2 && en1.getRoad() == en2.getRoad());
13231322 if (ok)
13241323 continue;
1325 if (en1.getRoad() != en2.getRoad() && en1.hasGaps == false && en2.hasGaps == false){
1324 if (en1.getRoad() != en2.getRoad() && !en1.hasGaps && !en2.hasGaps) {
13261325 allOK = false;
13271326 continue;
13281327 }
13561355 List<HousenumberMatch> possibleRemoves1 = new ArrayList<>();
13571356 List<HousenumberMatch> possibleRemoves2 = new ArrayList<>();
13581357
1359 if (en1.housenumberRoad.isRandom() == false && en2.housenumberRoad.isRandom() == false){
1358 if (!en1.housenumberRoad.isRandom() && !en2.housenumberRoad.isRandom()) {
13601359 // check if we can move a house from en1 to en2
13611360 for (HousenumberMatch house : houses1){
13621361 if (house.getGroup() != null)
14831482 if (delta1 > 0 && delta2 > 0){
14841483 if (en1.hasGaps != en2.hasGaps){
14851484 if (en1.hasGaps){
1486 if (possibleRemoves1.isEmpty() == false)
1485 if (!possibleRemoves1.isEmpty())
14871486 splitNum = possibleRemoves1.get(0).getHousenumber();
14881487 toSplit = en1;
14891488 } else {
1490 if (possibleRemoves2.isEmpty() == false)
1489 if (!possibleRemoves2.isEmpty())
14911490 splitNum = possibleRemoves2.get(0).getHousenumber();
14921491 toSplit = en2;
14931492 }
16541653 }
16551654
16561655 public boolean hasNumbers(){
1657 return getNumbers().isEmpty() == false;
1656 return !getNumbers().isEmpty();
16581657 }
16591658
16601659 /**
16631662 * @return
16641663 */
16651664 public ExtNumbers splitLargeGaps(){
1666 if (hasNumbers() == false)
1665 if (!hasNumbers())
16671666 return this;
16681667 // calculate the length of each road segment and
16691668 // the overall length covered by this interval
18571856 double distToTarget = t.distance(p);
18581857
18591858 if (beforeTarget){
1860 if (Double.isNaN(lastDist) == false && lastDist < distToTarget)
1859 if (!Double.isNaN(lastDist) && lastDist < distToTarget)
18611860 beforeTarget = false;
18621861 }
18631862 if (beforeTarget && distToTarget < maxBefore || !beforeTarget && distToTarget < maxAfter){
18641863 Double distLine = t.distToLineSegment(c1Dspl, c2Dspl);
1865 List<Coord> list = sortedByDistToLine.get(distLine);
1866 if (list == null){
1867 list = new ArrayList<>();
1868 sortedByDistToLine.put(distLine, list);
1869 }
1870 list.add(t);
1864 sortedByDistToLine.computeIfAbsent(distLine, k -> new ArrayList<>()).add(t);
18711865 }
18721866 lastDist = distToTarget;
18731867 }
19061900 int countFilledSides = 0;
19071901 int countNotInOrder = 0;
19081902 for (ExtNumbers curr = this; curr != null; curr = curr.next){
1909 if (curr.hasNumbers() == false)
1903 if (!curr.hasNumbers())
19101904 continue;
19111905 countFilledIvls++;
19121906 if (curr.notInOrder(Numbers.LEFT))
19261920 }
19271921
19281922 private boolean isPlausible(){
1929 if (getNumbers().isPlausible() == false)
1923 if (!getNumbers().isPlausible())
19301924 return false;
19311925 if (multipleZipOrCity(true) || multipleZipOrCity(false))
19321926 return false;
8282 private HashMap<CityInfo, CityInfo> cityInfos = new HashMap<>();
8383 private HashMap<ZipCodeInfo, ZipCodeInfo> zipInfos = new HashMap<>();
8484
85 private static final short housenumberTagKey1 = TagDict.getInstance().xlate("mkgmap:housenumber");
86 private static final short housenumberTagKey2 = TagDict.getInstance().xlate("addr:housenumber");
87 private static final short streetTagKey = TagDict.getInstance().xlate("mkgmap:street");
88 private static final short addrStreetTagKey = TagDict.getInstance().xlate("addr:street");
89 private static final short addrInterpolationTagKey = TagDict.getInstance().xlate("addr:interpolation");
90 private static final short addrPlaceTagKey = TagDict.getInstance().xlate("addr:place");
91 private static final short cityTagKey = TagDict.getInstance().xlate("mkgmap:city");
92 private static final short regionTagKey = TagDict.getInstance().xlate("mkgmap:region");
93 private static final short countryTagKey = TagDict.getInstance().xlate("mkgmap:country");
94 private static final short postalCodeTagKey = TagDict.getInstance().xlate("mkgmap:postal_code");
95 private static final short numbersTagKey = TagDict.getInstance().xlate("mkgmap:numbers");
85 private static final short TKM_HOUSENUMBER = TagDict.getInstance().xlate("mkgmap:housenumber");
86 private static final short TK_ADDR_HOUSENUMBER = TagDict.getInstance().xlate("addr:housenumber");
87 private static final short TKM_STREET = TagDict.getInstance().xlate("mkgmap:street");
88 private static final short TK_ADDR_STREET = TagDict.getInstance().xlate("addr:street");
89 private static final short TK_ADDR_INTERPOLATION = TagDict.getInstance().xlate("addr:interpolation");
90 private static final short TK_ADDR_PLACE = TagDict.getInstance().xlate("addr:place");
91 private static final short TKM_CITY = TagDict.getInstance().xlate("mkgmap:city");
92 private static final short TKM_REGION = TagDict.getInstance().xlate("mkgmap:region");
93 private static final short TKM_COUNTRY = TagDict.getInstance().xlate("mkgmap:country");
94 private static final short TKM_POSTAL_CODE = TagDict.getInstance().xlate("mkgmap:postal_code");
95 private static final short TKM_NUMBERS = TagDict.getInstance().xlate("mkgmap:numbers");
9696
9797 public HousenumberGenerator(EnhancedProperties props) {
9898 this.interpolationWays = new MultiHashMap<>();
102102
103103 numbersEnabled = props.containsKey("housenumbers");
104104 int n = props.getProperty("name-service-roads", 3);
105 if (n != nameSearchDepth){
105 if (n != nameSearchDepth) {
106106 nameSearchDepth = Math.min(25, Math.max(0, n));
107 if (nameSearchDepth != n)
107 if (nameSearchDepth != n) {
108108 System.err.println("name-service-roads=" + n + " was changed to name-service-roads=" + nameSearchDepth);
109 }
109110 }
110111 }
111112
115116 * @return the street name (or {@code null} if no street name set)
116117 */
117118 private static String getStreetname(Element e) {
118 String streetname = e.getTag(streetTagKey);
119 String streetname = e.getTag(TKM_STREET);
119120 if (streetname == null) {
120 streetname = e.getTag(addrStreetTagKey);
121 streetname = e.getTag(TK_ADDR_STREET);
121122 }
122123 return streetname;
123124 }
128129 * @return the house number (or {@code null} if no house number set)
129130 */
130131 public static String getHousenumber(Element e) {
131 String res = e.getTag(housenumberTagKey1);
132 String res = e.getTag(TKM_HOUSENUMBER);
132133 if (res != null)
133134 return res;
134 return e.getTag(housenumberTagKey2);
135 return e.getTag(TK_ADDR_HOUSENUMBER);
135136 }
136137
137138 /**
183184 int housenumber;
184185 Pattern p = Pattern.compile("\\D*(\\d+)\\D?.*");
185186 Matcher m = p.matcher(housenumberString);
186 if (m.matches() == false) {
187 if (!m.matches()) {
187188 return null;
188189 }
189190 try {
196197 }
197198
198199
199 private HousenumberElem parseElement(Element el, String sign){
200 String city = el.getTag(cityTagKey);
201 String region = el.getTag(regionTagKey);
202 String country = el.getTag(countryTagKey);
200 private HousenumberElem parseElement(Element el, String sign) {
201 String city = el.getTag(TKM_CITY);
202 String region = el.getTag(TKM_REGION);
203 String country = el.getTag(TKM_COUNTRY);
203204 CityInfo ci = getCityInfos(city,region,country);
204205 HousenumberElem house = new HousenumberElem(el, ci);
205 if (house.getLocation() == null){
206 if (house.getLocation() == null) {
206207 // there has been a report that indicates match.getLocation() == null
207208 // could not reproduce so far but catching it here with some additional
208209 // information. (WanMil)
215216
216217 house.setSign(sign);
217218 Integer hn = parseHousenumber(sign);
218 if (hn == null){
219 if (hn == null) {
219220 if (log.isDebugEnabled())
220221 log.debug("No housenumber (", el.toBrowseURL(), "): ", sign);
221222 return null;
222223 }
223 if (hn < 0 || hn > 1_000_000){
224 if (hn < 0 || hn > 1_000_000) {
224225 log.warn("Number looks wrong, is ignored",house.getSign(),hn,"element",el.toBrowseURL());
225226 return null;
226227 }
227228 house.setHousenumber(hn);
228229 house.setStreet(getStreetname(el));
229 house.setPlace(el.getTag(addrPlaceTagKey));
230 String zipStr = el.getTag(postalCodeTagKey);
230 house.setPlace(el.getTag(TK_ADDR_PLACE));
231 String zipStr = el.getTag(TKM_POSTAL_CODE);
231232 ZipCodeInfo zip = getZipInfos(zipStr);
232233 house.setZipCode(zip);
233234 return house;
238239 CityInfo ciOld = cityInfos.get(ci);
239240 if (ciOld != null)
240241 return ciOld;
241 // log.debug(ci);
242242 cityInfos.put(ci, ci);
243243 return ci;
244244 }
252252 return zip;
253253 }
254254
255 private HousenumberElem handleElement(Element el){
255 private HousenumberElem handleElement(Element el) {
256256 String sign = getHousenumber(el);
257257 if (sign == null)
258258 return null;
268268 * @param n an OSM node
269269 */
270270 public void addNode(Node n) {
271 if (numbersEnabled == false) {
271 if (!numbersEnabled) {
272272 return;
273273 }
274 if("false".equals(n.getTag(numbersTagKey)))
274 if("false".equals(n.getTag(TKM_NUMBERS)))
275275 return;
276276
277 if ("true".equals(n.getTag(POIGeneratorHook.AREA2POI_TAG))){
277 if ("true".equals(n.getTag(POIGeneratorHook.TKM_AREA2POI))) {
278278 // ignore POI created for buildings
279279 return;
280280 }
282282 if (houseElem == null)
283283 return;
284284
285 if (n.getTag(HousenumberHooks.partOfInterpolationTagKey) != null)
285 if (n.getTag(HousenumberHooks.TKM_PART_OF_INTERPOLATION) != null)
286286 interpolationNodes.put(n.getId(),houseElems.size()-1);
287287 }
288288
291291 * @param w a way
292292 */
293293 public void addWay(Way w) {
294 if (numbersEnabled == false) {
294 if (!numbersEnabled) {
295295 return;
296296 }
297 if("false".equals(w.getTag(numbersTagKey)))
297 if("false".equals(w.getTag(TKM_NUMBERS)))
298298 return;
299299
300 String ai = w.getTag(addrInterpolationTagKey);
301 if (ai != null){
300 String ai = w.getTag(TK_ADDR_INTERPOLATION);
301 if (ai != null) {
302302 // the way has the addr:interpolation=* tag, parse info
303303 // created by the HousenumberHook
304304 List<HousenumberElem> nodes = new ArrayList<>();
305 String nodeIds = w.getTag(HousenumberHooks.mkgmapNodeIdsTagKey);
306 if (nodeIds == null){
305 String nodeIds = w.getTag(HousenumberHooks.TKM_NODE_IDS);
306 if (nodeIds == null) {
307307 // way was rejected by hook
308308 } else {
309309 String[] ids = nodeIds.split(",");
310 for (String idString : ids){
310 for (String idString : ids) {
311311 Long id = Long.decode(idString);
312312 Integer elemPos = interpolationNodes.get(id);
313 if (elemPos != null){
313 if (elemPos != null) {
314314 HousenumberElem node = houseElems.get(elemPos);
315 if (node != null){
315 if (node != null) {
316316 assert node.getElement().getId() == id;
317317 nodes.add(node);
318318 }
323323 return;
324324 }
325325
326 if (w.hasIdenticalEndPoints()){
326 if (w.hasIdenticalEndPoints()) {
327327 // we are only interested in polygons now
328328 handleElement(w);
329329 }
341341 */
342342 private void interpretInterpolationWay(Way w, List<HousenumberElem> nodes) {
343343 int numNodes = nodes.size();
344 String addrInterpolationMethod = w.getTag(addrInterpolationTagKey);
344 String addrInterpolationMethod = w.getTag(TK_ADDR_INTERPOLATION);
345345 int step = 0;
346346 switch (addrInterpolationMethod) {
347347 case "all":
361361 int pos = 0;
362362 List<HousenumberIvl> hivls = new ArrayList<>();
363363 String streetName = null;
364 for (int i = 0; i+1 < numNodes; i++){
364 for (int i = 0; i+1 < numNodes; i++) {
365365 // the way have other points, find the sequence including the pair of nodes
366366 HousenumberElem he1 = nodes.get(i);
367367 HousenumberElem he2 = nodes.get(i+1);
368 int pos1 = -1, pos2 = -1;
369 for (int k = pos; k < w.getPoints().size(); k++){
370 if (w.getPoints().get(k) == he1.getLocation()){
368 int pos1 = -1;
369 for (int k = pos; k < w.getPoints().size(); k++) {
370 if (w.getPoints().get(k) == he1.getLocation()) {
371371 pos1 = k;
372372 break;
373373 }
374374 }
375 if (pos1 < 0){
375 if (pos1 < 0) {
376376 log.error("addr:interpolation node not found in way",w);
377377 return;
378378 }
379 for (int k = pos1+1; k < w.getPoints().size(); k++){
380 if (w.getPoints().get(k) == he2.getLocation()){
379 int pos2 = -1;
380 for (int k = pos1+1; k < w.getPoints().size(); k++) {
381 if (w.getPoints().get(k) == he2.getLocation()) {
381382 pos2 = k;
382383 break;
383384 }
384385 }
385 if (pos2 < 0){
386 if (pos2 < 0) {
386387 log.error("addr:interpolation node not found in way",w);
387388 return;
388389 }
389390 pos = pos2;
390391 String street = he1.getStreet();
391 if (street != null && street.equals(he2.getStreet())){
392 if (street != null && street.equals(he2.getStreet())) {
392393 if (streetName == null)
393394 streetName = street;
394 else if (streetName.equals(street) == false){
395 else if (!streetName.equals(street)) {
395396 log.warn(w.toBrowseURL(),"addr:interpolation=even is used with different street names",streetName,street);
396397 return;
397398 }
404405 hivl.setStep(step);
405406 hivl.calcSteps();
406407 hivl.setPoints(w.getPoints().subList(pos1, pos2+1));
407 // if (pos1 > 0){
408 // double angle = Utils.getAngle(w.getPoints().get(pos1-1), w.getPoints().get(pos1), w.getPoints().get(pos1+1));
409 // if (Math.abs(angle) > 75){
410 // log.warn(w.toBrowseURL(),"addr:interpolation way has sharp angle at number",start,"cannot use it");
411 // return;
412 // }
413 //
414 // }
415408
416409 hivls.add(hivl);
417 if ("even".equals(addrInterpolationMethod) && (start % 2 != 0 || end % 2 != 0)){
410 if ("even".equals(addrInterpolationMethod) && (start % 2 != 0 || end % 2 != 0)) {
418411 log.warn(w.toBrowseURL(),"addr:interpolation=even is used with odd housenumber(s)",start,end);
419412 return;
420413 }
421 if ("odd".equals(addrInterpolationMethod) && (start % 2 == 0 || end % 2 == 0)){
414 if ("odd".equals(addrInterpolationMethod) && (start % 2 == 0 || end % 2 == 0)) {
422415 log.warn(w.toBrowseURL(),"addr:interpolation=odd is used with even housenumber(s)",start,end);
423416 return;
424417 }
425418
426 if (start == end && he1.getSign().equals(he2.getSign())){
427 // handle special case from CanVec imports
428 if (pos1 == 0 && pos2 +1 == w.getPoints().size()){
429 hivl.setEqualEnds();
430 log.warn(w.toBrowseURL(),"addr:interpolation way connects two points with equal numbers, numbers are ignored");
431 }
419 if (start == end && he1.getSign().equals(he2.getSign()) && pos1 == 0
420 && pos2 + 1 == w.getPoints().size()) {
421 // handle special case from CanVec imports
422 hivl.setEqualEnds();
423 log.warn(w.toBrowseURL(),
424 "addr:interpolation way connects two points with equal numbers, numbers are ignored");
432425 }
433426 }
434427 }
445438 public void addRoad(Way osmRoad, MapRoad road) {
446439 allRoads.add(road);
447440 if (numbersEnabled) {
448 if("false".equals(osmRoad.getTag(numbersTagKey)))
441 if("false".equals(osmRoad.getTag(TKM_NUMBERS)))
449442 road.setSkipHousenumberProcessing(true);
450443
451444 /*
453446 * only the first. This ensures that we don't try to assign numbers from bad
454447 * matches to these copies.
455448 */
456 if(!road.isSkipHousenumberProcessing()){
457 if (firstRoadSameOSMWay != null){
458 if (firstRoadSameOSMWay.getRoadDef().getId() == road.getRoadDef().getId()){
459 if (firstRoadSameOSMWay.getPoints().equals(road.getPoints())){
460 road.setSkipHousenumberProcessing(true);
461 return;
462 }
463 }
464 }
449 if(!road.isSkipHousenumberProcessing()) {
450 if (firstRoadSameOSMWay != null
451 && firstRoadSameOSMWay.getRoadDef().getId() == road.getRoadDef().getId()
452 && firstRoadSameOSMWay.getPoints().equals(road.getPoints())) {
453 road.setSkipHousenumberProcessing(true);
454 return;
455 }
465456 road.getPoints().get(0).setNumberNode(true);
466457 road.getPoints().get(road.getPoints().size() - 1).setNumberNode(true);
467458 firstRoadSameOSMWay = road;
479470 public void addRelation(Relation r) {
480471 String relType = r.getTag("type");
481472 // the wiki says that we should also evaluate type=street
482 if ("associatedStreet".equals(relType) || "street".equals(relType)){
473 if ("associatedStreet".equals(relType) || "street".equals(relType)) {
483474 List<Element> houses= new ArrayList<>();
484475 List<Element> streets = new ArrayList<>();
485476 for (Map.Entry<String, Element> member : r.getElements()) {
499490 streets.add(w);
500491 break;
501492 case "":
502 if (w.getTag("highway") != null){
493 if (w.getTag("highway") != null) {
503494 streets.add(w);
504495 continue;
505496 }
520511 String streetNameFromRoads = null;
521512 List<Element> unnamedStreetElems = new ArrayList<>();
522513 boolean nameFromStreetsIsUnclear = false;
523 if (streets.isEmpty() == false) {
514 if (!streets.isEmpty()) {
524515 for (Element street : streets) {
525 String roadName = street.getTag(streetTagKey);
516 String roadName = street.getTag(TKM_STREET);
526517 if (roadName == null)
527518 roadName = street.getTag("name");
528 if (roadName == null){
519 if (roadName == null) {
529520 unnamedStreetElems.add(street);
530521 continue;
531522 }
532523 if (streetNameFromRoads == null)
533524 streetNameFromRoads = roadName;
534 else if (streetNameFromRoads.equals(roadName) == false)
525 else if (!streetNameFromRoads.equals(roadName)) {
535526 nameFromStreetsIsUnclear = true;
536 }
537 }
538 if (streetName == null){
539 if (nameFromStreetsIsUnclear == false)
527 }
528 }
529 }
530 if (streetName == null) {
531 if (!nameFromStreetsIsUnclear) {
540532 streetName = streetNameFromRoads;
541 else {
533 } else {
542534 log.warn("Relation",r.toBrowseURL(),": ignored, street name is not clear.");
543535 return;
544536 }
545537
546538 } else {
547 if (streetNameFromRoads != null){
548 if (nameFromStreetsIsUnclear == false && streetName.equals(streetNameFromRoads) == false){
549 if (unnamedStreetElems.isEmpty() == false){
539 if (streetNameFromRoads != null) {
540 if (!nameFromStreetsIsUnclear && !streetName.equals(streetNameFromRoads)) {
541 if (!unnamedStreetElems.isEmpty()) {
550542 log.warn("Relation",r.toBrowseURL(),": ignored, street name is not clear.");
551543 return;
552544 }
553545 log.warn("Relation",r.toBrowseURL(),": street name is not clear, using the name from the way, not that of the relation.");
554546 streetName = streetNameFromRoads;
555547 }
556 else if (nameFromStreetsIsUnclear == true){
548 else if (nameFromStreetsIsUnclear) {
557549 log.warn("Relation",r.toBrowseURL(),": street name is not clear, using the name from the relation.");
558550 }
559551 }
560552 }
561553 int countModHouses = 0;
562 if (streetName != null && streetName.isEmpty() == false){
554 if (streetName != null && !streetName.isEmpty()) {
563555 for (Element house : houses) {
564 if (addStreetTagFromRel(r, house, streetName) )
556 if (addStreetTagFromRel(r, house, streetName))
565557 countModHouses++;
566558 }
567559 for (Element street : unnamedStreetElems) {
568 street.addTag(streetTagKey, streetName);
560 street.addTag(TKM_STREET, streetName);
569561 street.addTag("name", streetName);
570562 }
571563 }
572 if (log.isInfoEnabled()){
573 if (countModHouses > 0 || !unnamedStreetElems.isEmpty()){
564 if (log.isInfoEnabled()) {
565 if (countModHouses > 0 || !unnamedStreetElems.isEmpty()) {
574566 if (countModHouses > 0)
575567 log.info("Relation",r.toBrowseURL(),": added tag mkgmap:street=",streetName,"to",countModHouses,"of",houses.size(),"house members" );
576568 if (!unnamedStreetElems.isEmpty())
587579 * Add the tag mkgmap:street=streetName to the element of the
588580 * relation if it does not already have a street name tag.
589581 */
590 private static boolean addStreetTagFromRel(Relation r, Element house, String streetName){
582 private static boolean addStreetTagFromRel(Relation r, Element house, String streetName) {
591583 String addrStreet = getStreetname(house);
592 if (addrStreet == null){
593 house.addTag(streetTagKey, streetName);
584 if (addrStreet == null) {
585 house.addTag(TKM_STREET, streetName);
594586 if (log.isDebugEnabled())
595587 log.debug("Relation",r.toBrowseURL(),": adding tag mkgmap:street=" + streetName, "to house",house.toBrowseURL());
596588 return true;
597589 }
598 else if (addrStreet.equals(streetName) == false){
599 if (house.getTag(streetTagKey) != null){
590 if (!addrStreet.equals(streetName)) {
591 if (house.getTag(TKM_STREET) != null) {
600592 log.warn("Relation",r.toBrowseURL(),": street name from relation doesn't match existing mkgmap:street tag for house",house.toBrowseURL(),"the house seems to be member of another type=associatedStreet relation");
601 house.deleteTag(streetTagKey);
602 }
603 else
593 house.deleteTag(TKM_STREET);
594 } else {
604595 log.warn("Relation",r.toBrowseURL(),": street name from relation doesn't match existing name for house",house.toBrowseURL());
596 }
605597 }
606598 return false;
607599 }
622614 initialHousesForRoads = null;
623615 log.info("found",hnrList.size(),"road candidates for address search");
624616
625 useAddrPlaceTag(hnrList);
617 useAddrPlaceTag();
626618 Map<MapRoad, HousenumberRoad> road2HousenumberRoadMap = new HashMap<>();
627 for (HousenumberRoad hnr : hnrList){
619 for (HousenumberRoad hnr : hnrList) {
628620 road2HousenumberRoadMap.put(hnr.getRoad(), hnr);
629621 }
630622 Int2ObjectOpenHashMap<HashSet<MapRoad>> nodeId2RoadLists = new Int2ObjectOpenHashMap<>();
631 for (MapRoad road : allRoads){
632 for (Coord co : road.getPoints()){
623 for (MapRoad road : allRoads) {
624 for (Coord co : road.getPoints()) {
633625 if (co.getId() == 0)
634626 continue;
635627 HashSet<MapRoad> connectedRoads = nodeId2RoadLists.get(co.getId());
636 if (connectedRoads == null){
628 if (connectedRoads == null) {
637629 connectedRoads = new HashSet<>();
638630 nodeId2RoadLists.put(co.getId(), connectedRoads);
639631 }
642634 }
643635 List<HousenumberRoad> addedRoads = new ArrayList<>();
644636 Iterator<HousenumberRoad> iter = hnrList.iterator();
645 while (iter.hasNext()){
637 while (iter.hasNext()) {
646638 HousenumberRoad hnr = iter.next();
647639
648640 List<HousenumberMatch> lostHouses = hnr.checkStreetName(road2HousenumberRoadMap, nodeId2RoadLists);
649 for (HousenumberMatch house : lostHouses){
641 for (HousenumberMatch house : lostHouses) {
650642 MapRoad r = house.getRoad();
651 if (r != null){
643 if (r != null) {
652644 HousenumberRoad hnr2 = road2HousenumberRoadMap.get(r);
653 if (hnr2 == null){
645 if (hnr2 == null) {
654646 CityInfo ci = getCityInfos(r.getCity(), r.getRegion(), r.getCountry());
655647 hnr2 = new HousenumberRoad(r, ci, Arrays.asList(house));
656648 if (r.getZip() != null)
662654 }
663655 }
664656 }
665 if (hnr.getName() == null){
657 if (hnr.getName() == null) {
666658 iter.remove();
667 for (HousenumberMatch house : hnr.getHouses()){
659 for (HousenumberMatch house : hnr.getHouses()) {
668660 log.warn("found no plausible road name for address",house.toBrowseURL(),", closest road id:",house.getRoad());
669661 }
670662 }
671663 }
672664 hnrList.addAll(addedRoads);
673665 // TODO: interpolate addr:interpolation houses
674 removeDupsGroupedByCityAndName(hnrList);
666 removeDupsGroupedByCityAndName();
675667
676668 // group by street name and city
677669 TreeMap<String, TreeMap<CityInfo, List<HousenumberRoad>>> streetnameCityRoadMap = new TreeMap<>();
678 for (HousenumberRoad hnr : hnrList){
679 TreeMap<CityInfo, List<HousenumberRoad>> cluster = streetnameCityRoadMap.get(hnr.getName());
680 if (cluster == null){
681 cluster = new TreeMap<>();
682 streetnameCityRoadMap.put(hnr.getName(), cluster);
683 }
684 List<HousenumberRoad> roadsInCluster = cluster.get(hnr.getRoadCityInfo());
685 if (roadsInCluster == null){
686 roadsInCluster = new ArrayList<>();
687 cluster.put(hnr.getRoadCityInfo(), roadsInCluster);
688 }
689 roadsInCluster.add(hnr);
690 }
691
692 for (Entry<String, TreeMap<CityInfo, List<HousenumberRoad>>> streetNameEntry : streetnameCityRoadMap.entrySet()){
670 for (HousenumberRoad hnr : hnrList) {
671 TreeMap<CityInfo, List<HousenumberRoad>> cluster = streetnameCityRoadMap.computeIfAbsent(hnr.getName(),
672 k -> new TreeMap<>());
673 cluster.computeIfAbsent(hnr.getRoadCityInfo(), k -> new ArrayList<>()).add(hnr);
674 }
675
676 for (Entry<String, TreeMap<CityInfo, List<HousenumberRoad>>> streetNameEntry : streetnameCityRoadMap.entrySet()) {
693677 String streetName = streetNameEntry.getKey();
694678
695 for (Entry<CityInfo, List<HousenumberRoad>> clusterEntry : streetNameEntry.getValue().entrySet()){
679 for (Entry<CityInfo, List<HousenumberRoad>> clusterEntry : streetNameEntry.getValue().entrySet()) {
696680 useInterpolationInfo(streetName, clusterEntry.getValue(), road2HousenumberRoadMap);
697681 }
698682
699 for (Entry<CityInfo, List<HousenumberRoad>> clusterEntry : streetNameEntry.getValue().entrySet()){
683 for (Entry<CityInfo, List<HousenumberRoad>> clusterEntry : streetNameEntry.getValue().entrySet()) {
700684 List<HousenumberRoad> roadsInCluster = clusterEntry.getValue();
701 if (log.isDebugEnabled()){
685 if (log.isDebugEnabled()) {
702686 log.debug("processing road(s) with name",streetName,"in",clusterEntry.getKey() );
703687 }
704 for (HousenumberRoad hnr : roadsInCluster){
688 for (HousenumberRoad hnr : roadsInCluster) {
705689 hnr.buildIntervals();
706690 }
707691 boolean optimized = false;
708 for (int loop = 0; loop < 10; loop++){
709 for (HousenumberRoad hnr : roadsInCluster){
692 for (int loop = 0; loop < 10; loop++) {
693 for (HousenumberRoad hnr : roadsInCluster) {
710694 hnr.checkIntervals();
711695 }
712696 checkWrongRoadAssignmments(roadsInCluster);
713697 boolean changed = hasChanges(roadsInCluster);
714 if (!optimized && !changed){
715 for (HousenumberRoad hnr : roadsInCluster){
698 if (!optimized && !changed) {
699 for (HousenumberRoad hnr : roadsInCluster) {
716700 hnr.improveSearchResults();
717701 }
718702 changed = hasChanges(roadsInCluster);
721705 if (!changed)
722706 break;
723707 }
724 for (HousenumberRoad hnr : roadsInCluster){
708 for (HousenumberRoad hnr : roadsInCluster) {
725709 hnr.setNumbers();
726710 }
727711 }
728712 }
729713 }
730714
731 if (log.isInfoEnabled()){
732 for (HousenumberElem house : houseElems){
733 if (house.getRoad() == null){
715 if (log.isInfoEnabled()) {
716 for (HousenumberElem house : houseElems) {
717 if (house.getRoad() == null) {
734718 if (house.getStreet() != null)
735719 log.info("found no plausible road for house number element",house.toBrowseURL(),house.getStreet(),house.getSign());
736720 else
739723 }
740724 }
741725 for (MapRoad r : allRoads) {
742 if (log.isDebugEnabled()){
726 if (log.isDebugEnabled()) {
743727 List<Numbers> finalNumbers = r.getRoadDef().getNumbersList();
744 if (finalNumbers != null){
728 if (finalNumbers != null) {
745729 log.info("id:"+r.getRoadDef().getId(),", final numbers,",r,"in",r.getCity());
746 for (Numbers cn : finalNumbers){
730 for (Numbers cn : finalNumbers) {
747731 if (cn.isEmpty())
748732 continue;
749733 log.info("id:"+r.getRoadDef().getId(),", Left: ",cn.getLeftNumberStyle(),cn.getIndex(),"Start:",cn.getLeftStart(),"End:",cn.getLeftEnd());
758742 private List<HousenumberRoad> createHousenumberRoads(
759743 MultiHashMap<MapRoad, HousenumberMatch> initialHousesForRoads) {
760744 List<HousenumberRoad> hnrList = new ArrayList<>();
761 for (MapRoad road : allRoads){
745 for (MapRoad road : allRoads) {
762746 if (road.isSkipHousenumberProcessing())
763747 continue;
764748 List<HousenumberMatch> houses = initialHousesForRoads.get(road);
783767
784768 long t3 = System.currentTimeMillis();
785769 MultiHashMap<MapRoad,HousenumberMatch> initialHousesForRoads = new MultiHashMap<>();
786 for (int i = 0; i < houseElems.size(); i++){
770 for (int i = 0; i < houseElems.size(); i++) {
787771 HousenumberElem house = houseElems.get(i);
788772 HousenumberMatch bestMatch = roadSegmentIndex.createHousenumberMatch(house);
789773 houseElems.set(i, bestMatch);
790 if (bestMatch.getRoad() == null){
774 if (bestMatch.getRoad() == null) {
791775 bestMatch.setIgnored(true); // XXX maybe create a pseudo road with zero length?
792776 // log.warn("found no plausible road for house number element",house.getElement().toBrowseURL());
793777 continue;
800784 return initialHousesForRoads;
801785 }
802786
803 private void useAddrPlaceTag(List<HousenumberRoad> hnrList){
787 private void useAddrPlaceTag() {
804788 HashMap<CityInfo,MultiHashMap<String,HousenumberMatch>> cityPlaceHouseMap = new LinkedHashMap<>();
805 for (int i = 0; i < houseElems.size(); i++){
789 for (int i = 0; i < houseElems.size(); i++) {
806790 HousenumberElem house = houseElems.get(i);
807 if (house.getRoad() == null)
791 if (house.getRoad() == null || house.getPlace() == null)
808792 continue;
809 if (house.getPlace() == null)
810 continue;
811 if (house instanceof HousenumberMatch){
793 if (house instanceof HousenumberMatch) {
812794 HousenumberMatch hm = (HousenumberMatch) house;
813 if (hm.getHousenumberRoad() == null)
814 continue;
815 } else
816 continue;
817 MultiHashMap<String, HousenumberMatch> subMap = cityPlaceHouseMap.get(house.getCityInfo());
818 if (subMap == null){
819 subMap = new MultiHashMap<>();
820 cityPlaceHouseMap.put(house.getCityInfo(), subMap);
821 }
822 subMap.add(house.getPlace(), (HousenumberMatch) house);
795 if (hm.getHousenumberRoad() != null) {
796 cityPlaceHouseMap.computeIfAbsent(house.getCityInfo(), k -> new MultiHashMap<>())
797 .add(house.getPlace(), (HousenumberMatch) house);
798 }
799 }
823800 }
824801 log.info("analysing",cityPlaceHouseMap.size(),"cities with addr:place=* houses" );
825 for (Entry<CityInfo, MultiHashMap<String, HousenumberMatch>> topEntry : cityPlaceHouseMap.entrySet()){
802 for (Entry<CityInfo, MultiHashMap<String, HousenumberMatch>> topEntry : cityPlaceHouseMap.entrySet()) {
826803 CityInfo cityInfo = topEntry.getKey();
827804 List<String> placeNames = new ArrayList<>(topEntry.getValue().keySet());
828805 placeNames.sort(null);
829 for (String placeName : placeNames){
806 for (String placeName : placeNames) {
830807 List<HousenumberMatch> placeHouses = topEntry.getValue().get(placeName);
831808 HashSet<HousenumberRoad> roads = new LinkedHashSet<>();
832809 Int2IntOpenHashMap usedNumbers = new Int2IntOpenHashMap();
838815 int roadsWithNames = 0;
839816 int unnamedCloseRoads = 0;
840817
841 for (HousenumberMatch house : placeHouses){
842 if (house.getStreet() != null ){
818 for (HousenumberMatch house : placeHouses) {
819 if (house.getStreet() != null ) {
843820 ++housesWithStreet;
844 if (house.getStreet().equalsIgnoreCase(house.getRoad().getStreet())){
821 if (house.getStreet().equalsIgnoreCase(house.getRoad().getStreet())) {
845822 ++housesWithMatchingStreet;
846823 }
847824 } else {
852829 if (added && house.getRoad().getStreet() != null)
853830 ++roadsWithNames;
854831 int oldCount = usedNumbers.put(house.getHousenumber(),1);
855 if (oldCount != 0){
832 if (oldCount != 0) {
856833 usedNumbers.put(house.getHousenumber(), oldCount + 1);
857834 ++dupNumbers;
858835 }
859836 Integer oldSignCount = usedSigns.put(house.getSign(), 1);
860 if (oldSignCount != null){
837 if (oldSignCount != null) {
861838 usedSigns.put(house.getSign(), oldSignCount + 1);
862839 ++dupSigns;
863840 }
864841 }
865842
866 if (log.isDebugEnabled()){
843 if (log.isDebugEnabled()) {
867844 log.debug("place",placeName,"in city",cityInfo, ":", "houses:", placeHouses.size(),
868845 ",duplicate numbers/signs:", dupNumbers+"/"+dupSigns,
869846 ",roads (named/unnamed):", roads.size(),"("+roadsWithNames+"/"+(roads.size()- roadsWithNames)+")",
871848 ",street = name of closest road:", housesWithMatchingStreet,
872849 ",houses without addr:street near named road:", unnamedCloseRoads);
873850 }
874 if ((float) dupSigns / placeHouses.size() < 0.25 ){
851 if ((float) dupSigns / placeHouses.size() < 0.25 ) {
875852 if (log.isDebugEnabled())
876853 log.debug("will not use gaps in intervals for roads in",placeName );
877 for (HousenumberRoad hnr : roads){
854 for (HousenumberRoad hnr : roads) {
878855 hnr.setRemoveGaps(true);
879856 }
880857 }
881 if (placeHouses.size() > housesWithStreet){ // XXX: threshold value?
858 if (placeHouses.size() > housesWithStreet) { // XXX: threshold value?
882859 LongArrayList ids = new LongArrayList();
883 for (HousenumberRoad hnr : roads){
860 for (HousenumberRoad hnr : roads) {
884861 ids.add(hnr.getRoad().getRoadDef().getId());
885862 hnr.addPlaceName(placeName);
886863 }
900877 * @param initialHousesForRoads map that is updated when wrong road assignments were found
901878 */
902879 private void handleInterpolationWays(MultiHashMap<MapRoad, HousenumberMatch> initialHousesForRoads) {
903 for (Entry<String, List<HousenumberIvl>> entry : interpolationWays.entrySet()){
880 for (Entry<String, List<HousenumberIvl>> entry : interpolationWays.entrySet()) {
904881 List<HousenumberIvl> infos = entry.getValue();
905 for (HousenumberIvl info : infos){
906 if (info.isBad()){
882 for (HousenumberIvl info : infos) {
883 if (info.isBad()) {
907884 continue;
908885 }
909886 boolean isOK = info.setNodeRefs(interpolationNodes, houseElems);
910887 if (!isOK)
911888 continue;
912889 HousenumberMatch[] houses = info.getHouseNodes();
913 MapRoad uncheckedRoads[] = new MapRoad[houses.length];
890 MapRoad[] uncheckedRoads = new MapRoad[houses.length];
914891 for (int i = 0 ; i < houses.length; i++)
915892 uncheckedRoads[i] = houses[i].getRoad();
916893 isOK = info.checkRoads();
917894 // check if houses are assigned to different roads now
918895 houses = info.getHouseNodes();
919 for (int i = 0 ; i < houses.length; i++){
920 if (houses[i].getRoad() != uncheckedRoads[i]){
896 for (int i = 0 ; i < houses.length; i++) {
897 if (houses[i].getRoad() != uncheckedRoads[i]) {
921898 initialHousesForRoads.removeMapping(uncheckedRoads[i], houses[i]);
922 if (houses[i].isIgnored() == false)
899 if (!houses[i].isIgnored())
923900 initialHousesForRoads.add(houses[i].getRoad(), houses[i]);
924901 else {
925902 if (!isOK)
931908 }
932909 }
933910
934 private void removeDupsGroupedByCityAndName(List<HousenumberRoad> hnrList){
911 private void removeDupsGroupedByCityAndName() {
935912 HashMap<CityInfo,MultiHashMap<String,HousenumberMatch>> cityNameHouseMap = new LinkedHashMap<>();
936 for (int i = 0; i < houseElems.size(); i++){
937 HousenumberElem house = houseElems.get(i);
913 for (HousenumberElem house : houseElems) {
938914 if (house.getRoad() == null)
939915 continue;
940 if (house instanceof HousenumberMatch){
916 if (house instanceof HousenumberMatch) {
941917 HousenumberMatch hm = (HousenumberMatch) house;
942918 if (hm.isIgnored())
943919 continue;
944920 HousenumberRoad hnr = hm.getHousenumberRoad();
945921 if (hnr == null || hnr.getName() == null)
946922 continue;
947 MultiHashMap<String, HousenumberMatch> subMap = cityNameHouseMap.get(hm.getCityInfo());
948 if (subMap == null){
949 subMap = new MultiHashMap<>();
950 cityNameHouseMap.put(hm.getCityInfo(), subMap);
951 }
952 subMap.add(hnr.getName(), hm);
953 }
954 }
955
956 for (Entry<CityInfo, MultiHashMap<String, HousenumberMatch>> topEntry : cityNameHouseMap.entrySet()){
957 for (Entry<String, List<HousenumberMatch>> entry : topEntry.getValue().entrySet()){
923 cityNameHouseMap.computeIfAbsent(hm.getCityInfo(), k -> new MultiHashMap<>()).add(hnr.getName(), hm);
924 }
925 }
926
927 for (Entry<CityInfo, MultiHashMap<String, HousenumberMatch>> topEntry : cityNameHouseMap.entrySet()) {
928 for (Entry<String, List<HousenumberMatch>> entry : topEntry.getValue().entrySet()) {
958929 markSimpleDuplicates(entry.getKey(), entry.getValue());
959930 }
960
961931 }
962932 }
963933
982952 long t1 = System.currentTimeMillis();
983953
984954 List<MapRoad> unnamedRoads = new ArrayList<>();
985 for (MapRoad road : allRoads){
955 for (MapRoad road : allRoads) {
986956 if (road.isSkipHousenumberProcessing())
987957 continue;
988958
989 if (road.getStreet() == null){
959 if (road.getStreet() == null) {
990960 // if a road has a label but getStreet() returns null,
991961 // the road probably has a ref. We assume these are not service roads.
992 if (road.getName() == null){
962 if (road.getName() == null) {
993963 unnamedRoads.add(road);
994964 List<Coord> nodes = new ArrayList<>();
995 for (Coord co : road.getPoints()){
965 for (Coord co : road.getPoints()) {
996966 if (co.getId() != 0)
997967 nodes.add(co);
998968 }
1008978 log.debug("identifyServiceRoad step 1 took",(t2-t1),"ms, found",roadNamesByNodeIds.size(),"nodes to check and",numUnnamedRoads,"unnamed roads" );
1009979 long t3 = System.currentTimeMillis();
1010980 int named = 0;
1011 for (int pass = 1; pass <= nameSearchDepth; pass ++){
981 for (int pass = 1; pass <= nameSearchDepth; pass ++) {
1012982 int unnamed = 0;
1013983 List<MapRoad> namedRoads = new ArrayList<>();
1014 for (int j = 0; j < unnamedRoads.size(); j++){
984 for (int j = 0; j < unnamedRoads.size(); j++) {
1015985 MapRoad road = unnamedRoads.get(j);
1016986 if (road == null)
1017987 continue;
1018988 unnamed++;
1019989 List<Coord> coordNodes = coordNodesUnnamedRoads.get(road);
1020990 String name = null;
1021 for (Coord co : coordNodes){
1022 if (unclearNodeIds.contains(co.getId())){
991 for (Coord co : coordNodes) {
992 if (unclearNodeIds.contains(co.getId())) {
1023993 name = null;
1024994 unnamedRoads.set(j, null); // don't process again
1025995 break;
1029999 continue;
10301000 if (name == null)
10311001 name = possibleName;
1032 else if (name.equals(possibleName) == false){
1002 else if (!name.equals(possibleName)) {
10331003 name = null;
10341004 unnamedRoads.set(j, null); // don't process again
10351005 break;
10361006 }
10371007 }
1038 if (name != null){
1008 if (name != null) {
10391009 named++;
10401010 road.setStreet(name);
10411011 namedRoads.add(road);
10421012 unnamedRoads.set(j, null); // don't process again
10431013 }
10441014 }
1045 for (MapRoad road : namedRoads){
1015 for (MapRoad road : namedRoads) {
10461016 road.setNamedByHousenumberProcessing(true);
10471017 String name = road.getStreet();
10481018 if (log.isDebugEnabled())
10571027 log.debug("pass",pass,unnamed,named);
10581028 }
10591029 long t4 = System.currentTimeMillis();
1060 if (log.isDebugEnabled()){
1030 if (log.isDebugEnabled()) {
10611031 log.debug("indentifyServiceRoad step 2 took",(t4-t3),"ms, found a name for",named,"of",numUnnamedRoads,"roads" );
10621032 }
1063 return;
10641033 }
10651034
10661035 private void identifyNodes(List<Coord> roadPoints,
10671036 String streetName, Int2ObjectOpenHashMap<String> roadNamesByNodeIds, HashSet<Integer> unclearNodes) {
1068 for (Coord co : roadPoints){
1069 if (co.getId() != 0){
1037 for (Coord co : roadPoints) {
1038 if (co.getId() != 0) {
10701039 String prevName = roadNamesByNodeIds.put(co.getId(), streetName);
1071 if (prevName != null){
1072 if (prevName.equals(streetName) == false)
1073 unclearNodes.add(co.getId());
1040 if (prevName != null && !prevName.equals(streetName)) {
1041 unclearNodes.add(co.getId());
10741042 }
10751043 }
10761044 }
10961064 if (interpolationInfos.isEmpty())
10971065 return;
10981066 List<HousenumberMatch> housesWithIvlInfo = new ArrayList<>();
1099 for (HousenumberRoad hnr : roadsInCluster){
1100 for (HousenumberMatch house : hnr.getHouses()){
1067 for (HousenumberRoad hnr : roadsInCluster) {
1068 for (HousenumberMatch house : hnr.getHouses()) {
11011069 if (house.getIntervalInfoRefs() > 0)
11021070 housesWithIvlInfo.add(house);
11031071 }
11121080 Int2ObjectOpenHashMap<HousenumberMatch> existingNumbers = new Int2ObjectOpenHashMap<>();
11131081 HashMap<HousenumberIvl, List<HousenumberMatch>> housesToAdd = new LinkedHashMap<>();
11141082
1115 for (HousenumberRoad hnr : roadsInCluster){
1083 for (HousenumberRoad hnr : roadsInCluster) {
11161084 for (HousenumberMatch house : hnr.getHouses())
11171085 existingNumbers.put(house.getHousenumber(), house);
11181086 }
11191087 int inCluster = 0;
11201088 boolean allOK = true;
11211089 // for loop may change the list
1122 for (int i = 0; i < interpolationInfos.size(); i++){
1090 for (int i = 0; i < interpolationInfos.size(); i++) {
11231091 HousenumberIvl hivl = interpolationInfos.get(i);
1124 if (hivl.inCluster(housesWithIvlInfo) == false || hivl.ignoreForInterpolation())
1092 if (!hivl.inCluster(housesWithIvlInfo) || hivl.ignoreForInterpolation())
11251093 continue;
11261094 ++inCluster;
11271095 String hivlDesc = hivl.getDesc();
11281096 HousenumberIvl hivlTest = simpleDupCheckSet.get(hivlDesc);
1129 if (hivlTest != null){
1097 if (hivlTest != null) {
11301098 // happens often in Canada (CanVec imports): two or more addr:interpolation ways with similar meaning
11311099 // sometimes at completely different road parts, sometimes at exactly the same
11321100 log.warn("found additional addr:interpolation way with same meaning, is ignored:",streetName, hivl, hivlTest);
11381106
11391107 id2IvlMap.put(hivl.getId(), hivl);
11401108 List<HousenumberMatch> interpolatedHouses = hivl.getInterpolatedHouses();
1141 if (interpolatedHouses.isEmpty() == false){
1142 if (interpolatedHouses.get(0).getRoad() == null){
1109 if (!interpolatedHouses.isEmpty()) {
1110 if (interpolatedHouses.get(0).getRoad() == null) {
11431111 // the interpolated houses are not all along one road
11441112 findRoadForInterpolatedHouses(streetName, interpolatedHouses, roadsInCluster);
11451113 }
11461114
11471115 int dupCount = 0;
1148 for (HousenumberMatch house : interpolatedHouses){
1116 for (HousenumberMatch house : interpolatedHouses) {
11491117 if (house.getRoad() == null || house.getDistance() > HousenumberIvl.MAX_INTERPOLATION_DISTANCE_TO_ROAD)
11501118 continue;
11511119 boolean ignoreGenOnly = false;
11521120 HousenumberMatch old = interpolatedNumbers.get(house.getHousenumber());
1153 if (old == null){
1121 if (old == null) {
11541122 ignoreGenOnly = true;
11551123 old = existingNumbers.get(house.getHousenumber());
11561124 }
1157 if (old != null){
1125 if (old != null) {
11581126 // try to build new intervals using existing node
11591127 HousenumberIvl[] splitIvls = hivl.trySplitAt(old);
1160 if (splitIvls != null){
1128 if (splitIvls != null) {
11611129 log.info("adding address",streetName,old,old.toBrowseURL(),"to addr:interpolation way, replacing", hivl,"by",Arrays.deepToString(splitIvls));
11621130 interpolationInfos.add(splitIvls[0]);
11631131 interpolationInfos.add(splitIvls[1]);
11671135 // forget both or only one ? Which one?
11681136 house.setIgnored(true);
11691137 double distToOld = old.getLocation().distance(house.getLocation());
1170 if (distToOld > MAX_DISTANCE_SAME_NUM){
1138 if (distToOld > MAX_DISTANCE_SAME_NUM) {
11711139 if (old.isInterpolated())
11721140 log.info("conflict caused by addr:interpolation way",streetName,hivl,"and interpolated address",old,"at",old.getLocation().toDegreeString());
11731141 else
11741142 log.info("conflict caused by addr:interpolation way",streetName,hivl,"and address element",old,"at",old.getLocation().toDegreeString());
11751143 dupCount++;
1176 if (!ignoreGenOnly){
1144 if (!ignoreGenOnly) {
11771145 old.setIgnored(true);
11781146 long ivlId = old.getElement().getOriginalId();
11791147 HousenumberIvl bad = id2IvlMap.get(ivlId);
11851153 }
11861154 if (hivl.ignoreForInterpolation())
11871155 continue;
1188 if (dupCount > 0){
1156 if (dupCount > 0) {
11891157 log.warn("addr:interpolation way",streetName,hivl,"is ignored, it produces",dupCount,"duplicate number(s) too far from existing nodes");
11901158 badIvls.add(hivl);
11911159 }
11981166 }
11991167 if (inCluster == 0)
12001168 return;
1201 for (HousenumberIvl badIvl: badIvls){
1169 for (HousenumberIvl badIvl: badIvls) {
12021170 allOK = false;
12031171 badIvl.ignoreNodes();
12041172 housesToAdd.remove(badIvl);
12051173 }
12061174 Iterator<Entry<HousenumberIvl, List<HousenumberMatch>>> iter = housesToAdd.entrySet().iterator();
1207 while (iter.hasNext()){
1175 while (iter.hasNext()) {
12081176 Entry<HousenumberIvl, List<HousenumberMatch>> entry = iter.next();
12091177 if (log.isInfoEnabled())
12101178 log.info("using generated house numbers from addr:interpolation way",entry.getKey());
1211 for (HousenumberMatch house : entry.getValue()){
1212 if (house.getRoad() != null && house.isIgnored() == false){
1179 for (HousenumberMatch house : entry.getValue()) {
1180 if (house.getRoad() != null && !house.isIgnored()) {
12131181 HousenumberRoad hnr = road2HousenumberRoadMap.get(house.getRoad());
1214 if (hnr == null){
1182 if (hnr == null) {
12151183 log.error("internal error: found no housenumber road for interpolated house",house.toBrowseURL());
12161184 continue;
12171185 }
12191187 }
12201188 }
12211189 }
1222 if (log.isDebugEnabled()){
1190 if (log.isDebugEnabled()) {
12231191 if (allOK)
12241192 log.debug("found no problems with interpolated numbers from addr:interpolations ways for roads with name",streetName);
12251193 else
12411209 house.setDistance(Double.POSITIVE_INFINITY); // make sure that we don't use an old match
12421210 house.setRoad(null);
12431211 List<HousenumberMatch> matches = new ArrayList<>();
1244 for (HousenumberRoad hnr : roadsInCluster){
1212 for (HousenumberRoad hnr : roadsInCluster) {
12451213 MapRoad r = hnr.getRoad();
12461214 // make sure that we use the street info if available
1247 if (house.getPlace() != null){
1248 if (house.getStreet() != null && r.getStreet() != null && house.getStreet().equals(r.getStreet()) == false)
1249 continue;
1215 if (house.getPlace() != null && house.getStreet() != null && r.getStreet() != null
1216 && house.getStreet().equals(r.getStreet())) {
1217 continue;
12501218 }
12511219 HousenumberMatch test = new HousenumberMatch(house);
12521220 findClosestRoadSegment(test, r);
1253 if (test.getRoad() != null && test.getGroup() != null || test.getDistance() < MAX_DISTANCE_TO_ROAD){
1221 if (test.getRoad() != null && test.getGroup() != null || test.getDistance() < MAX_DISTANCE_TO_ROAD) {
12541222 matches.add(test);
12551223 }
12561224 }
1257 if (matches.isEmpty()){
1225 if (matches.isEmpty()) {
12581226 house.setIgnored(true);
12591227 continue;
1260 }
1261
1262
1263 HousenumberMatch closest, best;
1264 best = closest = matches.get(0);
1265
1266 if (matches.size() > 1){
1228 }
1229
1230 HousenumberMatch closest = matches.get(0);
1231 HousenumberMatch best = closest;
1232
1233 if (matches.size() > 1) {
12671234 // multiple roads, we assume that the closest is the best
12681235 // but we may have to check the alternatives as well
12691236
12751242 house.setSegmentFrac(best.getSegmentFrac());
12761243 house.setRoad(best.getRoad());
12771244 house.setSegment(best.getSegment());
1278 for (HousenumberMatch altHouse : matches){
1245 for (HousenumberMatch altHouse : matches) {
12791246 if (altHouse.getRoad() != best.getRoad() && altHouse.getDistance() < MAX_DISTANCE_TO_ROAD)
12801247 house.addAlternativeRoad(altHouse.getRoad());
12811248 }
12861253 house.calcRoadSide();
12871254 }
12881255 // plausibility check for duplicate house numbers
1289 if (prev != null && prev.getHousenumber() == house.getHousenumber()){
1256 if (prev != null && prev.getHousenumber() == house.getHousenumber()
1257 && prev.getSign().equals(house.getSign())) {
12901258 // duplicate number (e.g. 10 and 10 or 10 and 10A or 10A and 10B)
1291 if (prev.getSign().equals(house.getSign())){
1292 prev.setDuplicate(true);
1293 house.setDuplicate(true);
1294 }
1295 }
1296
1297 if (house.getRoad() == null) {
1298 if (house.isIgnored() == false)
1299 log.warn("found no plausible road for house number element",house.toBrowseURL(),"(",streetName,house.getSign(),")");
1300 }
1301 if (!house.isIgnored())
1259 prev.setDuplicate(true);
1260 house.setDuplicate(true);
1261 }
1262
1263 if (house.getRoad() == null && !house.isIgnored()) {
1264 log.warn("found no plausible road for house number element",house.toBrowseURL(),"(",streetName,house.getSign(),")");
1265 }
1266 if (!house.isIgnored()) {
13021267 prev = house;
1303 }
1304 }
1305
1268 }
1269 }
1270 }
13061271
13071272 private static void markSimpleDuplicates(String streetName, List<HousenumberMatch> housesNearCluster) {
13081273 List<HousenumberMatch> sortedHouses = new ArrayList<>(housesNearCluster);
13091274 sortedHouses.sort(new HousenumberMatchByNumComparator());
13101275 int n = sortedHouses.size();
1311 for (int i = 1; i < n; i++){
1276 for (int i = 1; i < n; i++) {
13121277 HousenumberMatch house1 = sortedHouses.get(i-1);
13131278 if (house1.isIgnored())
13141279 continue;
13171282 continue;
13181283 if (house1.getHousenumber() != house2.getHousenumber())
13191284 continue;
1320 if (house1.getRoad() == house2.getRoad()){
1285 if (house1.getRoad() == house2.getRoad()) {
13211286 if (house1.isFarDuplicate())
13221287 house2.setFarDuplicate(true);
13231288 continue; // handled later
13311296 else {
13321297 CityInfo city1 = house1.getCityInfo();
13331298 CityInfo city2 = house2.getCityInfo();
1334 if (city1 != null && city1.equals(city2) == false){
1299 if (city1 != null && !city1.equals(city2)) {
13351300 markFarDup = true;
13361301 }
13371302 }
1338 if (markFarDup){
1303 if (markFarDup) {
13391304 if (log.isDebugEnabled())
13401305 log.debug("keeping duplicate numbers assigned to different roads in cluster ", streetName, house1,house2);
13411306 house1.setFarDuplicate(true);
13431308 continue;
13441309 }
13451310 boolean ignore2nd = false;
1346 if (dist < 30){
1311 if (dist < 30) {
13471312 ignore2nd = true;
13481313 } else {
13491314 Coord c1s = house1.getRoad().getPoints().get(house1.getSegment());
13501315 Coord c1e = house1.getRoad().getPoints().get(house1.getSegment() + 1);
13511316 Coord c2s = house2.getRoad().getPoints().get(house2.getSegment());
13521317 Coord c2e = house2.getRoad().getPoints().get(house2.getSegment() + 1);
1353 if (c1s == c2s || c1s == c2e || c1e == c2s || c1e == c2e){
1318 if (c1s == c2s || c1s == c2e || c1e == c2s || c1e == c2e) {
13541319 // roads are directly connected
13551320 ignore2nd = true;
13561321 }
13571322 }
1358 if (ignore2nd){
1323 if (ignore2nd) {
13591324 house2.setIgnored(true);
1360 if (log.isDebugEnabled()){
1325 if (log.isDebugEnabled()) {
13611326 if (house1.getSign().equals(house2.getSign()))
13621327 log.debug("duplicate number is ignored",streetName,house2.getSign(),house2.toBrowseURL() );
13631328 else
13881353 house.setDistance(Double.POSITIVE_INFINITY);
13891354 boolean foundGroupLink = false;
13901355 int end = Math.min(r.getPoints().size(), stopSeg+1);
1391 for (int node = firstSeg; node + 1 < end; node++){
1356 for (int node = firstSeg; node + 1 < end; node++) {
13921357 Coord c1 = r.getPoints().get(node);
13931358 Coord c2 = r.getPoints().get(node + 1);
13941359 double frac = getFrac(c1, c2, cx);
13951360 double dist = distanceToSegment(c1,c2,cx,frac);
1396 if (house.getGroup() != null && house.getGroup().linkNode == c1){
1397 if (c1.highPrecEquals(c2) == false){
1361 if (house.getGroup() != null && house.getGroup().linkNode == c1) {
1362 if (!c1.highPrecEquals(c2)) {
13981363 log.debug("block doesn't have zero length segment! Road:",r,house);
13991364 }
14001365 foundGroupLink = true;
14111376 }
14121377 }
14131378
1414 if (house.getGroup() != null && house.getGroup().linkNode != null && foundGroupLink == false){
1379 if (house.getGroup() != null && house.getGroup().linkNode != null && !foundGroupLink) {
14151380 log.debug(r,house,"has a group but the link was not found, should only happen after split of zero-length-segment");
14161381 }
1417 if (oldRoad == r){
1418 if (house.getDistance() > MAX_DISTANCE_TO_ROAD + 2.5 && oldDist <= MAX_DISTANCE_TO_ROAD ){
1419 log.warn("line distorted? Road segment was moved by more than",
1420 String.format("%.2f m", 2.5), ", from address", r, house.getSign());
1421 }
1422 }
1423 }
1424
1425 private static boolean hasChanges(
1426 List<HousenumberRoad> housenumberRoads) {
1427 for (HousenumberRoad hnr : housenumberRoads){
1428 if (hnr.isChanged())
1429 return true;
1430 }
1431 return false;
1382 if (oldRoad == r && house.getDistance() > MAX_DISTANCE_TO_ROAD + 2.5 && oldDist <= MAX_DISTANCE_TO_ROAD) {
1383 log.warn("line distorted? Road segment was moved by more than", String.format("%.2f m", 2.5),
1384 ", from address", r, house.getSign());
1385 }
1386 }
1387
1388 private static boolean hasChanges(List<HousenumberRoad> housenumberRoads) {
1389 return housenumberRoads.stream().anyMatch(HousenumberRoad::isChanged);
14321390 }
14331391
14341392 /**
14381396 private static void checkWrongRoadAssignmments(List<HousenumberRoad> housenumberRoads) {
14391397 if (housenumberRoads.size() < 2)
14401398 return;
1441 for (int loop = 0; loop < 10; loop++){
1399 for (int loop = 0; loop < 10; loop++) {
14421400 boolean changed = false;
1443 for (int i = 0; i+1 < housenumberRoads.size(); i++){
1401 for (int i = 0; i+1 < housenumberRoads.size(); i++) {
14441402 HousenumberRoad hnr1 = housenumberRoads.get(i);
14451403 hnr1.setChanged(false);
1446 for (int j = i+1; j < housenumberRoads.size(); j++){
1404 for (int j = i+1; j < housenumberRoads.size(); j++) {
14471405 HousenumberRoad hnr2 = housenumberRoads.get(j);
14481406 hnr2.setChanged(false);
14491407 hnr1.checkWrongRoadAssignmments(hnr2);
1450 if (hnr1.isChanged()){
1408 if (hnr1.isChanged()) {
14511409 changed = true;
14521410 hnr1.checkIntervals();
14531411 }
1454 if (hnr2.isChanged()){
1412 if (hnr2.isChanged()) {
14551413 changed = true;
14561414 hnr2.checkIntervals();
14571415 }
15611519 if (otherMatches.isEmpty())
15621520 return closestMatch;
15631521 HousenumberMatch bestMatch = closestMatch;
1564 for (HousenumberMatch alternative : otherMatches){
1522 for (HousenumberMatch alternative : otherMatches) {
15651523 if (alternative == closestMatch)
15661524 continue;
15671525 if (closestMatch.getDistance() < alternative.getDistance())
15731531 Coord cx = closestMatch.getLocation();
15741532 double dist = closestMatch.getDistance();
15751533 double dist1 = cx.distance(c1);
1576 double angle, altAngle;
1534 double angle;
15771535 if (dist1 == dist)
15781536 angle = Utils.getAngle(c2, c1, cx);
15791537 else
15821540 Coord c4 = alternative.getRoad().getPoints().get(alternative.getSegment()+1);
15831541
15841542 double dist3 = cx.distance(c3);
1543 double altAngle;
15851544 if (dist3 == dist)
15861545 altAngle = Utils.getAngle(c4, c3, cx);
15871546 else
15881547 altAngle = Utils.getAngle(c3, c4, cx);
15891548 double delta = 90 - Math.abs(angle);
15901549 double deltaAlt = 90 - Math.abs(altAngle);
1591 if (delta > deltaAlt){
1550 if (delta > deltaAlt) {
15921551 bestMatch = alternative;
1593 c1 = c3;
1594 c2 = c4;
1595 }
1596 }
1597 if (log.isDebugEnabled()){
1598 if (closestMatch.getRoad() != bestMatch.getRoad()){
1552 }
1553 }
1554 if (log.isDebugEnabled()) {
1555 if (closestMatch.getRoad() != bestMatch.getRoad()) {
15991556 log.debug("check angle: using road",bestMatch.getRoad().getRoadDef().getId(),"instead of",closestMatch.getRoad().getRoadDef().getId(),"for house number",bestMatch.getSign(),bestMatch.toBrowseURL());
1600 } else if (closestMatch != bestMatch){
1557 } else if (closestMatch != bestMatch) {
16011558 log.debug("check angle: using road segment",bestMatch.getSegment(),"instead of",closestMatch.getSegment(),"for house number element",bestMatch.toBrowseURL());
16021559 }
16031560 }
16121569 * @return {@code true} point lies on the left side; {@code false} point lies on the right side
16131570 */
16141571 public static boolean isLeft(Coord spoint1, Coord spoint2, Coord point) {
1615 if (spoint1.distance(spoint2) == 0){
1572 if (spoint1.distance(spoint2) == 0) {
16161573 log.warn("road segment length is 0 in left/right evaluation");
16171574 }
16181575
16781635 * @param length
16791636 * @return string with length, e.g. "0.23 m" or "116.12 m"
16801637 */
1681 public static String formatLen(double length){
1638 public static String formatLen(double length) {
16821639 return String.format("%.2f m", length);
16831640 }
16841641
17271684
17281685 }
17291686
1730 public void build(List<MapRoad> roads){
1731 for (MapRoad road : roads){
1687 public void build(List<MapRoad> roads) {
1688 for (MapRoad road : roads) {
17321689 if (road.isSkipHousenumberProcessing())
17331690 continue;
17341691 List<Coord> points = road.getPoints();
17371694
17381695 List<RoadPoint> roadPoints = new ArrayList<>();
17391696 RoadPoint rp;
1740 for (int i = 0; i + 1 < points.size(); i++){
1697 for (int i = 0; i + 1 < points.size(); i++) {
17411698 Coord c1 = points.get(i);
17421699 Coord c2 = points.get(i + 1);
17431700 int part = 0;
17441701 rp = new RoadPoint(road, c1, i, part++);
17451702 roadPoints.add(rp);
1746 while (true){
1703 while (true) {
17471704 double segLen = c1.distance(c2);
17481705 double frac = maxSegmentLength / segLen;
17491706 if (frac >= 1)
17521709 c1 = c1.makeBetweenPoint(c2, frac);
17531710 rp = new RoadPoint(road, c1, i, part++);
17541711 roadPoints.add(rp);
1755 segLen -= maxSegmentLength;
17561712 }
17571713 }
17581714 int last = points.size() - 1;
17601716 roadPoints.add(rp);
17611717
17621718 Collections.shuffle(roadPoints);
1763 for (RoadPoint toAdd : roadPoints){
1719 for (RoadPoint toAdd : roadPoints) {
17641720 int id = toAdd.p.getId();
17651721 if (id == 0)
17661722 kdTree.add(toAdd);
17671723 else {
17681724 // Coord node, add only once to KD-tree with all roads
17691725 Set<RoadPoint> set = nodeId2RoadPointMap.get(id);
1770 if (set == null){
1726 if (set == null) {
17711727 set = new LinkedHashSet<>();
17721728 nodeId2RoadPointMap.put(id, set);
17731729 kdTree.add(toAdd);
17781734 }
17791735 }
17801736
1781 public List<RoadPoint> getCLoseRoadPoints(HousenumberElem house){
1737 public List<RoadPoint> getCLoseRoadPoints(HousenumberElem house) {
17821738 Set<RoadPoint> closeRoadPoints = kdTree.findClosePoints(house, kdSearchRange);
17831739 List<RoadPoint> result = new ArrayList<>();
1784 for (RoadPoint rp : closeRoadPoints){
1740 for (RoadPoint rp : closeRoadPoints) {
17851741 int id = rp.p.getId();
17861742 if (id != 0)
17871743 result.addAll(nodeId2RoadPointMap.get(id));
17961752 * @param house
17971753 * @return null if no road was found, else a {@link HousenumberMatch} instance
17981754 */
1799 public HousenumberMatch createHousenumberMatch(HousenumberElem house){
1755 public HousenumberMatch createHousenumberMatch(HousenumberElem house) {
18001756 HousenumberMatch closest = new HousenumberMatch(house);
18011757 List<RoadPoint> closeRoadPoints = getCLoseRoadPoints(house);
18021758 if (closeRoadPoints.isEmpty())
18181774 BitSet testedSegments = new BitSet();
18191775 MapRoad lastRoad = null;
18201776 HousenumberMatch hnm = null;
1821 for (RoadPoint rp : closeRoadPoints){
1822 if (house.getStreet() != null){
1777 for (RoadPoint rp : closeRoadPoints) {
1778 if (house.getStreet() != null && rp.r.getStreet() != null
1779 && !house.getStreet().equalsIgnoreCase(rp.r.getStreet())) {
18231780 // we have a given street name, accept only roads with similar name or no name
1824 if (rp.r.getStreet() != null && house.getStreet().equalsIgnoreCase(rp.r.getStreet()) == false)
1825 continue;
1826 }
1827 if (rp.r != lastRoad){
1781 continue;
1782 }
1783 if (rp.r != lastRoad) {
18281784 hnm = new HousenumberMatch(house);
18291785 testedSegments.clear();
18301786 matches.add(hnm);
18311787 lastRoad = rp.r;
18321788 }
1833 double oldDist = hnm.getDistance();
1834 if (rp.partOfSeg >= 0){
1835 // rp.p is at start or before end of segment
1836 if (testedSegments.get(rp.segment) == false){
1837 testedSegments.set(rp.segment);
1838 checkSegment(hnm, rp.r, rp.segment);
1839 }
1840 }
1841 if (rp.partOfSeg < 0){
1842 // rp is at end of road, check (also) the preceding segment
1843 if (rp.segment < 1){
1844 log.error("internal error: trying to use invalid roadPoint",rp);
1845 } else if (testedSegments.get(rp.segment - 1) == false){
1846 testedSegments.set(rp.segment-1);
1847 checkSegment(hnm, rp.r, rp.segment-1);
1848 }
1849 }
1850 if (oldDist == hnm.getDistance())
1851 continue;
1789 if (rp.partOfSeg >= 0 && !testedSegments.get(rp.segment)) {
1790 // rp.p is at start or before end of segment
1791 testedSegments.set(rp.segment);
1792 checkSegment(hnm, rp.r, rp.segment);
1793 }
1794 if (rp.partOfSeg < 0) {
1795 // rp is at end of road, check (also) the preceding segment
1796 if (rp.segment < 1) {
1797 log.error("internal error: trying to use invalid roadPoint", rp);
1798 } else if (!testedSegments.get(rp.segment - 1)) {
1799 testedSegments.set(rp.segment - 1);
1800 checkSegment(hnm, rp.r, rp.segment - 1);
1801 }
1802 }
18521803 }
18531804 if (matches.isEmpty())
18541805 return closest; // closest has not yet a road
18611812 if (closest.getStreet() != null && closest.getStreet().equalsIgnoreCase(closest.getRoad().getStreet()))
18621813 bestMatchingName = closest;
18631814
1864 for (HousenumberMatch altHouse : matches){
1815 for (HousenumberMatch altHouse : matches) {
18651816 if (altHouse.getDistance() >= MAX_DISTANCE_TO_ROAD)
18661817 break;
1867 if (altHouse.getRoad() != closest.getRoad()){
1868 if (house.getStreet() != null && altHouse.getDistance() > closest.getDistance()){
1869 if (house.getStreet().equalsIgnoreCase(altHouse.getRoad().getStreet())){
1870 if (bestMatchingName == null || bestMatchingName.getDistance() > altHouse.getDistance()){
1818 if (altHouse.getRoad() != closest.getRoad()) {
1819 if (house.getStreet() != null && altHouse.getDistance() > closest.getDistance()) {
1820 if (house.getStreet().equalsIgnoreCase(altHouse.getRoad().getStreet())) {
1821 if (bestMatchingName == null || bestMatchingName.getDistance() > altHouse.getDistance()) {
18711822 bestMatchingName = altHouse;
18721823 }
18731824 } else {
18781829 closest.addAlternativeRoad(altHouse.getRoad());
18791830 }
18801831 }
1881 if (bestMatchingName != null){
1882 if (house.getStreet().equals(bestMatchingName.getRoad().getStreet()) == false){
1883 log.warn("accepting match in spite of different capitalisation" , house.getStreet(),house.getSign(), bestMatchingName.getRoad().getRoadDef(), "house:",house.toBrowseURL());
1884 bestMatchingName.setStreet(bestMatchingName.getRoad().getStreet());
1885 closest.setStreet(bestMatchingName.getStreet());
1886 }
1832 if (bestMatchingName != null && !house.getStreet().equals(bestMatchingName.getRoad().getStreet())) {
1833 log.warn("accepting match in spite of different capitalisation", house.getStreet(), house.getSign(),
1834 bestMatchingName.getRoad().getRoadDef(), "house:", house.toBrowseURL());
1835 bestMatchingName.setStreet(bestMatchingName.getRoad().getStreet());
1836 closest.setStreet(bestMatchingName.getStreet());
18871837 }
18881838 if (closest == bestMatchingName || bestMatchingName == null || bestMatchingName.getDistance() > MAX_DISTANCE_TO_ROAD)
18891839 return closest;
18921842 if (ratio < 0.25)
18931843 return closest;
18941844 HousenumberMatch best = closest;
1895 if (ratio > 0.75){
1845 if (ratio > 0.75) {
18961846 // prefer the road with the matching name
1897 for (MapRoad r : closest.getAlternativeRoads()){
1847 for (MapRoad r : closest.getAlternativeRoads()) {
18981848 if (house.getStreet().equalsIgnoreCase(r.getStreet()))
18991849 bestMatchingName.addAlternativeRoad(r);
19001850 }
19011851 best = bestMatchingName;
19021852 best.calcRoadSide();
19031853 } else {
1904 if (log.isDebugEnabled()){
1854 if (log.isDebugEnabled()) {
19051855 log.debug("further checks needed for address", closest.getStreet(), closest.getSign(), closest.toBrowseURL(),
19061856 formatLen(closest.getDistance()), formatLen(bestMatchingName.getDistance()));
19071857 }
19101860 return best;
19111861 }
19121862
1913 }
1914
1915 private static void checkSegment(HousenumberMatch house, MapRoad road, int seg){
1916 Coord cx = house.getLocation();
1917 Coord c0 = road.getPoints().get(seg);
1918 Coord c1 = road.getPoints().get(seg + 1);
1919 double frac = getFrac(c0, c1, cx);
1920 double dist = distanceToSegment(c0,c1,cx,frac);
1921 if (dist < house.getDistance()){
1922 house.setDistance(dist);
1923 house.setRoad(road);
1924 house.setSegment(seg);
1925 house.setSegmentFrac(frac);
1926 }
1927 }
1928
1863 private static void checkSegment(HousenumberMatch house, MapRoad road, int seg) {
1864 Coord cx = house.getLocation();
1865 Coord c0 = road.getPoints().get(seg);
1866 Coord c1 = road.getPoints().get(seg + 1);
1867 double frac = getFrac(c0, c1, cx);
1868 double dist = distanceToSegment(c0,c1,cx,frac);
1869 if (dist < house.getDistance()) {
1870 house.setDistance(dist);
1871 house.setRoad(road);
1872 house.setSegment(seg);
1873 house.setSegmentFrac(frac);
1874 }
1875 }
1876 }
19291877 }
19301878
19311879
317317 return true;
318318 }
319319
320 if (housesFormAGroup(house, last) == false){
320 if (!housesFormAGroup(house, last)) {
321321 return false;
322322 }
323323 if (houses.size() > 1){
324324 HousenumberMatch first = houses.get(0);
325 if (housesFormAGroup(house, first) == false){
325 if (!housesFormAGroup(house, first)) {
326326 HousenumberMatch preLast = houses.get(houses.size()-2);
327327 double angle = Utils.getAngle(house.getLocation(), last.getLocation(), preLast.getLocation());
328328 if (Math.abs(angle) > 30)
6565 private boolean ignoreForInterpolation;
6666
6767 private boolean equalEnds;
68 private static final short streetTagKey = TagDict.getInstance().xlate("mkgmap:street");
69 private static final short housenumberTagKey = TagDict.getInstance().xlate("mkgmap:housenumber");
70 private static final short addrInterpolationTagKey = TagDict.getInstance().xlate("addr:interpolation");
68 private static final short TKM_STREET = TagDict.getInstance().xlate("mkgmap:street");
69 private static final short TKM_HOUSENUMBER = TagDict.getInstance().xlate("mkgmap:housenumber");
70 private static final short TK_ADDR_INTERPOLATION = TagDict.getInstance().xlate("addr:interpolation");
7171
7272
7373 public HousenumberIvl(String streetName, Way interpolationWay, Node n1, Node n2) {
7878 }
7979
8080 public void setPoints(List<Coord> points) {
81 this.points = new ArrayList<Coord>(points);
81 this.points = new ArrayList<>(points);
8282 }
8383 public void setStep(int step) {
8484 this.step = step;
167167 MapRoad bestRoad = null;
168168 // make sure that the closest road is one with a matching name
169169 for (int i = 0; i < 2; i++){
170 while (streetName.equals(knownHouses[i].getRoad().getStreet()) == false && knownHouses[i].hasAlternativeRoad()){
170 while (!streetName.equals(knownHouses[i].getRoad().getStreet()) && knownHouses[i].hasAlternativeRoad()) {
171171 HousenumberMatch testx = new HousenumberMatch(knownHouses[i]);
172172 MapRoad r = knownHouses[i].getAlternativeRoads().remove(0);
173173 if (streetName.equals(r.getStreet())){
189189 HousenumberMatch[] closest = new HousenumberMatch[2];
190190 boolean foundSingleRoad = false;
191191 for (MapRoad r : toTest){
192 if (streetName.equals(r.getStreet()) == false)
192 if (!streetName.equals(r.getStreet()))
193193 continue;
194194 foundSingleRoad = true;
195195 for (int i = 0; i < 2; i++){
245245 }
246246 }
247247 }
248 if (foundSingleRoad){
249 if (r.isNamedByHousenumberProcessing() == false)
248 if (foundSingleRoad) {
249 if (!r.isNamedByHousenumberProcessing())
250250 break;
251251 // the closest road was originally unnamed , try to find one that is named in OSM
252 if (bestRoad == null){
252 if (bestRoad == null) {
253253 bestRoad = r;
254254 closest[0] = test[0];
255255 closest[1] = test[1];
263263 test[1] = closest[1];
264264 }
265265 if (!foundSingleRoad){
266 if (streetName.equals(knownHouses[0].getRoad().getStreet()) == false || streetName.equals(knownHouses[1].getRoad().getStreet()) == false){
266 if (!streetName.equals(knownHouses[0].getRoad().getStreet()) || !streetName.equals(knownHouses[1].getRoad().getStreet())) {
267267 log.warn("cannot find reasonable road for both nodes",streetName,this);
268268 return false;
269269 }
314314 boolean distanceWarningIssued = false;
315315 CityInfo ci = knownHouses[0].getCityInfo();
316316 ZipCodeInfo zip = knownHouses[0].getZipCode();
317 if (ci != null && ci.equals(knownHouses[1].getCityInfo()) == false)
317 if (ci != null && !ci.equals(knownHouses[1].getCityInfo()))
318318 log.warn("addr:interpolation way connects houses in different cities",streetName,this,"using city",ci,"for all interpolated adresses");
319 if (zip != null && zip.equals(knownHouses[1].getZipCode()) == false)
319 if (zip != null && !zip.equals(knownHouses[1].getZipCode()))
320320 log.warn("addr:interpolation way connects houses with differnt zip codes",streetName,this,"using zip code",zip,"for all interpolated adresses");
321321
322 for (Coord co : interpolatedPoints){
322 for (Coord co : interpolatedPoints) {
323323 hn += usedStep;
324324 Node generated = new Node(interpolationWay.getId(), co);
325 generated.setFakeId();
326 generated.addTag(streetTagKey, streetName);
325 generated.markAsGeneratedFrom(interpolationWay);
326 generated.addTag(TKM_STREET, streetName);
327327 String number = String.valueOf(hn);
328 generated.addTag(housenumberTagKey, number);
328 generated.addTag(TKM_HOUSENUMBER, number);
329329 // TODO: maybe add check that city info and zip code of both houses is equal ?
330330 // what if not ?
331331 HousenumberElem houseElem = new HousenumberElem(generated, ci);
337337 if (roadForInterpolatedHouses != null){
338338 HousenumberGenerator.findClosestRoadSegment(house, roadForInterpolatedHouses);
339339 if (house.getRoad() == null || house.getDistance() > MAX_INTERPOLATION_DISTANCE_TO_ROAD ){
340 if (distanceWarningIssued == false){
340 if (!distanceWarningIssued){
341341 log.warn("interpolated house is not close to expected road",this,house);
342342 distanceWarningIssued = true;
343343 }
350350 }
351351
352352 if (log.isDebugEnabled()){
353 String addrInterpolationMethod = interpolationWay.getTag(addrInterpolationTagKey);
354 if (hasMultipleRoads == false)
353 String addrInterpolationMethod = interpolationWay.getTag(TK_ADDR_INTERPOLATION);
354 if (!hasMultipleRoads)
355355 log.debug(this,"generated",addrInterpolationMethod,"interpolated number(s) for",knownHouses[0].getRoad());
356356 else
357357 log.debug(this,"generated",addrInterpolationMethod,"interpolated number(s) for",streetName);
495495 if (elemPos == null || elemPos >= houseElems.size())
496496 return false;
497497 HousenumberElem he = houseElems.get(elemPos);
498 if (he instanceof HousenumberMatch == false)
499 return false;
500 if (he.getElement().getId() != id)
498 if (!(he instanceof HousenumberMatch) || he.getElement().getId() != id)
501499 return false;
502500 knownHouses[i] = (HousenumberMatch) he;
503501 knownHouses[i].incIntervalInfoRefs();
546544 else {
547545 // create a Node instance
548546 Node toAdd = new Node(houseToAdd.getElement().getId(), houseToAdd.getLocation());
549 toAdd.setFakeId();
547 toAdd.markAsGeneratedFrom(houseToAdd.getElement());
550548 toAdd.copyTags(houseToAdd.element);
551549 HousenumberElem hnElem = new HousenumberElem(toAdd, houseToAdd.getCityInfo());
552550 hnm = new HousenumberMatch(hnElem);
120120 }
121121
122122 public boolean hasAlternativeRoad() {
123 return alternativeRoads != null && alternativeRoads.isEmpty() == false;
123 return alternativeRoads != null && !alternativeRoads.isEmpty();
124124 }
125125
126126 public boolean isIgnored() {
309309 public boolean isEqualAddress(HousenumberElem other){
310310 if (getRoad() != other.getRoad())
311311 return false;
312 if (getSign().equals(other.getSign()) == false)
312 if (!getSign().equals(other.getSign()))
313313 return false;
314314 if (getPlace() != other.getPlace()) {
315315 if (getPlace() == null)
316316 return false;
317 if (getPlace().equals(other.getPlace()) == false)
317 if (!getPlace().equals(other.getPlace()))
318318 return false;
319319 }
320320 if (getZipCode() != null && other.getZipCode() != null){
321 if (getZipCode().equals(other.getZipCode()) == false)
321 if (!getZipCode().equals(other.getZipCode()))
322322 return false;
323323 }
324324 if (getCityInfo() != null && other.getCityInfo() != null){
325 if (getCityInfo().equals(other.getCityInfo()) == false)
325 if (!getCityInfo().equals(other.getCityInfo()))
326326 return false;
327327 }
328328 return true;
8383 filterGroups();
8484 if (houseNumbers.isEmpty())
8585 return;
86 List<HousenumberMatch> leftNumbers = new ArrayList<HousenumberMatch>();
87 List<HousenumberMatch> rightNumbers = new ArrayList<HousenumberMatch>();
86 List<HousenumberMatch> leftNumbers = new ArrayList<>();
87 List<HousenumberMatch> rightNumbers = new ArrayList<>();
8888
8989 for (HousenumberMatch house : houseNumbers) {
9090 if (house.getRoad() == null || house.isIgnored()){
113113 assert road.getPoints().get(0).isNumberNode();
114114 for (Coord p : road.getPoints()) {
115115 // An ordinary point in the road.
116 if (p.isNumberNode() == false) {
116 if (!p.isNumberNode()) {
117117 currNodePos++;
118118 continue;
119119 }
176176 if (HousenumberGroup.housesFormAGroup(predHouse, house))
177177 group = new HousenumberGroup(this, houses.subList(j-1, j+1));
178178 } else {
179 if (group.tryAddHouse(house) == false){
179 if (!group.tryAddHouse(house)) {
180180 if(group.verify())
181181 groups.add(group);
182182 group = null;
193193 for (HousenumberGroup group : groups){
194194 int oldNumPoints = getRoad().getPoints().size();
195195 if (nodesAdded){
196 if (group.recalcPositions() == false)
196 if (!group.recalcPositions())
197197 continue;
198198 }
199199 if (group.findSegment(streetName, groups)){
269269 for (int i = 1; i < houseNumbers.size(); i++){
270270 HousenumberMatch house1 = houseNumbers.get(i - 1);
271271 HousenumberMatch house2 = houseNumbers.get(i);
272 if (house1.getSign().equals(house2.getSign()) == false){
272 if (!house1.getSign().equals(house2.getSign())) {
273273 usedForCalc = null;
274274 } else {
275 if (house1.isEqualAddress(house2) == false)
275 if (!house1.isEqualAddress(house2))
276276 continue;
277277 // found a duplicate address (e.g. 2 and 2 or 1b and 1b in same road,city etc.)
278278 double distBetweenHouses = house2.getLocation().distance(house1.getLocation());
439439 else {
440440 if (used == null)
441441 used = prev;
442 if (prev.getSign().equals(house.getSign()) && prev.isEqualAddress(house) == false){
442 if (prev.getSign().equals(house.getSign()) && !prev.isEqualAddress(house)) {
443443 // we want to keep these duplicates
444444 } else {
445445 house.setIgnored(true);
461461 for (ExtNumbers en1 = head1; en1 != null; en1 = en1.next){
462462 if (changed)
463463 break;
464 if (en1.hasNumbers() == false)
464 if (!en1.hasNumbers())
465465 continue;
466466 ExtNumbers head2 = other.extNumbersHead;
467467 for (ExtNumbers en2 = head2; en2 != null; en2 = en2.next){
468468 if (changed)
469469 break;
470 if (en2.hasNumbers() == false)
470 if (!en2.hasNumbers())
471471 continue;
472472 int res = ExtNumbers.checkIntervals(streetName, en1, en2);
473473 switch (res) {
603603 }
604604
605605 public void setRandom(boolean isRandom) {
606 if (this.isRandom == false)
606 if (!this.isRandom)
607607 if (log.isDebugEnabled())
608608 log.debug("detected random case",this);
609609 this.isRandom = isRandom;
628628 setChanged(true);
629629 else {
630630 ExtNumbers test = en.hasNumbers() ? en : en.next;
631 if (test.getNumbers().isSimilar(curr.getNumbers()) == false)
631 if (!test.getNumbers().isSimilar(curr.getNumbers()))
632632 setChanged(true);
633633 }
634634 if (curr.prev == null)
660660 List<HousenumberMatch> wrongHouses = Collections.emptyList();
661661 double minDist = Double.MAX_VALUE;
662662 double maxDist = 0;
663 if (houseNumbers.isEmpty() == false){
663 if (!houseNumbers.isEmpty()) {
664664 HashMap<String, Integer>possibleStreetNamesFromHouses = new HashMap<>();
665665 HashMap<String, Integer>possiblePlaceNamesFromHouses = new HashMap<>();
666666 for (HousenumberMatch house : houseNumbers){
5959
6060 /**
6161 * Class to extract elevation information from SRTM files in hgt format.
62 * @param path a comma separated list of directories which may contain *.hgt files.
62 * @param paths a comma separated list of directories which may contain *.hgt files.
6363 * @param bbox the bounding box of the tile for which the DEM information is needed.
6464 * @param demPolygonMapUnits optional bounding polygon which describes the area for
6565 * which elevation should be read from hgt files.
6666 * @param interpolationMethod
6767 */
68 public HGTConverter(String path, Area bbox, java.awt.geom.Area demPolygonMapUnits, double extra) {
68 public HGTConverter(String paths, Area bbox, java.awt.geom.Area demPolygonMapUnits, double extra) {
6969 // make bigger box for interpolation or aligning of areas
7070 int minLat = (int) Math.floor(Utils.toDegrees(bbox.getMinLat()) - extra);
7171 int minLon = (int) Math.floor(Utils.toDegrees(bbox.getMinLong()) - extra);
9292 Area rdrBbox = new Area(lat, lon, lat+1.0, lon+1.0);
9393 int testMode = intersectsPoly(rdrBbox);
9494 if (testMode != 0) {
95 HGTReader rdr = new HGTReader(lat, lon, path);
95 HGTReader rdr = new HGTReader(lat, lon, paths);
9696 readers[row][col] = rdr;
9797 maxRes = Math.max(maxRes, rdr.getRes());
9898 }
5656 * Class to read a single HGT file.
5757 * @param lat in degrees, -90 .. 90
5858 * @param lon - -180..180
59 * @param dirsWithHGT comma separated list of directories to search for *.hgt files
59 * @param dirsWithHGT string with comma separated list of directories to search for *.hgt files
6060 * Supported are also zip files containing *.hgt files and directories containing *.hgt.zip.
6161 */
6262 public HGTReader(int lat, int lon, String dirsWithHGT) {
122122 for (CoastlineWay w : coastlines) {
123123 if (w.getBbox().intersects(bbox)) {
124124 Way x = new Way(w.getOriginalId(), w.getPoints());
125 x.setFakeId();
125 x.markAsGeneratedFrom(w);
126126 x.addTag("natural", "coastline");
127127 ways.add(x);
128128 }
172172 }
173173
174174 @Override
175 protected void removeAllTags() {
175 public void removeAllTags() {
176176 }
177177
178178 @Override
2727 public abstract class Element {
2828 private static final Logger log = Logger.getLogger(Element.class);
2929
30 private static final byte TYPE_NODE = 1;
31 private static final byte TYPE_WAY = 2;
32 private static final byte TYPE_RELATION = 3;
33
3034 private Tags tags;
3135 private long id;
3236 private long originalId;
37 private byte origType;
3338
3439 /**
3540 * returns a copy of the tags or a new instance if the element has no tags
214219 originalId = id;
215220 }
216221
217 public void setFakeId() {
222 /**
223 * Mark this element as generated from another element.
224 * @param orig the original element (used to extract the type)
225 */
226 public void markAsGeneratedFrom(Element orig) {
218227 id = FakeIdGenerator.makeFakeId();
219 }
220
228 origType = elementToType(orig);
229 }
230
231 private static byte elementToType(Element orig) {
232 if (orig instanceof Node)
233 return TYPE_NODE;
234 if (orig instanceof Way)
235 return TYPE_WAY;
236 if (orig instanceof Relation)
237 return TYPE_RELATION;
238 throw new IllegalArgumentException("invalid type");
239 }
240
241 public String getOrigElement() {
242 switch (origType) {
243 case TYPE_NODE:
244 return "node";
245 case TYPE_WAY:
246 return "way";
247 case TYPE_RELATION:
248 return "relation";
249 default:
250 throw new IllegalArgumentException("invalid type");
251 }
252 }
253
221254 public String toTagString() {
222255 if (tags == null)
223256 return "[]";
241274 protected void copyIds(Element other) {
242275 id = other.id;
243276 originalId = other.originalId;
277 origType = other.origType;
244278 }
245279
246280 public String getName() {
254288 return tags.getTagsWithPrefix(prefix, removePrefix);
255289 }
256290
257 protected void removeAllTags() {
291 public void removeAllTags() {
258292 tags = null;
259293 }
260294
299333 name += " ";
300334 return name + "(OSM id " + getId() + ")";
301335 }
336
337 /**
338 * Calculate a short string to be used in log messages or style functions
339 * echo or echotags.
340 *
341 * @return string containing the element type (Node/Way/Relation followed by
342 * either the id or - if the id is a fake id - the string "generated
343 * from " followed by the id of the source element that was used to
344 * generate this element.
345 */
346 public String getBasicLogInformation() {
347 String className = getClass().getSimpleName();
348 return ("GeneralRelation".equals(className)) ? "Relation" : className
349 + (FakeIdGenerator.isFakeId(getId()) ? " generated from " + getOrigElement(): "") + " " + originalId;
350 }
302351 }
6868 private final String[] deadEndArgs;
6969
7070 /** name of the tag that contains a ;-separated list of tag names that should be removed after all elements have been processed */
71 public static final short MKGMAP_REMOVE_TAG_KEY = TagDict.getInstance().xlate("mkgmap:removetags");
71 public static final short TKM_REMOVETAGS = TagDict.getInstance().xlate("mkgmap:removetags");
7272
7373 public ElementSaver(EnhancedProperties args) {
7474 if (args.getProperty("preserve-element-order", false)) {
362362 public void deferRelation(long id, Relation parentRel, String role) {
363363 deferredRelationMap.add(id, new AbstractMap.SimpleEntry<>(role, parentRel));
364364 }
365
366 /**
367 * Return the node object associated with the given id or create one.
368 * This is called for the node members of a relation. We always want a node for them, not just a coord.
369 * @param id the node id
370 * @return the existing node or a newly created one (without tags) or null if no coord is associated with the id
371 */
372 public Node getOrCreateNode(long id) {
373 Node node = nodeMap.get(id);
374 if (node == null) {
375 // we didn't make a node for this point earlier,
376 // do it now (if it exists)
377 Coord co = getCoord(id);
378 if (co != null) {
379 node = new Node(id, co);
380 addNode(node);
381 }
382 }
383 return node;
384 }
365385 }
5656 // actions will always be executed
5757 private boolean propogateActionsOnContinue;
5858
59 @SuppressWarnings("incomplete-switch")
5960 public static boolean checkType(FeatureKind featureKind, int type) {
6061 if (type >= 0x010000) {
6162 if ((type & 0xff) > 0x1f)
6263 return false;
6364 } else {
64 if (featureKind == FeatureKind.POLYLINE && type > 0x3f
65 || (featureKind == FeatureKind.POLYGON && (type > 0x7f || type == 0x4a))) {
66 return false;
67 } else if (featureKind == FeatureKind.POINT) {
65 switch (featureKind) {
66 case POLYLINE:
67 if (type > 0x3f)
68 return false;
69 break;
70 case POLYGON:
71 if (type > 0x7f || type == 0x4a)
72 return false;
73 break;
74 case POINT:
6875 if (type < 0x0100)
6976 return false;
7077 int subtype = type & 0xff;
71 if (subtype > 0x1f || MapPoint.isCityType(type) && subtype != 0) {
72 return false;
78 if (MapPoint.isCityType(type)) {
79 if (subtype != 0)
80 return false;
81 } else if (type >= 0x1600 && type < 0x1e00 || // Andrzej Popowski says.
82 type >= 0x2a00 && type < 0x3100 || // These may be indexed and this
83 type >= 0x6400 && type < 0x6700) { // confines subtype to 5 bits
84 if (subtype > 0x1f)
85 return false;
86 } else {
87 if (subtype > 0x3f)
88 return false;
7389 }
90 break;
7491 }
7592 }
7693 return true;
242259 * known to cause routing errors if used for non-routable lines.
243260 */
244261 public static boolean isSpecialRoutableLineType(int type){
245 return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1b;
262 return type >= 0x01 && type <= 0x13 || type == 0x16 || type == 0x1a || type == 0x1b;
246263 }
247264
248265 /**
1414
1515 import java.util.ArrayList;
1616 import java.util.Arrays;
17 import java.util.Collections;
1718 import java.util.HashSet;
1819 import java.util.List;
1920 import java.util.Set;
2021
22 import uk.me.parabola.imgfmt.app.Area;
2123 import uk.me.parabola.imgfmt.app.Coord;
2224 import uk.me.parabola.imgfmt.app.Exit;
2325 import uk.me.parabola.log.Logger;
26 import uk.me.parabola.util.ElementQuadTree;
2427 import uk.me.parabola.util.EnhancedProperties;
2528
2629 /**
3235 public class HighwayHooks implements OsmReadingHooks {
3336 private static final Logger log = Logger.getLogger(HighwayHooks.class);
3437
35 private final List<Way> motorways = new ArrayList<>();
36 private final List<Node> exits = new ArrayList<>();
38 private final List<Element> motorways = new ArrayList<>(); // will all be Ways
39 private final List<Element> exits = new ArrayList<>();
3740
3841 private boolean makeOppositeCycleways;
3942 private ElementSaver saver;
4245 private Node currentNodeInWay;
4346
4447 @Override
45 public boolean init(ElementSaver saver, EnhancedProperties props) {
48 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
4649 this.saver = saver;
4750 if(props.getProperty("make-all-cycleways", false)) {
4851 log.error("option make-all-cycleways is deprecated, please use make-opposite-cycleways");
6063
6164 @Override
6265 public Set<String> getUsedTags() {
63 Set<String> usedTags = new HashSet<>(Arrays.asList("highway", "access", "barrier", "FIXME", "fixme",
64 "route", "oneway", "junction", "name", Exit.TAG_ROAD_REF, "ref"));
66 Set<String> usedTags = new HashSet<>(Arrays.asList("highway", "access", "barrier", "oneway", "junction", "name",
67 Exit.TAG_ROAD_REF, "ref", "motorroad"));
6568 if (makeOppositeCycleways) {
6669 // need the additional tags
6770 usedTags.add("cycleway");
7679
7780 @Override
7881 public void onAddNode(Node node) {
79 String val = node.getTag("highway");
80 if (val != null && ("motorway_junction".equals(val) || "services".equals(val))) {
82 String highway = node.getTag("highway");
83 if (highway != null && ("motorway_junction".equals(highway) || "services".equals(highway) || "rest_area".equals(highway))) {
8184 exits.add(node);
8285 node.addTag("mkgmap:osmid", String.valueOf(node.getId()));
8386 }
8487 }
8588
8689 @Override
87 public void onCoordAddedToWay(Way way, long id, Coord co) {
90 public void onNodeAddedToWay(Way way, long id) {
8891 if (!linkPOIsToWays)
8992 return;
9093
9194 currentNodeInWay = saver.getNode(id);
95 if (currentNodeInWay == null)
96 return;
97 Coord co = currentNodeInWay.getLocation();
98
9299 // if this Coord is also a POI, replace it with an
93100 // equivalent CoordPOI that contains a reference to
94101 // the POI's Node so we can access the POI's tags
95 if (!(co instanceof CoordPOI) && currentNodeInWay != null) {
102 if (!(co instanceof CoordPOI)) {
96103 // for now, only do this for nodes that have
97104 // certain tags otherwise we will end up creating
98105 // a CoordPOI for every node in the way
136143 @Override
137144 public void onAddWay(Way way) {
138145 String highway = way.getTag("highway");
139 if (highway != null || "ferry".equals(way.getTag("route"))) {
146 if (highway != null) {
140147 // if the way is a roundabout but isn't already
141148 // flagged as "oneway", flag it here
142149 if ("roundabout".equals(way.getTag("junction")) && way.getTag("oneway") == null) {
165172 }
166173 }
167174
168 if("motorway".equals(highway) || "trunk".equals(highway))
175 if ("motorway".equals(highway) || "trunk".equals(highway) || "primary".equals(highway) || way.tagIsLikeYes("motorroad"))
169176 motorways.add(way);
177 else if (linkPOIsToWays && ("services".equals(highway) || "rest_area".equals(highway))) {
178 exits.add(way);
179 way.addTag("mkgmap:osmid", String.valueOf(way.getId()));
180 }
170181 }
171182
172183 @Override
176187 motorways.clear();
177188 }
178189
190 private static final int XTRA = 150; // very approx 300m
179191 private void finishExits() {
180 for (Node e : exits) {
192 if (exits.isEmpty() || motorways.isEmpty())
193 return;
194 ElementQuadTree majorRoads = new ElementQuadTree(saver.getBoundingBox(), motorways);
195 for (Element e : exits) {
181196 String refTag = Exit.TAG_ROAD_REF;
182197 if (e.getTag(refTag) == null) {
183198 String exitName = e.getTag("name");
186201
187202 String ref = null;
188203 Way motorway = null;
189 for (Way w : motorways) {
190 // uses an implicit call of Coord.equals()
191 if (w.getPoints().contains(e.getLocation())) {
192 motorway = w;
193 ref = w.getTag("ref");
194 if(ref != null)
195 break;
204 Area bBox;
205 if (e instanceof Node)
206 bBox = Area.getBBox(Collections.singletonList(((Node) e).getLocation()));
207 else
208 bBox = Area.getBBox(((Way) e).getPoints());
209 String highway = e.getTag("highway");
210 final boolean isServices = "services".equals(highway) || "rest_area".equals(highway);
211 if (isServices) // services will be just off the road, so increase size
212 bBox = new Area(bBox.getMinLat() - XTRA, bBox.getMinLong() - XTRA, bBox.getMaxLat() + XTRA, bBox.getMaxLong() + XTRA);
213 List<Way> possibleRoads = new ArrayList<>();
214 for (Element w : majorRoads.get(bBox)) {
215 motorway = (Way) w;
216 ref = motorway.getTag("ref");
217 if (ref != null) {
218 if (isServices) {
219 possibleRoads.add(motorway); // save all possibilities
220 } else { // probably on 2+ roads, save possibilities to find the more major road (doesn't have to be motorway)
221 if (!(e instanceof Node))
222 log.warn("Motorway exit", exitName, "expected to be a Node", e);
223 else if (motorway.getPoints().contains(((Node) e).getLocation()))
224 possibleRoads.add(motorway);
225 }
196226 }
197227 }
198228
229 if (possibleRoads.size() > 1) {
230 if (isServices) { // pick the closest road
231 Coord serviceCoord;
232 if (e instanceof Node)
233 serviceCoord = ((Node) e).getLocation();
234 else
235 // Simple-minded logic to see if a Way that probably defines [part of] a
236 // services area, hence might become a POI if option --link-pois-to-ways,
237 // is near the specified road.
238 // Just pick an arbitary Coord on the services boundary and find the nearest
239 // any Coord in the roads.
240 // No need to check if it is near the line between far-apart Coords because
241 // there also needs to be a junction so that can get off the road to the
242 // services.
243 serviceCoord = ((Way)e).getFirstPoint();
244 long closestRoad = Long.MAX_VALUE;
245 for (Way road : possibleRoads) {
246 long closestCoord = Long.MAX_VALUE;
247 for (Coord pointOnRoad : road.getPoints()) {
248 long dist = pointOnRoad.distanceInHighPrecSquared(serviceCoord);
249 if (dist < closestCoord)
250 closestCoord = dist;
251 }
252 if (closestCoord < closestRoad) {
253 closestRoad = closestCoord;
254 motorway = road;
255 ref = motorway.getTag("ref");
256 }
257 }
258 } else { // pick the most major road
259 int bestRoad = Integer.MAX_VALUE;
260 for (Way road : possibleRoads) {
261 String roadType = road.getTag("highway");
262 int thisRoad = 4;
263 if ("motorway".equals(roadType))
264 thisRoad = 0;
265 else if (road.tagIsLikeYes("motorroad"))
266 thisRoad = 1;
267 else if ("trunk".equals(roadType))
268 thisRoad = 2;
269 else if ("primary".equals(roadType))
270 thisRoad = 3;
271 if (thisRoad < bestRoad) {
272 bestRoad = thisRoad;
273 motorway = road;
274 ref = motorway.getTag("ref");
275 }
276 }
277 }
278 //log.info("Exit", exit, possibleRoads.size(), "options, chosen:", motorway, ref);
279 } // else 0 or 1 road; ref/motorway null or set correctly
199280 if (ref != null) {
200281 log.info("Adding", refTag + "=" + ref, "to exit", exitName);
201282 e.addTag(refTag, ref);
202283 } else if(motorway != null) {
203 log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag (" + e.getLocation().toOSMURL() + ")");
284 log.warn("Motorway exit", exitName, "is positioned on a motorway that doesn't have a 'ref' tag", e);
204285 }
205286 }
206287 }
1313 package uk.me.parabola.mkgmap.reader.osm;
1414
1515 import java.util.ArrayList;
16 import java.util.Arrays;
1617 import java.util.HashSet;
1718 import java.util.List;
1819 import java.util.Set;
20 import java.util.stream.Collectors;
1921
20 import uk.me.parabola.imgfmt.app.Coord;
2122 import uk.me.parabola.log.Logger;
2223 import uk.me.parabola.util.EnhancedProperties;
2324
3132
3233 private ElementSaver saver;
3334 private final List<Node> nodes = new ArrayList<>();
34 private boolean clearNodes;
3535
36 private static final short addrHousenumberTagKey = TagDict.getInstance().xlate("addr:housenumber");
37 private static final short addrInterpolationTagKey = TagDict.getInstance().xlate("addr:interpolation");
36 private static final short TK_ADDR_HOUSENUMBER = TagDict.getInstance().xlate("addr:housenumber");
37 private static final short TK_ADDR_INTERPOLATION = TagDict.getInstance().xlate("addr:interpolation");
3838
39 public static final short partOfInterpolationTagKey = TagDict.getInstance().xlate("mkgmap:part-of-interpolation");
40 public static final short mkgmapNodeIdsTagKey = TagDict.getInstance().xlate("mkgmap:node-ids");
39 public static final short TKM_PART_OF_INTERPOLATION = TagDict.getInstance().xlate("mkgmap:part-of-interpolation");
40 public static final short TKM_NODE_IDS = TagDict.getInstance().xlate("mkgmap:node-ids");
4141 @Override
42 public boolean init(ElementSaver saver, EnhancedProperties props) {
42 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
4343 this.saver = saver;
4444 if (!props.getProperty("addr-interpolation", true))
4545 return false;
4848
4949 @Override
5050 public Set<String> getUsedTags() {
51 HashSet<String> usedTags = new HashSet<>();
52 usedTags.add("addr:street");
53 usedTags.add("addr:housenumber");
54 usedTags.add("addr:interpolation");
55 usedTags.add("addr:place");
56 return usedTags;
51 return new HashSet<>(Arrays.asList("addr:street", "addr:housenumber", "addr:interpolation", "addr:place"));
5752 }
5853
5954 @Override
60 public void onCoordAddedToWay(Way way, long id, Coord co) {
61 if (clearNodes){
62 nodes.clear();
63 clearNodes = false;
55 public void onNodeAddedToWay(Way way, long id) {
56 Node currentNodeInWay = saver.getNode(id);
57 if (currentNodeInWay != null && currentNodeInWay.getTag(TK_ADDR_HOUSENUMBER) != null) {
58 // this node might be part of a way that has the addr:interpolation tag
59 nodes.add(currentNodeInWay);
6460 }
65 Node currentNodeInWay = saver.getNode(id);
66 if (currentNodeInWay == null || currentNodeInWay.getTag(addrHousenumberTagKey) == null)
67 return;
68 // this node might be part of a way that has the addr:interpolation tag
69 nodes.add(currentNodeInWay);
7061 }
7162
7263 @Override
7364 public void onAddWay(Way way) {
74 clearNodes = true; // make sure that the list is cleared with the next coord
75 String ai = way.getTag(addrInterpolationTagKey);
76 if (ai == null)
77 return;
78 if (nodes.size() < 2){
79 log.warn(way.toBrowseURL(),"tag addr:interpolation="+ai, "is ignored, found less than two valid nodes.");
80 return;
65 try {
66 String ai = way.getTag(TK_ADDR_INTERPOLATION);
67 if (ai == null)
68 return;
69 if (nodes.size() < 2) {
70 log.warn(way.toBrowseURL(), "tag addr:interpolation=" + ai, "is ignored, found less than two valid nodes.");
71 return;
72 }
73 switch (ai) {
74 case "odd":
75 case "even":
76 case "all":
77 case "1":
78 case "2":
79 break;
80 default:
81 if (log.isInfoEnabled())
82 log.warn(way.toBrowseURL(), "tag addr:interpolation=" + ai, "is ignored");
83 return;
84 }
85
86 nodes.forEach(n -> n.addTag(TKM_PART_OF_INTERPOLATION, "1"));
87 way.addTag(TKM_NODE_IDS, nodes.stream().map(n -> Long.toString(n.getId())).collect(Collectors.joining(",")));
88 } finally {
89 // always clear, else we would use nodes for the wrong way
90 nodes.clear();
8191 }
82 switch (ai) {
83 case "odd":
84 case "even":
85 case "all":
86 case "1":
87 case "2":
88 break;
89 default:
90 if (log.isInfoEnabled())
91 log.warn(way.toBrowseURL(),"tag addr:interpolation="+ai, "is ignored");
92 return;
93 }
94
95 StringBuilder sb = new StringBuilder();
96 int num = nodes.size();
97 for (int i = 0; i < num; i++) {
98 Node n = nodes.get(i);
99 String id = String.valueOf(n.getId());
100 sb.append(id);
101 if (i + 1 < num)
102 sb.append(",");
103 n.addTag(partOfInterpolationTagKey, "1");
104 }
105 way.addTag(mkgmapNodeIdsTagKey, sb.toString());
106 }
107
108 @Override
109 public void end() {
110 nodes.clear();
11192 }
11293 }
6363 private boolean processDestinations;
6464 private boolean processExits;
6565
66 private static final short TAG_KEY_HIGHWAY = TagDict.getInstance().xlate("highway");
67 private static final short TAG_KEY_ONEWAY = TagDict.getInstance().xlate("oneway");
68 private static final short TAG_KEY_EXIT_TO = TagDict.getInstance().xlate("exit_to");
69 private static final short TAG_KEY_DEST_HINT_WORK = TagDict.getInstance().xlate("mkgmap:dest_hint_work");
66 private static final short TK_HIGHWAY = TagDict.getInstance().xlate("highway");
67 private static final short TK_ONEWAY = TagDict.getInstance().xlate("oneway");
68 private static final short TK_EXIT_TO = TagDict.getInstance().xlate("exit_to");
69 private static final short TKM_DEST_HINT_WORK = TagDict.getInstance().xlate("mkgmap:dest_hint_work");
7070
7171 @Override
72 public boolean init(ElementSaver saver, EnhancedProperties props) {
72 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
7373 this.saver = saver;
7474 nameFinder = new NameFinder(props);
7575 processDestinations = props.containsKey("process-destination");
8787 // ignore one-node or zero-node ways
8888 continue;
8989 }
90 String highwayTag = w.getTag(TAG_KEY_HIGHWAY);
90 String highwayTag = w.getTag(TK_HIGHWAY);
9191 if (highwayTag != null && highwayTypes.contains(highwayTag)) {
9292 processHighWay(w, highwayTag);
9393 }
169169 }
170170
171171 if (destHint != null) {
172 w.addTag(TAG_KEY_DEST_HINT_WORK, destHint);
172 w.addTag(TKM_DEST_HINT_WORK, destHint);
173173 destinationLinkWays.add(w);
174174
175175 if (log.isDebugEnabled() && !standardTagKey.equals(destSourceTagKey)) {
266266 if (dist <= maxLength) {
267267 // create a new way with the first two points and identical tags
268268 Way precedingWay = new Way(w.getOriginalId(), w.getPoints().subList(0, 1 + 1));
269 precedingWay.setFakeId();
269 precedingWay.markAsGeneratedFrom(w);
270270 precedingWay.copyTags(w);
271271
272272 saver.addWay(precedingWay);
304304 cConnection = alternative;
305305 // create the new way with identical tags
306306 w.getPoints().add(i, cConnection);
307 Way precedingWay = new Way(w.getOriginalId(), new ArrayList<Coord>(w.getPoints().subList(0, i + 1)));
308 precedingWay.setFakeId();
307 Way precedingWay = new Way(w.getOriginalId(), new ArrayList<>(w.getPoints().subList(0, i + 1)));
308 precedingWay.markAsGeneratedFrom(w);
309309 precedingWay.copyTags(w);
310310
311311 saver.addWay(precedingWay);
334334 * @return <code>true</code> if the node is a usable exit, else <code>false</code>
335335 */
336336 private boolean isTaggedAsExit(Node node) {
337 return "motorway_junction".equals(node.getTag(TAG_KEY_HIGHWAY))
338 && (node.getTag("ref") != null || (nameFinder.getName(node) != null) || node.getTag(TAG_KEY_EXIT_TO) != null);
337 return "motorway_junction".equals(node.getTag(TK_HIGHWAY))
338 && (node.getTag("ref") != null || (nameFinder.getName(node) != null) || node.getTag(TK_EXIT_TO) != null);
339339 }
340340
341341 /**
373373 log.debug(destinationLinkWays.size(),"links with destination tag");
374374 while (!linksWithDestination.isEmpty()) {
375375 Way linkWay = linksWithDestination.poll();
376 String destination = linkWay.getTag(TAG_KEY_DEST_HINT_WORK);
376 String destination = linkWay.getTag(TKM_DEST_HINT_WORK);
377377 if (log.isDebugEnabled())
378378 log.debug("Check way", linkWay.getId(), linkWay.toTagString());
379379
383383 Set<Way> nextWays = adjacentWays.get(c);
384384 if (nextWays != null) {
385385 for (Way connectedWay : nextWays) {
386 String nextDest = connectedWay.getTag(TAG_KEY_DEST_HINT_WORK);
386 String nextDest = connectedWay.getTag(TKM_DEST_HINT_WORK);
387387
388388 if (log.isDebugEnabled())
389389 log.debug("Followed by",connectedWay.getId(),connectedWay.toTagString());
395395
396396 boolean startEndConnection = c == c2;
397397 if (startEndConnection && !connectedWay.equals(linkWay)
398 && connectedWay.getTag(TAG_KEY_HIGHWAY).endsWith("_link")
398 && connectedWay.getTag(TK_HIGHWAY).endsWith("_link")
399399 && destination.equals(nextDest)) {
400400 // do not use this way because there is another link before that with the same destination
401401 boolean removed = destinationLinkWays.remove(connectedWay);
435435 log.debug("Exit node", exitNode, "has no connected ways. Skip it.");
436436 return;
437437 }
438 String exitTo = exitNode.getTag(TAG_KEY_EXIT_TO);
438 String exitTo = exitNode.getTag(TK_EXIT_TO);
439439 if (exitTo != null) {
440440 int countMatches = 0;
441441 int preferred = Integer.MAX_VALUE;
442442 for (Way w : exitWays) {
443 String hw = w.getTag(TAG_KEY_HIGHWAY);
443 String hw = w.getTag(TK_HIGHWAY);
444444 int pos = hwSorted.indexOf(hw);
445445 if (pos < preferred) {
446446 preferred = pos;
463463 }
464464
465465 private void processExitWay(Node exitNode, Way w, String exitTo) {
466 String highwayLinkTag = w.getTag(TAG_KEY_HIGHWAY);
466 String highwayLinkTag = w.getTag(TK_HIGHWAY);
467467 if (highwayLinkTag.endsWith("_link")) {
468468 log.debug("Try to cut", highwayLinkTag, w, "into three parts for giving hint to exit", exitNode);
469469 Way hintWay = splitWay(w, "exit");
487487
488488 private String fixDestHint(Way hintWay) {
489489 if (processDestinations) {
490 String hint = hintWay.getTag(TAG_KEY_DEST_HINT_WORK);
490 String hint = hintWay.getTag(TKM_DEST_HINT_WORK);
491491 if (hint != null) {
492 hintWay.deleteTag(TAG_KEY_DEST_HINT_WORK);
492 hintWay.deleteTag(TKM_DEST_HINT_WORK);
493493 hintWay.addTag("mkgmap:dest_hint", hint);
494494 }
495495 return hint;
504504 while (!destinationLinkWays.isEmpty()) {
505505 Way w = destinationLinkWays.iterator().next();
506506 destinationLinkWays.remove(w);
507 String highwayLinkTag = w.getTag(TAG_KEY_HIGHWAY);
507 String highwayLinkTag = w.getTag(TK_HIGHWAY);
508508 if (!canSplit(w) || !highwayLinkTag.endsWith("_link"))
509509 continue;
510510
622622 * @return <code>true</code> way is oneway
623623 */
624624 private static boolean isOnewayInDirection(Way w) {
625 if (w.tagIsLikeYes(TAG_KEY_ONEWAY)) {
625 if (w.tagIsLikeYes(TK_ONEWAY)) {
626626 return true;
627627 }
628628
629629 // check if oneway is set implicitly by the highway type (motorway and motorway_link)
630 String onewayTag = w.getTag(TAG_KEY_ONEWAY);
631 String highwayTag = w.getTag(TAG_KEY_HIGHWAY);
630 String onewayTag = w.getTag(TK_ONEWAY);
631 String highwayTag = w.getTag(TK_HIGHWAY);
632632 return onewayTag == null && highwayTag != null
633633 && ("motorway".equals(highwayTag)|| "motorway_link".equals(highwayTag));
634634 }
639639 * @return <code>true</code> way is oneway in opposite direction
640640 */
641641 private static boolean isOnewayOppositeDirection(Way w) {
642 return "-1".equals(w.getTag(TAG_KEY_ONEWAY));
642 return "-1".equals(w.getTag(TK_ONEWAY));
643643 }
644644
645645 /**
648648 * @return <code>true</code> way is not oneway
649649 */
650650 private static boolean isNotOneway(Way w) {
651 return "no".equals(w.getTag(TAG_KEY_ONEWAY)) || (!isOnewayInDirection(w) && !isOnewayOppositeDirection(w));
651 return "no".equals(w.getTag(TK_ONEWAY)) || (!isOnewayInDirection(w) && !isOnewayOppositeDirection(w));
652652 }
653653 }
5353 private EnhancedProperties props;
5454
5555 @Override
56 public boolean init(ElementSaver saver, EnhancedProperties props) {
56 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
5757 boundaryDirName = props.getProperty("bounds");
5858
5959 if (boundaryDirName == null) {
147147 if (mpCenter != null && saver.getBoundingBox().contains(mpCenter)){
148148 // create a fake node for which the bounds information is collected
149149 Node mpNode = new Node(r.getOriginalId(), mpCenter);
150 mpNode.setFakeId();
150 mpNode.markAsGeneratedFrom(r);
151151 processElem(mpNode);
152152 // copy the bounds tags back to the multipolygon
153153 for (String boundsTag : BoundaryQuadTree.mkgmapTagsArray) {
236236 for (Area area : finishedAreas) {
237237 Way w = singularAreaToWay(area, rel.getOriginalId());
238238 if (w != null) {
239 w.setFakeId();
239 w.markAsGeneratedFrom(rel);
240240 // make sure that equal coords are changed to identical coord instances
241241 // this allows merging in the ShapeMerger
242242 int n = w.getPoints().size();
2222 private ElementSaver saver;
2323
2424 @Override
25 public boolean init(ElementSaver saver, EnhancedProperties props) {
25 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
2626 this.saver = saver;
2727 return true;
2828 }
3232 long t1 = System.currentTimeMillis();
3333 log.info("Finishing multipolygons");
3434 for (Way way : saver.getWays().values()) {
35 String removeTag = way.getTag(ElementSaver.MKGMAP_REMOVE_TAG_KEY);
35 String removeTag = way.getTag(ElementSaver.TKM_REMOVETAGS);
3636 if (removeTag == null) {
3737 continue;
3838 }
4040 if (log.isDebugEnabled()) {
4141 log.debug("Remove tags",Arrays.toString(tagsToRemove),"from way",way.getId(),way.toTagString());
4242 }
43 way.deleteTag(ElementSaver.MKGMAP_REMOVE_TAG_KEY);
43 way.deleteTag(ElementSaver.TKM_REMOVETAGS);
4444 for (String rTag : tagsToRemove) {
4545 way.deleteTag(rTag);
4646 }
5555 public static final String STYLE_FILTER_POLYGON = "polygon";
5656
5757 /** A tag that is set with value true on each polygon that is created by the mp processing. */
58 public static final short MP_CREATED_TAG_KEY = TagDict.getInstance().xlate("mkgmap:mp_created");
59 private static final short MP_ROLE_TAG_KEY = TagDict.getInstance().xlate("mkgmap:mp_role");
60 private static final short CACHE_AREA_SIZE_TAG_KEY = TagDict.getInstance().xlate("mkgmap:cache_area_size");
58 public static final short TKM_MP_CREATED = TagDict.getInstance().xlate("mkgmap:mp_created");
59 private static final short TKM_MP_ROLE = TagDict.getInstance().xlate("mkgmap:mp_role");
60 private static final short TKM_CACHE_AREA_SIZEKEY = TagDict.getInstance().xlate("mkgmap:cache_area_size");
6161 private final Map<Long, Way> tileWayMap;
6262 private final Map<Long, String> roleMap = new HashMap<>();
6363
553553
554554 /**
555555 * Removes all non closed ways from the given list.
556 * <code>{@link Way#hasIdenticalEndPoints()} == false</code>)
556 * <code>!{@link Way#hasIdenticalEndPoints()}</code>)
557557 *
558558 * @param wayList
559559 * list of ways
10011001 mpWay.setFullArea(fullArea);
10021002 // mark this polygons so that only polygon style rules are applied
10031003 mpWay.addTag(STYLE_FILTER_TAG, STYLE_FILTER_POLYGON);
1004 mpWay.addTag(MP_CREATED_TAG_KEY, "true");
1004 mpWay.addTag(TKM_MP_CREATED, "true");
10051005
10061006 if (currentPolygon.outer) {
1007 mpWay.addTag(MP_ROLE_TAG_KEY, "outer");
1007 mpWay.addTag(TKM_MP_ROLE, "outer");
10081008 if (isAreaSizeCalculated())
10091009 mpAreaSize += calcAreaSize(mpWay.getPoints());
10101010 } else {
1011 mpWay.addTag(MP_ROLE_TAG_KEY, "inner");
1011 mpWay.addTag(TKM_MP_ROLE, "inner");
10121012 }
10131013
10141014 getMpPolygons().put(mpWay.getId(), mpWay);
10691069 // the simple line information should be used.
10701070 for (Way orgOuterWay : outerWaysForLineTagging) {
10711071 Way lineTagWay = new Way(getOriginalId(), orgOuterWay.getPoints());
1072 lineTagWay.setFakeId();
1072 lineTagWay.markAsGeneratedFrom(this);
10731073 lineTagWay.addTag(STYLE_FILTER_TAG, STYLE_FILTER_LINE);
1074 lineTagWay.addTag(MP_CREATED_TAG_KEY, "true");
1074 lineTagWay.addTag(TKM_MP_CREATED, "true");
10751075 if (mpAreaSizeStr != null) {
10761076 // assign the area size of the whole multipolygon to all outer polygons
1077 lineTagWay.addTag(CACHE_AREA_SIZE_TAG_KEY, mpAreaSizeStr);
1077 lineTagWay.addTag(TKM_CACHE_AREA_SIZEKEY, mpAreaSizeStr);
10781078 }
10791079 for (Entry<String,String> tag : outerTags.entrySet()) {
10801080 lineTagWay.addTag(tag.getKey(), tag.getValue());
11041104 if (isAreaSizeCalculated()) {
11051105 // assign the area size of the whole multipolygon to all outer polygons
11061106 String mpAreaSizeStr = String.format(Locale.US, "%.3f", mpAreaSize);
1107 addTag(CACHE_AREA_SIZE_TAG_KEY, mpAreaSizeStr);
1107 addTag(TKM_CACHE_AREA_SIZEKEY, mpAreaSizeStr);
11081108 for (Way w : mpPolygons.values()) {
1109 if ("outer".equals(w.getTag(MP_ROLE_TAG_KEY))) {
1110 w.addTag(CACHE_AREA_SIZE_TAG_KEY, mpAreaSizeStr);
1109 if ("outer".equals(w.getTag(TKM_MP_ROLE))) {
1110 w.addTag(TKM_CACHE_AREA_SIZEKEY, mpAreaSizeStr);
11111111 }
11121112 }
11131113 }
18051805 // the simple line information should be used.
18061806 for (Way orgOuterWay : outerWaysForLineTagging) {
18071807 Way lineTagWay = new Way(getOriginalId(), orgOuterWay.getPoints());
1808 lineTagWay.setFakeId();
1808 lineTagWay.markAsGeneratedFrom(this);
18091809 lineTagWay.addTag(STYLE_FILTER_TAG, STYLE_FILTER_LINE);
1810 lineTagWay.addTag(MP_CREATED_TAG_KEY, "true");
1810 lineTagWay.addTag(TKM_MP_CREATED, "true");
18111811 for (Entry<String, String> tag : tags.entrySet()) {
18121812 lineTagWay.addTag(tag.getKey(), tag.getValue());
18131813
18701870 if (tag == null || tag.isEmpty()) {
18711871 return;
18721872 }
1873 String tagsToRemove = way.getTag(ElementSaver.MKGMAP_REMOVE_TAG_KEY);
1873 String tagsToRemove = way.getTag(ElementSaver.TKM_REMOVETAGS);
18741874
18751875 if (tagsToRemove == null) {
18761876 tagsToRemove = tag;
18831883 }
18841884 tagsToRemove += ";" + tag;
18851885 }
1886 way.addTag(ElementSaver.MKGMAP_REMOVE_TAG_KEY, tagsToRemove);
1886 way.addTag(ElementSaver.TKM_REMOVETAGS, tagsToRemove);
18871887 }
18881888
18891889 /**
19501950
19511951 public JoinedWay(Way originalWay) {
19521952 super(originalWay.getOriginalId(), originalWay.getPoints());
1953 setFakeId();
1953 markAsGeneratedFrom(originalWay);
19541954 originalWays = new ArrayList<>();
19551955 addWay(originalWay);
19561956
1616
1717 public class OsmCoastDataSource extends OsmMapDataSource {
1818
19 private static final Set<String> coastlineTags = Collections.singleton("natural");
20
19 @Override
2120 protected OsmReadingHooks[] getPossibleHooks() {
2221 // no hooks
2322 return new OsmReadingHooks[] {};
2423 }
2524
25 @Override
2626 protected void createElementSaver() {
2727 elementSaver = new CoastlineElementSaver(getConfig());
2828 }
2929
30 @Override
3031 public Set<String> getUsedTags() {
31 return coastlineTags;
32 return Collections.singleton("natural");
3233 }
3334 }
3030 *
3131 * @param way The OSM way.
3232 */
33 public void convertWay(Way way);
33 default void convertWay(Way way) {}
3434
3535 /**
3636 * Takes a node (that has its own identity) and converts it from the OSM
3838 *
3939 * @param node The node to convert.
4040 */
41 public void convertNode(Node node);
41 default void convertNode(Node node) {}
4242
4343 /**
4444 * Takes a relation and applies rules that affect the garmin types
5151 *
5252 * @param relation The relation to convert.
5353 */
54 public void convertRelation(Relation relation);
54 default void convertRelation(Relation relation) {}
5555
56 public default void augmentWith(uk.me.parabola.mkgmap.reader.osm.ElementSaver elementSaver) {
57 }
56 default void augmentWith(uk.me.parabola.mkgmap.reader.osm.ElementSaver elementSaver) {}
5857
5958 /**
6059 * Set the bounding box for this map. This should be set before any other
6867 *
6968 * @param bbox The bounding area.
7069 */
71 public void setBoundingBox(Area bbox);
70 default void setBoundingBox(Area bbox) {}
7271
7372 /**
7473 * Called when all conversion has been done.
7574 */
76 public void end();
75 default void end() {}
7776
7877 /**
7978 * @return true/false if source contains info about driving side, else null
8079 */
81 public Boolean getDriveOnLeft();
80 default Boolean getDriveOnLeft() {
81 return null; // unknown
82 }
8283 }
1818 import java.util.Set;
1919 import java.util.regex.Pattern;
2020
21 import uk.me.parabola.imgfmt.ExitException;
2122 import uk.me.parabola.imgfmt.FormatException;
2223 import uk.me.parabola.imgfmt.app.Area;
2324 import uk.me.parabola.imgfmt.app.Coord;
7778 usedTags = null;
7879 return;
7980 }
80 usedTags = new HashMap<String, String>();
81 usedTags = new HashMap<>();
8182 for (String s : used) {
8283 if (s == null) {
8384 continue;
161162 * It is saved
162163 * @param way The way that was read.
163164 */
164 protected void endWay(Way way) {
165 protected final void endWay(Way way) {
165166 way.setClosedInOSM(firstNodeRef == lastNodeRef);
166167 way.setComplete(!missingNodeRef);
167168
174175 * @param way The Way.
175176 * @param id The coordinate id.
176177 */
177 protected void addCoordToWay(Way way, long id) {
178 protected final void addCoordToWay(Way way, long id) {
178179 lastNodeRef = id;
179180 if (firstNodeRef == 0) firstNodeRef = id;
180181
181182 Coord co = saver.getCoord(id);
182183
183184 if (co != null) {
184 hooks.onCoordAddedToWay(way, id, co);
185 co = saver.getCoord(id);
185 Node node = saver.getNode(id);
186 if (node != null && node.getTagCount() > 0) {
187 hooks.onNodeAddedToWay(way, id);
188 // hooks can change the node and the coord object associated with the id
189 co = saver.getCoord(id);
190 if (co == null) {
191 throw new ExitException("Internal error: hooks removed coord with id " + id);
192 }
193 }
186194 way.addPoint(co);
187195 } else {
188196 missingNodeRef = true;
3131
3232 import uk.me.parabola.imgfmt.Utils;
3333 import uk.me.parabola.log.Logger;
34 import uk.me.parabola.mkgmap.CommandArgs;
3435 import uk.me.parabola.mkgmap.general.LevelInfo;
3536 import uk.me.parabola.mkgmap.general.LoadableMapDataSource;
36 import uk.me.parabola.mkgmap.osmstyle.NameFinder;
3737 import uk.me.parabola.mkgmap.osmstyle.StyleImpl;
3838 import uk.me.parabola.mkgmap.osmstyle.StyledConverter;
3939 import uk.me.parabola.mkgmap.reader.MapperBasedMapDataSource;
238238 for (OsmReadingHooks p : getPossibleHooks()) {
239239 if (p instanceof ResidentialHook && style != null && !style.getUsedTags().contains("mkgmap:residential"))
240240 continue;
241 if (p.init(saver, props)){
241 if (p.init(saver, props, style)){
242242 plugins.add(p);
243 if (p instanceof RelationStyleHook)
244 ((RelationStyleHook) p).setStyle(style);
245243 }
246244 }
247245
288286 parts[0] = parts[0].trim();
289287 parts[1] = parts[1].trim();
290288 if ("*".equals(parts[1])) {
291 deletedTags.put(parts[0], new HashSet<String>());
289 deletedTags.put(parts[0], new HashSet<>());
292290 } else {
293 Set<String> vals = deletedTags.get(parts[0]);
294 if (vals == null)
295 vals = new HashSet<>();
296 vals.add(parts[1]);
297 deletedTags.put(parts[0], vals);
291 deletedTags.computeIfAbsent(parts[0], k-> new HashSet<>()).add(parts[1]);
298292 }
299293 } else {
300294 log.error("Ignoring bad line in deleted tags file: " + line);
324318 setStyle(StyleImpl.readStyle(props));
325319
326320 usedTags.addAll(style.getUsedTags());
327 usedTags.addAll(NameFinder.getNameTags(props));
321 // make sure that we don't remove tags which are only used with the mkgmap:from-node: prefix
322 style.getUsedTags().stream().filter(s -> s.startsWith(POIGeneratorHook.FROM_NODE_PREFIX))
323 .map(s -> s.substring(POIGeneratorHook.FROM_NODE_PREFIX.length())).forEach(usedTags::add);
324 usedTags.addAll(CommandArgs.getNameTags(props));
328325 converter = new StyledConverter(style, mapper, props);
329326 }
330327
1616
1717 public class OsmPrecompSeaDataSource extends OsmMapDataSource {
1818
19 private static final Set<String> coastlineTags = Collections.singleton("natural");
20
19 @Override
2120 protected OsmReadingHooks[] getPossibleHooks() {
2221 // no hooks
2322 return new OsmReadingHooks[] {};
2423 }
2524
25 @Override
2626 public Set<String> getUsedTags() {
27 return coastlineTags;
27 return Collections.singleton("natural");
2828 }
2929 }
1414 import java.util.Collections;
1515 import java.util.Set;
1616
17 import uk.me.parabola.imgfmt.app.Coord;
1817 import uk.me.parabola.util.EnhancedProperties;
1918
2019 /**
4847 * element that is being passed in as it will be added automatically.
4948 *
5049 * @param props The command line options.
50 * @param style The style used for this input file
5151 *
5252 * @return If you return false then this set of hooks will not be used. So if they
5353 * are not needed based on the options supplied you can disable it.
5454 */
55 public default boolean init(ElementSaver saver, EnhancedProperties props) {
55 default boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
5656 return true;
5757 }
5858
6262 *
6363 * @return the tag names used by this hook
6464 */
65 public default Set<String> getUsedTags() {
65 default Set<String> getUsedTags() {
6666 return Collections.emptySet();
6767 }
6868
8080 *
8181 * @param way The osm way.
8282 */
83 public default void onAddWay(Way way) {}
83 default void onAddWay(Way way) {}
8484
8585 /**
86 * This is called whenever a node is added to a way. A node is something with tags, not just a Coord.
86 * This is called whenever a tagged node is added to a way.
8787 *
88 * The way will not have been added via addWay() yet. The node is the node that
89 *
88 * The way will not have been added via addWay() yet.
89 * Hooks can change the node and the coord object associated with the id, but they must not remove them.
90 *
9091 * @param way The incomplete way.
9192 * @param coordId The coordinate id of the node that is being added.
92 * @param co The coordinate.
9393 */
94 public default void onCoordAddedToWay(Way way, long coordId, Coord co) {}
94 default void onNodeAddedToWay(Way way, long coordId) {}
9595
9696 /**
9797 * Called after the file has been read. Can be used to add more elements to the saver
9898 * based on information stored up.
9999 */
100 public default void end() {}
100 default void end() {}
101101
102102 }
1515 import java.util.HashSet;
1616 import java.util.Set;
1717
18 import uk.me.parabola.imgfmt.app.Coord;
1918 import uk.me.parabola.util.EnhancedProperties;
2019
2120 /**
4847 }
4948
5049 @Override
51 public boolean init(ElementSaver saver, EnhancedProperties props) {
50 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
5251 for (int i = 0; i < readingHooks.length; i++)
53 readingHooks[i].init(saver, props);
52 readingHooks[i].init(saver, props, style);
5453 return true;
5554 }
5655
6160 }
6261
6362 @Override
64 public void onCoordAddedToWay(Way way, long coordId, Coord co) {
63 public void onNodeAddedToWay(Way way, long coordId) {
6564 for (int i = 0; i < readingHooks.length; i++)
66 readingHooks[i].onCoordAddedToWay(way, coordId, co);
65 readingHooks[i].onNodeAddedToWay(way, coordId);
6766 }
6867
6968 @Override
1515 import java.util.AbstractMap;
1616 import java.util.ArrayList;
1717 import java.util.Collections;
18 import java.util.HashSet;
1918 import java.util.IdentityHashMap;
2019 import java.util.List;
2120 import java.util.Map;
2221 import java.util.Map.Entry;
2322 import java.util.Set;
23 import java.util.stream.Collectors;
2424
2525 import uk.me.parabola.imgfmt.app.Coord;
2626 import uk.me.parabola.log.Logger;
6363 private static final Logger log = Logger.getLogger(POIGeneratorHook.class);
6464
6565 private List<Entry<String,String>> poiPlacementTags;
66 /**
67 * maps only those locations which are used in nodes with tags which are used in
68 * the points rules with the {@code FROM_NODE_PREFIX} and which are not already {@link CoordPOI} instances.
69 * The mapping is only needed to create the POIs, thus we don't create {@link CoordPOI} instances for them.
70 */
71 private IdentityHashMap<Coord, Node> coordToNodeMap;
6672
6773 private ElementSaver saver;
6874
6975 private boolean poisToAreas = false;
7076 private boolean poisToLines = false;
77 private boolean poisToLinesStart = false;
78 private boolean poisToLinesEnd = false;
79 private boolean poisToLinesMid = false;
80 private boolean poisToLinesOther = false;
7181 private NameFinder nameFinder;
7282 private AreaSizeFunction areaSizeFunction = new AreaSizeFunction();
83
84 private Set<String> usedTagsPOI;
7385
7486 /** Name of the bool tag that is set to true if a POI is created from an area */
75 public static final short AREA2POI_TAG = TagDict.getInstance().xlate("mkgmap:area2poi");
76 public static final short LINE2POI_TAG = TagDict.getInstance().xlate("mkgmap:line2poi");
77 public static final short LINE2POI_TYPE_TAG = TagDict.getInstance().xlate("mkgmap:line2poitype");
78 public static final short WAY_LENGTH_TAG = TagDict.getInstance().xlate("mkgmap:way-length");
87 public static final short TKM_AREA2POI = TagDict.getInstance().xlate("mkgmap:area2poi");
88 public static final short TKM_LINE2POI = TagDict.getInstance().xlate("mkgmap:line2poi");
89 public static final short TKM_LINE2POI_TYPE = TagDict.getInstance().xlate("mkgmap:line2poitype");
90 public static final short TKM_WAY_LENGTH = TagDict.getInstance().xlate("mkgmap:way-length");
7991
8092 @Override
81 public boolean init(ElementSaver saver, EnhancedProperties props) {
93 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
8294 poisToAreas = props.containsKey("add-pois-to-areas");
8395 poisToLines = props.containsKey("add-pois-to-lines");
96 if (poisToLines) {
97 String[] opts = {"all"};
98 if (!props.getProperty("add-pois-to-lines").isEmpty()) {
99 opts = props.getProperty("add-pois-to-lines").split(",");
100 }
101
102 for (String opt : opts) {
103 switch (opt.trim()) {
104 case "start":
105 poisToLinesStart = true;
106 break;
107 case "end":
108 poisToLinesEnd = true;
109 break;
110 case "mid":
111 poisToLinesMid = true;
112 break;
113 case "other":
114 poisToLinesOther= true;
115 break;
116 case "all":
117 poisToLinesStart= true;
118 poisToLinesEnd= true;
119 poisToLinesMid = true;
120 poisToLinesOther= true;
121 break;
122
123 default:
124 throw new IllegalArgumentException("Invalied argument '"+opt+"' for add-pois-to-lines");
125 }
126 }
127
128 }
84129
85130 if (!(poisToAreas || poisToLines)) {
86131 log.info("Disable Areas2POIHook because add-pois-to-areas and add-pois-to-lines option is not set.");
91136 this.poiPlacementTags = getPoiPlacementTags(props);
92137
93138 this.saver = saver;
94
139 if (style != null && style.getUsedTagsPOI() != null) {
140 // extract special tags used in the points file
141 usedTagsPOI = style.getUsedTagsPOI().stream()
142 .filter(s -> s.startsWith(FROM_NODE_PREFIX))
143 .map(s -> s.substring(POIGeneratorHook.FROM_NODE_PREFIX.length()))
144 .collect(Collectors.toSet());
145 } else {
146 usedTagsPOI = Collections.emptySet();
147 }
95148 return true;
96149 }
97150
149202 return tagList;
150203 }
151204
152
153205 @Override
154206 public Set<String> getUsedTags() {
155 // return all tags defined in the poiPlacementTags
156 Set<String> tags = new HashSet<>();
157 for (Entry<String,String> poiTag : poiPlacementTags) {
158 tags.add(poiTag.getKey());
159 }
160 return tags;
207 return poiPlacementTags.stream().map(Map.Entry::getKey).collect(Collectors.toSet());
161208 }
162209
163210 @Override
164211 public void end() {
165212 log.info(getClass().getSimpleName(), "started");
213 coordToNodeMap = new IdentityHashMap<>();
214 if (!usedTagsPOI.isEmpty()) {
215 for (Node n : saver.getNodes().values()) {
216 if (n.getLocation() instanceof CoordPOI)
217 continue;
218 for (String key : usedTagsPOI) {
219 if (n.getTag(key) != null) {
220 coordToNodeMap.put(n.getLocation(), n);
221 break;
222 }
223 }
224 }
225 }
166226 addPOIsForWays();
167227 addPOIsForMPs();
228 coordToNodeMap.clear();
168229 log.info(getClass().getSimpleName(), "finished");
169230 }
170231
208269 }
209270
210271 // do not add POIs for polygons created by multipolygon processing
211 if (w.tagIsLikeYes(MultiPolygonRelation.MP_CREATED_TAG_KEY)) {
272 if (w.tagIsLikeYes(MultiPolygonRelation.TKM_MP_CREATED)) {
212273 if (log.isDebugEnabled())
213274 log.debug("MP processed: Do not create POI for", w.toTagString());
214275 continue;
242303 // get the coord where the poi is placed
243304 Coord poiCoord = null;
244305 // do we have some labeling coords?
245 if (labelCoords.size() > 0) {
306 if (!labelCoords.isEmpty()) {
246307 int poiOrder = Integer.MAX_VALUE;
247308 // go through all points of the way and check if one of the coords
248309 // is a labeling coord
249310 for (Coord c : polygon.getPoints()) {
250311 Integer cOrder = labelCoords.get(c);
251312 if (cOrder != null && cOrder.intValue() < poiOrder) {
252 // this coord is a labeling coord
313 // this coord is a labelling coord
253314 // use it for the current way
254315 poiCoord = c;
255316 poiOrder = cOrder;
267328 }
268329 // add tag mkgmap:cache_area_size to the original polygon so that it is copied to the POI
269330 areaSizeFunction.value(polygon);
270 Node poi = createPOI(polygon, poiCoord, AREA2POI_TAG, 0);
271 saver.addNode(poi);
331 addPOI(polygon, poiCoord, TKM_AREA2POI, 0);
272332 }
273333
274334
286346 prevC = c;
287347 }
288348
289 Node startNode = createPOI(line, line.getFirstPoint(), LINE2POI_TAG, sumDist);
290 startNode.addTag(LINE2POI_TYPE_TAG,"start");
291 saver.addNode(startNode);
292
293 Node endNode = createPOI(line, line.getLastPoint(), LINE2POI_TAG, sumDist);
294 endNode.addTag(LINE2POI_TYPE_TAG,"end");
295 saver.addNode(endNode);
296
297 int noPOIs = 2;
298 Coord lastPoint = line.getFirstPoint();
299 if (line.getPoints().size() > 2) {
300 for (Coord inPoint : line.getPoints().subList(1, line.getPoints().size()-1)) {
301 if (inPoint.equals(lastPoint)){
349 int countPOIs = 0;
350 if (poisToLinesStart) {
351 Node startNode = addPOI(line, line.getFirstPoint(), TKM_LINE2POI, sumDist);
352 startNode.addTag(TKM_LINE2POI_TYPE, "start");
353 countPOIs++;
354 }
355
356 if (poisToLinesEnd) {
357 Node endNode = addPOI(line, line.getLastPoint(), TKM_LINE2POI, sumDist);
358 endNode.addTag(TKM_LINE2POI_TYPE, "end");
359 countPOIs++;
360 }
361
362 if (poisToLinesOther && line.getPoints().size() > 2) {
363 Coord lastPoint = line.getFirstPoint();
364 for (Coord inPoint : line.getPoints().subList(1, line.getPoints().size() - 1)) {
365 if (inPoint.equals(lastPoint)) {
302366 continue;
303367 }
304368 lastPoint = inPoint;
305 Node innerNode = createPOI(line, inPoint, LINE2POI_TAG, sumDist);
306 innerNode.addTag(LINE2POI_TYPE_TAG,"inner");
307 saver.addNode(innerNode);
308 noPOIs++;
309 }
310 }
311
312 Coord midPoint = null;
313 double remMidDist = sumDist/2;
314 for (int midPos =0; midPos < dists.size(); midPos++) {
315 double nextDist = dists.get(midPos);
316 if (remMidDist <= nextDist) {
317 double frac = remMidDist/nextDist;
318 midPoint = line.getPoints().get(midPos).makeBetweenPoint(line.getPoints().get(midPos+1), frac);
319 break;
320 }
321 remMidDist -= nextDist;
322 }
323
324 if (midPoint != null) {
325 Node midNode = createPOI(line, midPoint, LINE2POI_TAG, sumDist);
326 midNode.addTag(LINE2POI_TYPE_TAG,"mid");
327 saver.addNode(midNode);
328 noPOIs++;
329 }
330 return noPOIs;
331
332 }
333
334 private static Node createPOI(Element source, Coord poiCoord, short poiTypeTagKey, double wayLength) {
369 Node innerNode = addPOI(line, inPoint, TKM_LINE2POI, sumDist);
370 innerNode.addTag(TKM_LINE2POI_TYPE, "inner");
371 countPOIs++;
372 }
373 }
374 if (poisToLinesMid) {
375 Coord midPoint = null;
376 double remMidDist = sumDist / 2;
377 for (int midPos = 0; midPos < dists.size(); midPos++) {
378 double nextDist = dists.get(midPos);
379 if (remMidDist <= nextDist) {
380 double frac = remMidDist / nextDist;
381 midPoint = line.getPoints().get(midPos).makeBetweenPoint(line.getPoints().get(midPos + 1), frac);
382 break;
383 }
384 remMidDist -= nextDist;
385 }
386
387 if (midPoint != null) {
388 Node midNode = addPOI(line, midPoint, TKM_LINE2POI, sumDist);
389 midNode.addTag(TKM_LINE2POI_TYPE, "mid");
390 countPOIs++;
391 }
392 }
393 return countPOIs;
394
395 }
396
397 /** Prefix that is added to tags which are copied from the original node. */
398 public static final String FROM_NODE_PREFIX = "mkgmap:from-node:";
399
400 private Node addPOI(Element source, Coord poiCoord, short poiTypeTagKey, double wayLength) {
335401 Node poi = new Node(source.getOriginalId(), poiCoord);
336 poi.setFakeId();
402 poi.markAsGeneratedFrom(source);
337403 poi.copyTags(source);
338404 poi.deleteTag(MultiPolygonRelation.STYLE_FILTER_TAG);
339405 poi.addTag(poiTypeTagKey, "true");
340 if (poiTypeTagKey == LINE2POI_TAG) {
341 poi.addTag(WAY_LENGTH_TAG, String.valueOf(Math.round(wayLength)));
342 }
406 if (poiTypeTagKey == TKM_LINE2POI) {
407 poi.addTag(TKM_WAY_LENGTH, String.valueOf(Math.round(wayLength)));
408 }
409
410 Node node = null;
411 if (poiCoord instanceof CoordPOI) {
412 node = ((CoordPOI) poiCoord).getNode();
413 } else {
414 node = coordToNodeMap.get(poiCoord);
415 }
416 if (node != null) {
417 // add the original tags of the node with the prefix mkgmap:from-node:
418 for (Entry<String, String> entry : node.getTagEntryIterator()) {
419 if (!entry.getKey().startsWith("mkgmap:")) {
420 poi.addTag(FROM_NODE_PREFIX + entry.getKey(), entry.getValue());
421 }
422 }
423 }
424
343425 if (log.isDebugEnabled()) {
344426 log.debug("Create POI",poi.toTagString(),"from",source.getId(),source.toTagString());
345427 }
428 saver.addNode(poi);
346429 return poi;
347430
348431 }
396479 else
397480 point = adminCentre.getLocation();
398481 }
399 if (point == null) {
400 continue;
401 }
402
403 Node poi = createPOI(r, point, AREA2POI_TAG, 0);
404 // remove the type tag which makes only sense for relations
405 poi.deleteTag("type");
406 saver.addNode(poi);
407 mps2POI++;
482
483 if (point != null) {
484 Node poi = addPOI(r, point, TKM_AREA2POI, 0);
485 // remove the type tag which makes only sense for relations
486 poi.deleteTag("type");
487 mps2POI++;
488 }
408489 }
409490 log.info(mps2POI,"POIs from multipolygons created");
410491 }
2525 private ElementSaver saver;
2626 private NameFinder nameFinder;
2727
28 public boolean init(ElementSaver saver, EnhancedProperties props) {
28 @Override
29 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
2930 this.saver = saver;
31 this.style = style;
3032 nameFinder = new NameFinder(props);
3133 return true;
3234 }
3335
34 public void setStyle(Style style){
35 this.style = style;
36 }
37
36 @Override
3837 public void end() {
3938 Rule relationRules = style.getRelationRules();
4039 for (Relation rel : saver.getRelations().values()) {
1313 package uk.me.parabola.mkgmap.reader.osm;
1414
1515 import java.util.ArrayList;
16 import java.util.Arrays;
1617 import java.util.HashSet;
1718 import java.util.List;
1819 import java.util.Set;
4041 private NameFinder nameFinder;
4142
4243 @Override
43 public boolean init(ElementSaver saver, EnhancedProperties props) {
44 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
4445 if (!props.getProperty("residential-hook", true))
4546 return false;
4647 this.nameFinder = new NameFinder(props);
7677 residentialBoundaries = null;
7778 }
7879
79 private static final short landuseTagKey = TagDict.getInstance().xlate("landuse");
80 private static final short nameTagKey = TagDict.getInstance().xlate("name");
81 private static final short styleFilterTagKey = TagDict.getInstance().xlate("mkgmap:stylefilter");
82 private static final short otherKey = TagDict.getInstance().xlate("mkgmap:other");
80 private static final short TK_LANDUSE = TagDict.getInstance().xlate("landuse");
81 private static final short TK_NAME = TagDict.getInstance().xlate("name");
82 private static final short TKM_STYLEFILTER = TagDict.getInstance().xlate("mkgmap:stylefilter");
83 private static final short TKM_OTHER = TagDict.getInstance().xlate("mkgmap:other");
8384
8485 private BoundaryQuadTree buildResidentialBoundaryTree() {
8586 List<Boundary> residentials = new ArrayList<>();
8687 Tags tags = new Tags();
8788
8889 for (Way way : saver.getWays().values()) {
89 if (way.hasIdenticalEndPoints() && "residential".equals(way.getTag(landuseTagKey))) {
90 if ("polyline".equals(way.getTag(styleFilterTagKey)))
90 if (way.hasIdenticalEndPoints() && "residential".equals(way.getTag(TK_LANDUSE))) {
91 if ("polyline".equals(way.getTag(TKM_STYLEFILTER)))
9192 continue;
9293 String name = nameFinder.getName(way);
93 tags.put(nameTagKey, name == null ? "yes": name);
94 tags.put(TK_NAME, name == null ? "yes": name);
9495 Boundary b = new Boundary(Java2DConverter.createArea(way.getPoints()), tags, "w"+way.getId());
9596 residentials.add(b);
9697 }
115116 // try the mid point of the way first
116117 int middle = way.getPoints().size() / 2;
117118 Coord loc = way.hasIdenticalEndPoints() ? way.getCofG() : way.getPoints().get(middle);
118 if (! "residential".equals(way.getTag(landuseTagKey)))
119 if (! "residential".equals(way.getTag(TK_LANDUSE)))
119120 residentialTags = residentialBoundaries.get(loc);
120121 }
121122
122123 if (residentialTags != null) {
123 elem.addTag("mkgmap:residential", residentialTags.get(otherKey));
124 elem.addTag("mkgmap:residential", residentialTags.get(TKM_OTHER));
124125 }
125126 }
126127
127128 @Override
128129 public Set<String> getUsedTags() {
129 Set<String> used = new HashSet<>();
130 used.add("landuse");
131 used.add("name");
132 return used;
130 return new HashSet<>(Arrays.asList("landuse", "name"));
133131 }
134132 }
135133
2727 */
2828 public class RoutingHook implements OsmReadingHooks {
2929
30 private final Set<String> usedTags;
30 private final Set<String> usedTags = new HashSet<>();
3131
32 public RoutingHook() {
33 usedTags = new HashSet<>();
34 usedTags.add("except");
35 usedTags.add("restriction");
36 usedTags.add("restriction:foot");
37 usedTags.add("restriction:hgv");
38 usedTags.add("restriction:motorcar");
39 usedTags.add("restriction:vehicle");
40 usedTags.add("restriction:motor_vehicle");
41 usedTags.add("restriction:bicycle");
42 usedTags.add("restriction:bus");
43 }
32 @Override
33 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
34 // only enabled if the route option is set
35 if (props.containsKey("route")) {
4436
45 public boolean init(ElementSaver saver, EnhancedProperties props) {
46
47 if (props.getProperty("old-style", false)) {
48 // the access tags need to be loaded if the old style handling
49 // is active and access restrictions are handled by the java
50 // source code and not by the style
51 usedTags.add("access");
52 usedTags.add("bicycle");
53 usedTags.add("carpool");
54 usedTags.add("delivery");
55 usedTags.add("emergency");
56 usedTags.add("foot");
57 usedTags.add("goods");
58 usedTags.add("hgv");
59 usedTags.add("motorcar");
60 usedTags.add("motorcycle");
61 usedTags.add("psv");
62 usedTags.add("route");
63 usedTags.add("taxi");
64 }
65 int admLevelNod3 = props.getProperty("add-boundary-nodes-at-admin-boundaries", 2);
66 if (admLevelNod3 > 0) {
67 usedTags.add("boundary");
68 usedTags.add("admin_level");
37 usedTags.add("except");
38 usedTags.add("restriction");
39 usedTags.add("restriction:foot");
40 usedTags.add("restriction:hgv");
41 usedTags.add("restriction:motorcar");
42 usedTags.add("restriction:vehicle");
43 usedTags.add("restriction:motor_vehicle");
44 usedTags.add("restriction:bicycle");
45 usedTags.add("restriction:bus");
46
47 if (props.getProperty("old-style", false)) {
48 // the access tags need to be loaded if the old style handling
49 // is active and access restrictions are handled by the java
50 // source code and not by the style
51 usedTags.add("access");
52 usedTags.add("bicycle");
53 usedTags.add("carpool");
54 usedTags.add("delivery");
55 usedTags.add("emergency");
56 usedTags.add("foot");
57 usedTags.add("goods");
58 usedTags.add("hgv");
59 usedTags.add("motorcar");
60 usedTags.add("motorcycle");
61 usedTags.add("psv");
62 usedTags.add("route");
63 usedTags.add("taxi");
64 }
65 int admLevelNod3 = props.getProperty("add-boundary-nodes-at-admin-boundaries", 2);
66 if (admLevelNod3 > 0) {
67 usedTags.add("boundary");
68 usedTags.add("admin_level");
69 }
70 return true;
6971 }
7072
71
72 // only enabled if the route option is set
73 return props.containsKey("route");
73 return false;
7474 }
7575
7676 @Override
117117 * the whole thing is omitted if not used.
118118 */
119119 @Override
120 public boolean init(ElementSaver saver, EnhancedProperties props) {
120 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
121121 this.saver = saver;
122122
123123 precompSea = props.getProperty("precomp-sea", null);
442442 */
443443 // create copy of way that has only the natural=coastline tag
444444 Way shore = new Way(way.getOriginalId(), way.getPoints());
445 shore.setFakeId();
445 shore.markAsGeneratedFrom(way);
446446 shore.addTag("natural", "coastline");
447447 saver.addWay(shore);
448448
467467 if (way.hasIdenticalEndPoints()){
468468 // add a copy of this way to be able to draw it as a shape
469469 Way shapeWay = new Way(way.getOriginalId(), way.getPoints());
470 shapeWay.setFakeId();
470 shapeWay.markAsGeneratedFrom(way);
471471 shapeWay.copyTags(way);
472472 // change the tag so that only special rules looking for it are firing
473473 shapeWay.deleteTag("natural");
672672 for (Way w : seaPrecompWays) {
673673 // set a new id to be sure that the precompiled ids do not
674674 // interfere with the ids of this run
675 w.setFakeId();
675 w.markAsGeneratedFrom(w);
676676
677677 if ("land".equals(w.getTag("natural"))) {
678678 landWays.add(w);
790790 wm = w1;
791791 } else {
792792 wm = new Way(w1.getOriginalId(), w1.getPoints());
793 wm.setFakeId();
793 wm.markAsGeneratedFrom(w1);
794794 beginMap.put(wm.getFirstPoint(), wm);
795795 }
796796 beginMap.remove(w2.getFirstPoint());
10001000 toBeRemoved.add(segment);
10011001 for (List<Coord> pts : clipped) {
10021002 Way shore = new Way(segment.getOriginalId(), pts);
1003 shore.setFakeId();
1003 shore.markAsGeneratedFrom(segment);
10041004 toBeAdded.add(shore);
10051005 }
10061006 }
10891089 wm = w1;
10901090 } else {
10911091 wm = new Way(w1.getOriginalId());
1092 wm.setFakeId();
1092 wm.markAsGeneratedFrom(w1);
10931093 shoreline.remove(w1);
10941094 shoreline.add(wm);
10951095 wm.getPoints().addAll(w1.getPoints());
3333 * @author Steve Ratcliffe
3434 */
3535 public interface Style {
36 public String getOption(String name);
36 String getOption(String name);
3737
38 public StyleInfo getInfo();
38 StyleInfo getInfo();
3939
4040 /**
4141 * Get the rules that apply to ways. This includes lines and polygons
4242 * as they are not separate primitives in osm. It is a merge of the line
4343 * rules and the polygon rules.
4444 */
45 public Rule getWayRules();
45 Rule getWayRules();
4646
4747 /**
4848 * Get the rules that apply to nodes.
4949 */
50 public Rule getNodeRules();
50 Rule getNodeRules();
5151
5252 /**
5353 * Get the rules that apply to lines.
5454 */
55 public Rule getLineRules();
55 Rule getLineRules();
5656
5757 /**
5858 * Get the rules that apply to polygons.
5959 */
60 public Rule getPolygonRules();
60 Rule getPolygonRules();
6161
6262 /**
6363 * Get the relation rules.
6464 */
65 public Rule getRelationRules();
65 Rule getRelationRules();
6666
6767 /**
6868 * Get the overlay definitions. Most styles will not use this.
6969 */
70 public LineAdder getOverlays(LineAdder lineAdder);
70 LineAdder getOverlays(LineAdder lineAdder);
7171
7272 /**
7373 * Get the tags that are used by this style.
7474 */
75 public Set<String> getUsedTags();
75 Set<String> getUsedTags();
76
77 /**
78 * Get the tags that are used in the points file of this style
79 */
80 Set<String> getUsedTagsPOI();
7681
7782 /**
7883 * Report statistics for rule expressions.
7984 */
80 public void reportStats();
85 void reportStats();
8186 }
2929 private String version;
3030 private String summary;
3131 private String longDescription;
32 private final List<String> baseStyleNames = new ArrayList<String>();
32 private final List<String> baseStyleNames = new ArrayList<>();
3333
3434
3535 public String getSummary() {
3838 private List<Entry<String,String>> areasToPoiNodeTags;
3939
4040 @Override
41 public boolean init(ElementSaver saver, EnhancedProperties props) {
41 public boolean init(ElementSaver saver, EnhancedProperties props, Style style) {
4242 this.saver = saver;
4343
4444 // Get the tags from the POIGeneratorHook which are used to define the point
6262 long nodes = saver.getNodes().size();
6363
6464 // go through all nodes
65 for (Node node : new ArrayList<Node>(saver.getNodes().values())) {
65 for (Node node : new ArrayList<>(saver.getNodes().values())) {
6666
6767 // nodes without tags can be removed
6868 if (node.getTagCount() == 0) {
9595
9696 Rectangle bboxRect = new Rectangle(bbox.getMinLong(), bbox.getMinLat(), bbox.getWidth(), bbox.getHeight());
9797 long ways = saver.getWays().size();
98 for (Way way : new ArrayList<Way>(saver.getWays().values())) {
98 for (Way way : new ArrayList<>(saver.getWays().values())) {
9999 if (way.isViaWay())
100100 continue;
101101 if (way.getPoints().isEmpty()) {
3535 * @author Steve Ratcliffe
3636 */
3737 public class OsmBinHandler extends OsmHandler {
38
39 public OsmBinHandler() {
40 }
4138
4239 @Override
4340 public boolean isFileSupported(String name) {
202199 Element el = null;
203200
204201 if (binRel.getTypes(j) == Osmformat.Relation.MemberType.NODE) {
205 el = saver.getNode(mid);
206 if(el == null) {
207 // we didn't make a node for this point earlier,
208 // do it now (if it exists)
209 Coord co = saver.getCoord(mid);
210 if(co != null) {
211 el = new Node(mid, co);
212 saver.addNode((Node)el);
213 }
214 }
202 el = saver.getOrCreateNode(mid);
215203 } else if (binRel.getTypes(j) == Osmformat.Relation.MemberType.WAY) {
216204 el = saver.getWay(mid);
217205 } else if (binRel.getTypes(j) == Osmformat.Relation.MemberType.RELATION) {
1111 */
1212 package uk.me.parabola.mkgmap.reader.osm.boundary;
1313
14 import uk.me.parabola.imgfmt.app.Area;
15 import uk.me.parabola.mkgmap.reader.osm.Node;
1614 import uk.me.parabola.mkgmap.reader.osm.OsmConverter;
1715 import uk.me.parabola.mkgmap.reader.osm.Relation;
1816 import uk.me.parabola.mkgmap.reader.osm.Way;
2119 public class BoundaryConverter implements OsmConverter {
2220
2321 private final BoundarySaver saver;
22
2423 public BoundaryConverter(BoundarySaver saver) {
2524 this.saver= saver;
2625 }
3332 }
3433
3534 @Override
36 public void convertNode(Node node) {
37 }
38
39 @Override
4035 public void convertRelation(Relation relation) {
4136 if (relation instanceof BoundaryRelation) {
4237 Boundary boundary = ((BoundaryRelation)relation).getBoundary();
4540 }
4641 }
4742
48 @Override
49 public void setBoundingBox(Area bbox) {
50
51 }
52
53 @Override
54 public void end() {
55 }
56
57 @Override
58 public Boolean getDriveOnLeft(){
59 return null; // unknown
60 }
61
6243 }
2020 import java.util.ListIterator;
2121 import java.util.Queue;
2222 import java.util.Set;
23 import java.util.concurrent.Callable;
2423 import java.util.concurrent.ExecutionException;
2524 import java.util.concurrent.ExecutorCompletionService;
2625 import java.util.concurrent.ExecutorService;
4645 }
4746
4847 private static void saveArea(String attribute, Integer level, Area covered) {
49 String gpxBasename = "gpx/summary/" + attribute + "/admin_level="
50 + level;
48 String gpxBasename = "gpx/summary/" + attribute + "/admin_level=" + level;
5149
5250 List<List<Coord>> coveredPolys = Java2DConverter.areaToShapes(covered);
5351 Collections.reverse(coveredPolys);
6260 public static void main(String[] args) throws InterruptedException {
6361 int processors = Runtime.getRuntime().availableProcessors();
6462 ExecutorService excSvc = Executors.newFixedThreadPool(processors);
65 ExecutorCompletionService<Area> executor = new ExecutorCompletionService<Area>(
66 excSvc);
63 ExecutorCompletionService<Area> executor = new ExecutorCompletionService<>(excSvc);
6764 String workDirName = args[0];
6865 System.out.println(workDirName);
6966 File boundaryDir = new File(workDirName);
70 final Set<String> boundsFileNames = new HashSet<String>();
67 final Set<String> boundsFileNames = new HashSet<>();
7168 if (boundaryDir.isFile() && boundaryDir.getName().endsWith(".bnd")) {
7269 workDirName = boundaryDir.getParent();
7370 if (workDirName == null)
8582 for (String fileName : boundsFileNames) {
8683 String[] parts = fileName.substring("bounds_".length(),
8784 fileName.length() - 4).split("_");
88 int lat = Integer.valueOf(parts[0]);
89 int lon = Integer.valueOf(parts[1]);
85 int lat = Integer.parseInt(parts[0]);
86 int lon = Integer.parseInt(parts[1]);
9087 if (lat < minLat)
9188 minLat = lat;
9289 if (lat > maxLat)
9693 if (lon > maxLon)
9794 maxLon = lon;
9895 }
99 System.out.format("Covered area: (%d,%d)-(%d,%d)\n", minLat, minLon,
100 maxLat, maxLon);
96 System.out.format("Covered area: (%d,%d)-(%d,%d)%n", minLat, minLon, maxLat, maxLon);
10197 int maxSteps = 2;
10298
10399 final String boundaryDirName = workDirName;
104100 for (int adminlevel = 2; adminlevel < 12; adminlevel++) {
105 final Set<String> boundaryFileNames = Collections.synchronizedSet(new HashSet<String>(boundsFileNames));
101 final Set<String> boundaryFileNames = Collections.synchronizedSet(new HashSet<>(boundsFileNames));
106102 final int adminLevel = adminlevel;
107 final Queue<Future<Area>> areas = new LinkedBlockingQueue<Future<Area>>();
108 for (int lat = minLat; lat <= maxLat; lat += maxSteps
109 * BoundaryUtil.RASTER) {
110 for (int lon = minLon; lon <= maxLon; lon += maxSteps
111 * BoundaryUtil.RASTER) {
103 final Queue<Future<Area>> areas = new LinkedBlockingQueue<>();
104 for (int lat = minLat; lat <= maxLat; lat += maxSteps * BoundaryUtil.RASTER) {
105 for (int lon = minLon; lon <= maxLon; lon += maxSteps * BoundaryUtil.RASTER) {
112106 for (int latStep = 0; latStep < maxSteps
113107 && lat + latStep * BoundaryUtil.RASTER <= maxLat; latStep++) {
114108 for (int lonStep = 0; lonStep < maxSteps
115109 && lon + lonStep * BoundaryUtil.RASTER <= maxLon; lonStep++) {
116 final int fLat = lat + latStep
117 * BoundaryUtil.RASTER;
118 final int fLon = lon + lonStep
119 * BoundaryUtil.RASTER;
110 final int fLat = lat + latStep * BoundaryUtil.RASTER;
111 final int fLon = lon + lonStep * BoundaryUtil.RASTER;
120112
121 areas.add(executor.submit(new Callable<Area>() {
122 public Area call() {
123 String filename = "bounds_" + fLat + "_"
124 + fLon + ".bnd";
125 if (boundaryFileNames.contains(filename) == false) {
126 return new Area();
127 }
128 BoundaryCoverageUtil converter = new BoundaryCoverageUtil(
129 boundaryDirName, filename);
130 boundaryFileNames.remove(filename);
131 System.out.format("%5d bounds files remaining\n", boundaryFileNames.size());
132 return converter.getCoveredArea(adminLevel);
113 areas.add(executor.submit(() -> {
114 String filename = "bounds_" + fLat + "_" + fLon + ".bnd";
115 if (!boundaryFileNames.contains(filename)) {
116 return new Area();
133117 }
118 BoundaryCoverageUtil converter = new BoundaryCoverageUtil(boundaryDirName, filename);
119 boundaryFileNames.remove(filename);
120 System.out.format("%5d bounds files remaining%n", boundaryFileNames.size());
121 return converter.getCoveredArea(adminLevel);
134122 }));
135123 }
136124 }
139127
140128 final AtomicInteger mergeSteps = new AtomicInteger();
141129 while (areas.size() > 1) {
142 final List<Future<Area>> toMerge = new ArrayList<Future<Area>>();
143 for (int i = 0; i < maxSteps * 2 && areas.isEmpty() == false; i++) {
130 final List<Future<Area>> toMerge = new ArrayList<>();
131 for (int i = 0; i < maxSteps * 2 && !areas.isEmpty(); i++) {
144132 toMerge.add(areas.poll());
145133 }
146134 mergeSteps.incrementAndGet();
147 areas.add(executor.submit(new Callable<Area>() {
148 public Area call() {
149 Area a = new Area();
150 ListIterator<Future<Area>> mergeAreas = toMerge
151 .listIterator();
152 while (mergeAreas.hasNext()) {
153 try {
154 a.add(mergeAreas.next().get());
155 } catch (InterruptedException exp) {
156 System.err.println(exp);
157 } catch (ExecutionException exp) {
158 System.err.println(exp);
159 }
160 mergeAreas.remove();
135 areas.add(executor.submit(() -> {
136 Area a = new Area();
137 ListIterator<Future<Area>> mergeAreas = toMerge.listIterator();
138 while (mergeAreas.hasNext()) {
139 try {
140 a.add(mergeAreas.next().get());
141 } catch (InterruptedException exp1) {
142 System.err.println(exp1);
143 } catch (ExecutionException exp2) {
144 System.err.println(exp2);
161145 }
162 System.out.format("%5d merges remaining\n",mergeSteps.decrementAndGet());
163 return a;
146 mergeAreas.remove();
164147 }
148 System.out.format("%5d merges remaining%n",mergeSteps.decrementAndGet());
149 return a;
165150 }));
166151 }
167152 try {
171156 } catch (Exception exp) {
172157 System.err.println(exp);
173158 }
174 // }
175159 }
176160 excSvc.shutdown();
177161 }
9494 int bAll = bounds1.size() + bounds2.size();
9595 long tProgress = System.currentTimeMillis();
9696
97 while (bounds1.isEmpty() == false || bounds2.isEmpty() == false) {
97 while (!bounds1.isEmpty() || !bounds2.isEmpty()) {
9898 String f1 = bounds1.peek();
9999 String f2 = bounds2.peek();
100100
109109 if (cmp == 0) {
110110 Area a1 = loadArea(inputName1, f1, tag, value);
111111 Area a2 = loadArea(inputName2, f2, tag, value);
112 if (a1.isEmpty() == false|| a2.isEmpty() == false){
112 if (!a1.isEmpty() || !a2.isEmpty()){
113113 Area o1 = new Area(a1);
114114 o1.subtract(a2);
115 if (o1.isEmpty() == false)
115 if (!o1.isEmpty())
116116 only1.add(o1);
117117 Area o2 = new Area(a2);
118118 o2.subtract(a1);
119 if (o2.isEmpty() == false)
119 if (!o2.isEmpty())
120120 only2.add(o2);
121121 }
122122 bounds1.poll();
164164 dir = "."; // the local directory
165165 }
166166 BoundaryQuadTree bqt = BoundaryUtil.loadQuadTree(dir, bndFileName);
167 if (tag.equals("admin_level"))
167 if ("admin_level".equals(tag))
168168 return (bqt.getCoveredArea(Integer.valueOf(value)));
169169 Map<String, Tags> bTags = bqt.getTagsMap();
170170 Map<String, List<Area>> areas = bqt.getAreas();
228228 if (args.length < 2)
229229 printUsage();
230230 File f1 = new File(args[0]);
231 if (f1.exists() == false){
231 if (!f1.exists()) {
232232 System.err.println(args[0] + " does not exist");
233233 printUsage();
234234 }
235235 File f2 = new File(args[1]);
236 if (f2.exists() == false){
236 if (!f2.exists()) {
237237 System.err.println(args[1] + " does not exist");
238238 printUsage();
239239 }
2525
2626 public BoundaryElement(boolean outer, List<Coord> points) {
2727 this.outer = outer;
28 this.points = new ArrayList<Coord>(points);
28 this.points = new ArrayList<>(points);
2929 }
3030
3131 public Area getArea() {
5555 } else if (element instanceof Way) {
5656 Way w = (Way) element;
5757 // a single way must be closed
58 if (w.isClosedInOSM() == false) {
58 if (!w.isClosedInOSM()) {
5959 return false;
6060 }
6161 } else {
101101 break;
102102 }
103103 }
104 if (found == false){
104 if (!found) {
105105 System.out.println("No boundary with admin_level=" + adminlevel
106106 + " found.");
107107 continue;
156156 boundaryDirName = boundaryDir.getParent();
157157 if (boundaryDirName == null)
158158 boundaryDirName = ".";
159 boundaryFileNames = new ArrayList<String>();
159 boundaryFileNames = new ArrayList<>();
160160 boundaryFileNames.add(boundaryDir.getName());
161161 } else {
162162 boundaryFileNames = BoundaryUtil.getBoundaryDirContent(boundaryDirName);
7777 int gridLat = (co.getLatitude() - minLat) / BoundaryUtil.RASTER;
7878 int gridLon = (co.getLongitude() - minLon) / BoundaryUtil.RASTER;
7979 if (grid[gridLat][gridLon] == null){
80 if (emptyMessagePrinted[gridLat][gridLon] == false){
80 if (!emptyMessagePrinted[gridLat][gridLon]) {
8181 emptyMessagePrinted[gridLat][gridLon] = true;
8282 int keyLat = BoundaryUtil.getSplitBegin(co.getLatitude());
8383 int keyLon = BoundaryUtil.getSplitBegin(co.getLongitude());
2424 File outDir = new File (outDirName);
2525
2626 if (outDir.exists() ){
27 if (outDir.isDirectory() == false){
27 if (!outDir.isDirectory()) {
2828 System.err.println("target is not a directory, output is written to bounds.txt");
2929 outDir = new File(".");
3030 }
4141 break;
4242 Map<String, Tags> map = bqt.getTagsMap();
4343 for ( Entry<String, Tags> entry: map.entrySet()) {
44 TreeMap<String,String> btree = new TreeMap<String, String>();
44 TreeMap<String,String> btree = new TreeMap<>();
4545 String line = bndFile+ ":" + entry.getKey();
4646 Iterator<Entry<String,String>> tagIter = entry.getValue().entryIterator();
4747 while (tagIter.hasNext()) {
1313
1414 import java.util.HashMap;
1515 import java.util.List;
16 import java.util.Map;
1617 import java.util.regex.Pattern;
1718
1819 import uk.me.parabola.log.Logger;
2122 import uk.me.parabola.mkgmap.reader.osm.Element;
2223 import uk.me.parabola.mkgmap.reader.osm.TagDict;
2324 import uk.me.parabola.mkgmap.reader.osm.Tags;
24 import uk.me.parabola.mkgmap.reader.osm.boundary.Boundary;
2525 import uk.me.parabola.util.EnhancedProperties;
2626
2727 /**
8383 * @param boundaries list of boundaries
8484 * @return A Map that maps boundary Ids to the location relevant tags
8585 */
86 public HashMap<String, BoundaryLocationInfo> getPreparedLocationInfo(
86 public Map<String, BoundaryLocationInfo> getPreparedLocationInfo(
8787 List<Boundary> boundaries) {
88 HashMap<String, BoundaryLocationInfo> preparedLocationInfo = new HashMap<String, BoundaryLocationInfo> ();
88 HashMap<String, BoundaryLocationInfo> preparedLocationInfo = new HashMap<> ();
8989 if (boundaries != null){
9090 for (Boundary b :boundaries){
9191 preparedLocationInfo.put(b.getId(), parseTags(b.getTags()));
100100 * @return a name or null if no usable name tag was found
101101 */
102102 private String getName(Tags tags) {
103 if ("2".equals(tags.get(admin_levelTagKey))) {
103 if ("2".equals(tags.get(TK_ADMIN_LEVEL))) {
104104 // admin_level=2 boundaries. They need to be handled special because their name is changed
105105 // to the 3 letter ISO code using the Locator class and the LocatorConfig.xml file.
106106 for (short nameTagKey : Locator.PREFERRED_NAME_TAG_KEYS) {
123123 return null;
124124 }
125125
126 private static final short postal_codeTagKey = TagDict.getInstance().xlate("postal_code");
127 private static final short boundaryTagKey = TagDict.getInstance().xlate("boundary");
126 private static final short TK_POSTAL_CODE = TagDict.getInstance().xlate("postal_code");
127 private static final short TK_BOUNDARY = TagDict.getInstance().xlate("boundary");
128128 /**
129129 * Try to extract a zip code from the the tags of a boundary.
130130 * @param tags the boundary tags
131131 * @return null if no zip code was found, else a String that should be a zip code.
132132 */
133133 private String getZip(Tags tags) {
134 String zip = tags.get(postal_codeTagKey);
135 if (zip == null) {
136 if ("postal_code".equals(tags.get(boundaryTagKey))){
137 // unlikely
138 String name = tags.get("name");
139 if (name == null) {
140 name = getName(tags);
141 }
142 if (name != null) {
143 String[] nameParts = name.split(Pattern.quote(" "));
144 if (nameParts.length > 0) {
145 zip = nameParts[0].trim();
146 }
134 String zip = tags.get(TK_POSTAL_CODE);
135 if (zip == null && "postal_code".equals(tags.get(TK_BOUNDARY))){
136 // unlikely
137 String name = tags.get("name");
138 if (name == null) {
139 name = getName(tags);
140 }
141 if (name != null) {
142 String[] nameParts = name.split(Pattern.quote(" "));
143 if (nameParts.length > 0) {
144 zip = nameParts[0].trim();
147145 }
148146 }
149147 }
151149 }
152150
153151 public static final int UNSET_ADMIN_LEVEL = 100; // must be higher than real levels
154 private static final short admin_levelTagKey = TagDict.getInstance().xlate("admin_level");
152 private static final short TK_ADMIN_LEVEL = TagDict.getInstance().xlate("admin_level");
155153 /**
156154 * translate the admin_level tag to an integer.
157155 * @param tags the boundary tags
159157 * the conversion failed.
160158 */
161159 private static int getAdminLevel(Tags tags) {
162 if ("administrative".equals(tags.get(boundaryTagKey))) {
163 String level = tags.get(admin_levelTagKey);
160 if ("administrative".equals(tags.get(TK_BOUNDARY))) {
161 String level = tags.get(TK_ADMIN_LEVEL);
164162 if (level != null) {
165163 try {
166164 int res = Integer.parseInt(level);
4141 try (FileChannel in = (new FileInputStream(file)).getChannel();
4242 FileChannel out = (new FileOutputStream(new File(to, filename))).getChannel()) {
4343 in.transferTo(0, file.length(), out);
44 in.close();
45 out.close();
4644 } catch (IOException exp) {
4745 System.err.println(exp);
4846 }
6159 BoundarySaver bSave = new BoundarySaver(targetDir, BoundarySaver.QUADTREE_DATA_FORMAT);
6260 bSave.setCreateEmptyFiles(false);
6361
64 List<File> copy = new ArrayList<File>();
62 List<File> copy = new ArrayList<>();
6563
6664 int processed = 0;
6765 int all = fl1.size()+fl2.size();
129127 File merge = new File(args[2]);
130128
131129 // TODO: maybe allow zip as input
132 if (b1.exists() == false || b1.isDirectory() == false) {
130 if (!b1.exists() || !b1.isDirectory()) {
133131 System.err.println(b1 + " does not exist or is not a directory");
134132 return;
135133 }
136134
137 if (b2.exists() == false || b2.isDirectory() == false) {
135 if (!b2.exists() || !b2.isDirectory()) {
138136 System.err.println(b2 + " does not exist or is not a directory");
139137 return;
140138 }
141139
142 if (merge.exists() && merge.isDirectory() == false) {
140 if (merge.exists() && !merge.isDirectory()) {
143141 System.err.println(merge + " is not a directory");
144142 }
145143
146 if (merge.exists() == false) {
144 if (!merge.exists()) {
147145 merge.mkdirs();
148146 }
149147
109109
110110
111111 public static void main(String[] args) {
112 if (args[0].equals("--help") || args.length != 2) {
112 if ("--help".equals(args[0]) || args.length != 2) {
113113 System.err.println("Usage:");
114114 System.err.println("java -cp mkgmap.jar uk.me.parabola.mkgmap.reader.osm.boundary.BoundaryPreprocessor <inputfile> <boundsdir>");
115115 System.err.println(" <inputfile>: File containing boundary data (OSM, PBF or O5M format)");
6262
6363 // maps the "normal" tags of the boundaries that are saved in this tree to
6464 // the boundaryId
65 private final HashMap<String, Tags> boundaryTags = new LinkedHashMap<>();
65 private final Map<String, Tags> boundaryTags = new LinkedHashMap<>();
6666 // maps the location relevant info to the boundaryId
67 private final HashMap<String, BoundaryLocationInfo> preparedLocationInfo;
67 private final Map<String, BoundaryLocationInfo> preparedLocationInfo;
6868 // property controlled preparer
6969 private final BoundaryLocationPreparer preparer;
7070
141141 return;
142142 }
143143
144 Queue<PolygonStatus> polygonWorkingQueue = new LinkedBlockingQueue<PolygonStatus>();
144 Queue<PolygonStatus> polygonWorkingQueue = new LinkedBlockingQueue<>();
145145 BitSet nestedOuterPolygons = new BitSet();
146146 BitSet nestedInnerPolygons = new BitSet();
147147
255255 }
256256 outmostPolygonProcessing = false;
257257 } else {
258 for (String tag : new ArrayList<String>(outerTags.keySet())) {
259 if (outerTags.get(tag).equals(outerWay.getTag(tag)) == false) {
258 for (String tag : new ArrayList<>(outerTags.keySet())) {
259 if (!outerTags.get(tag).equals(outerWay.getTag(tag))) {
260260 outerTags.remove(tag);
261261 }
262262 }
311311 List<JoinedWay> unclosed = new ArrayList<>();
312312
313313 for (JoinedWay w : allWays) {
314 if (w.hasIdenticalEndPoints() == false) {
314 if (!w.hasIdenticalEndPoints()) {
315315 unclosed.add(w);
316316 }
317317 }
8989
9090 public BoundarySaver(File boundaryDir, String mode) {
9191 this.boundaryDir = boundaryDir;
92 if (boundaryDir.exists() && boundaryDir.isDirectory() == false){
92 if (boundaryDir.exists() && !boundaryDir.isDirectory()) {
9393 log.error("output target exists and is not a directory");
9494 System.exit(-1);
9595 }
189189 }
190190
191191 private void openStream(StreamInfo streamInfo, boolean newFile) {
192 if (streamInfo.file.getParentFile().exists() == false
192 if (!streamInfo.file.getParentFile().exists()
193193 && streamInfo.file.getParentFile() != null)
194194 streamInfo.file.getParentFile().mkdirs();
195195 FileOutputStream fileStream = null;
248248 + ".bnd");
249249 streams.put(filekey, stream);
250250 openStream(stream, true);
251 } else if (stream.isOpen() == false) {
251 } else if (!stream.isOpen()) {
252252 openStream(stream, false);
253253 }
254254 }
377377 * @param area the area (can be non-singular)
378378 * @throws IOException
379379 */
380 @SuppressWarnings("fallthrough")
380381 public static void writeArea(DataOutputStream dos, Shape area) throws IOException{
381382 double[] res = new double[6];
382383 double[] lastRes = new double[2];
106106 return bElements;
107107
108108 // result is not usable if first element is not outer
109 if (tryAgain == false){
109 if (!tryAgain){
110110 // cannot convert this area
111111 log.error(" first element is not outer. "+ bElements.get(0));
112112
458458 // 1st read the mkgmap release the boundary file is created by
459459 String mkgmapRel = "?";
460460 String firstId = inpStream.readUTF();
461 if ("BND".equals(firstId) == false){
461 if (!"BND".equals(firstId)) {
462462 throw new FormatException("Unsupported boundary data type "+firstId);
463463 }
464464
589589
590590 if ("boundary".equals(type) || "multipolygon".equals(type)) {
591591 String boundaryVal = b.getTags().get("boundary");
592 if ("administrative".equals(boundaryVal) == false)
592 if (!"administrative".equals(boundaryVal))
593593 return false;
594594 // for boundary=administrative the admin_level must be set
595595 if (b.getTags().get("admin_level") == null) {
710710 * @return a map with the divided shapes
711711 */
712712 public static Map<String, Shape> rasterArea(Area areaToSplit) {
713 return rasterShape(areaToSplit, new HashMap<String, Shape>());
713 return rasterShape(areaToSplit, new HashMap<>());
714714 }
715715
716716 /**
274274 lastRef[refType] += deltaRef;
275275 long memId = lastRef[refType];
276276 if (refType == 0) {
277 el = saver.getNode(memId);
278 if (el == null) {
279 // we didn't make a node for this point earlier,
280 // do it now (if it exists)
281 Coord co = saver.getCoord(memId);
282 if (co != null) {
283 el = new Node(memId, co);
284 saver.addNode((Node) el);
285 }
286 }
277 el = saver.getOrCreateNode(memId);
287278 } else if (refType == 1) {
288279 el = saver.getWay(memId);
289280 } else if (refType == 2) {
281281 if ("way".equals(type)){
282282 el = saver.getWay(id);
283283 } else if ("node".equals(type)) {
284 el = saver.getNode(id);
285 if(el == null) {
286 // we didn't make a node for this point earlier,
287 // do it now (if it exists)
288 Coord co = saver.getCoord(id);
289 if(co != null) {
290 el = new Node(id, co);
291 saver.addNode((Node)el);
292 }
293 }
284 el = saver.getOrCreateNode(id);
294285 } else if ("relation".equals(type)) {
295286 el = saver.getRelation(id);
296287 if (el == null) {
4545 {
4646 private static final Logger log = Logger.getLogger(OverviewMapDataSource.class);
4747
48 private final List<String> copyrights = new ArrayList<String>();
48 private final List<String> copyrights = new ArrayList<>();
4949 LevelInfo[] levels = null;
5050 private Path2D tileAreaPath = new Path2D.Double();
5151
614614 }
615615
616616 private void addLineString (String value, boolean close) {
617 List<List<Coord>> lists = lineStringMap.get(currentLevel);
618 if (lists == null) {
619 lists = new ArrayList<>();
620 lineStringMap.put(currentLevel, lists);
621 }
622 lists.add(coordsFromString(value, close));
617 lineStringMap.computeIfAbsent(currentLevel, k -> new ArrayList<>()).add(coordsFromString(value, close));
623618 }
624619
625620 private boolean isCommonValue(MapElement elem, String name, String value) {
115115 shape.setMinResolution(10);
116116 shape.setName("background");
117117
118 List<Coord> coords = new ArrayList<Coord>();
118 List<Coord> coords = new ArrayList<>();
119119
120120 Coord co = new Coord(startLat, startLong);
121121 coords.add(co);
209209
210210 double baseLat = lat + y * ELEMENT_SPACING;
211211 double baseLong = lon + x * ELEMENT_SPACING;
212 List<Coord> coords = new ArrayList<Coord>();
212 List<Coord> coords = new ArrayList<>();
213213
214214 Coord co = new Coord(baseLat, baseLong);
215215 coords.add(co);
253253 double baseLat = lat + y * ELEMENT_SPACING;
254254 double baseLong = lon + x * ELEMENT_SPACING;
255255
256 List<Coord> coords = new ArrayList<Coord>();
256 List<Coord> coords = new ArrayList<>();
257257
258258 Coord co = new Coord(baseLat, baseLong);
259259 //pg.addCoord(co);
111111 double baseLat = startLat + y * ELEMENT_SPACING;
112112 double baseLong = startLong + x * ELEMENT_SPACING;
113113
114 List<Coord> coords = new ArrayList<Coord>();
114 List<Coord> coords = new ArrayList<>();
115115
116116 for (int i = 0; i < 5; i++) {
117117 Coord co = new Coord(baseLat + i * ELEMENT_SIZE, baseLong + i * ELEMENT_SIZE);
207207 continue;
208208 }
209209 w.deleteTag(MultiPolygonRelation.STYLE_FILTER_TAG);
210 w.deleteTag(MultiPolygonRelation.MP_CREATED_TAG_KEY);
210 w.deleteTag(MultiPolygonRelation.TKM_MP_CREATED);
211211 ways.add(w);
212212 }
213213 }
215215
216216 try {
217217 // forward the ways to the queue of the saver thread
218 saveQueue.put(new SimpleEntry<String, List<Way>>(mergeData.getKey(), ways));
218 saveQueue.put(new SimpleEntry<>(mergeData.getKey(), ways));
219219 } catch (InterruptedException exp) {
220220 exp.printStackTrace();
221221 }
3535 * @author Steve Ratcliffe
3636 */
3737 public class CommonSection {
38 private static final Set<String> seen = new HashSet<String>();
38 private static final Set<String> seen = new HashSet<>();
3939 protected final TypData data;
4040 private boolean hasXpm;
4141
5050 * @return True if this routine has processed the tag.
5151 */
5252 protected boolean commonKey(TokenScanner scanner, TypElement current, String name, String value) {
53 if (name.equalsIgnoreCase("Type")) {
53 if ("Type".equalsIgnoreCase(name)) {
5454 try {
5555 int ival = Integer.decode(value);
5656 if (ival >= 0x100) {
6363 throw new SyntaxException(scanner, "Bad number " + value);
6464 }
6565
66 } else if (name.equalsIgnoreCase("SubType")) {
66 } else if ("SubType".equalsIgnoreCase(name)) {
6767 try {
6868 int ival = Integer.decode(value);
6969 current.setSubType(ival);
7878 throw new SyntaxException(scanner, "Bad number in " + value);
7979 }
8080
81 } else if (name.equalsIgnoreCase("Xpm")) {
81 } else if ("Xpm".equalsIgnoreCase(name)) {
8282 Xpm xpm = readXpm(scanner, value, current.simpleBitmap());
8383 current.setXpm(xpm);
8484
85 } else if (name.equalsIgnoreCase("FontStyle")) {
85 } else if ("FontStyle".equalsIgnoreCase(name)) {
8686 int font = decodeFontStyle(value);
8787 current.setFontStyle(font);
8888
89 } else if (name.equalsIgnoreCase("CustomColor") || name.equals("ExtendedLabels")) {
89 } else if ("CustomColor".equalsIgnoreCase(name) || "ExtendedLabels".equals(name)) {
9090 // These are just noise, the appropriate flag is set if any feature is used.
9191
92 } else if (name.equalsIgnoreCase("DaycustomColor")) {
92 } else if ("DaycustomColor".equalsIgnoreCase(name)) {
9393 current.setDayFontColor(value);
9494
95 } else if (name.equalsIgnoreCase("NightcustomColor")) {
95 } else if ("NightcustomColor".equalsIgnoreCase(name)) {
9696 current.setNightCustomColor(value);
9797
98 } else if (name.equalsIgnoreCase("Comment")) {
98 } else if ("Comment".equalsIgnoreCase(name)) {
9999 // a comment that is ignored.
100100 } else {
101101 return false;
105105 }
106106
107107 protected int decodeFontStyle(String value) {
108 if (value.startsWith("NoLabel") || value.equalsIgnoreCase("nolabel")) {
108 if (value.startsWith("NoLabel") || "nolabel".equalsIgnoreCase(value)) {
109109 return 1;
110 } else if (value.equalsIgnoreCase("SmallFont") || value.equalsIgnoreCase("Small")) {
110 } else if ("SmallFont".equalsIgnoreCase(value) || "Small".equalsIgnoreCase(value)) {
111111 return 2;
112 } else if (value.equalsIgnoreCase("NormalFont") || value.equalsIgnoreCase("Normal")) {
112 } else if ("NormalFont".equalsIgnoreCase(value) || "Normal".equalsIgnoreCase(value)) {
113113 return 3;
114 } else if (value.equalsIgnoreCase("LargeFont") || value.equalsIgnoreCase("Large")) {
114 } else if ("LargeFont".equalsIgnoreCase(value) || "Large".equalsIgnoreCase(value)) {
115115 return 4;
116 } else if (value.equalsIgnoreCase("Default")) {
116 } else if ("Default".equalsIgnoreCase(value)) {
117117 return 0;
118118 } else {
119119 warnUnknown("font value " + value);
131131 * @param header The string containing the xpm header and other extended data provided on the
132132 * same line.
133133 */
134 private void parseXpmHeader(TokenScanner scanner, ColourInfo info, String header) {
134 private static void parseXpmHeader(TokenScanner scanner, ColourInfo info, String header) {
135135 TokenScanner s2 = new TokenScanner("string", new StringReader(header));
136136
137137 if (s2.checkToken("\""))
172172 if (colour.charAt(0) == '#') {
173173 colour = scanner.nextValue();
174174 colourInfo.addColour(colourTag, new Rgb(colour));
175 } else if (colour.equalsIgnoreCase("none")) {
175 } else if ("none".equalsIgnoreCase(colour)) {
176176 colourInfo.addTransparent(colourTag);
177177 } else {
178178 throw new SyntaxException(scanner, "Unrecognised colour: " + colour);
190190 * Get any keywords that are on the end of the colour line. Must not step
191191 * over the new line boundary.
192192 */
193 private void readExtraColourInfo(TokenScanner scanner, AlphaAdder colour) {
193 private static void readExtraColourInfo(TokenScanner scanner, AlphaAdder colour) {
194194 while (!scanner.isEndOfFile()) {
195195 Token tok = scanner.nextRawToken();
196196 if (tok.isEol())
328328 * by quotes. The can be trailing attribute that sets the opacity of
329329 * the final pixel.
330330 */
331 private int readTrueImageLine(TokenScanner scanner, final int[] image, int count) {
331 private static int readTrueImageLine(TokenScanner scanner, final int[] image, int count) {
332332 do {
333333 scanner.validateNext("#");
334334 String col = scanner.nextValue();
3131 * There is only one tag in this section.
3232 */
3333 public void processLine(TokenScanner scanner, String name, String value) {
34 if (!name.equalsIgnoreCase("Type"))
34 if (!"Type".equalsIgnoreCase(name))
3535 throw new SyntaxException(scanner, "Unrecognised keyword in draw order section: " + name);
3636
3737 String[] typeDrawOrder = value.split(",",-1);
2929 }
3030
3131 public void processLine(TokenScanner scanner, String name, String value) {
32 if (name.equalsIgnoreCase("String")) {
32 if ("String".equalsIgnoreCase(name)) {
3333 // There is only one string and it doesn't have a language prefix.
3434 // But if it does we will just ignore it.
3535 current.addLabel(value);
3939 if (commonKey(scanner, current, name, value))
4040 return;
4141
42 if (name.equalsIgnoreCase("IconXpm")) {
42 if ("IconXpm".equalsIgnoreCase(name)) {
4343 Xpm xpm = readXpm(scanner, value, current.simpleBitmap());
4444 current.addIcon(xpm);
4545 } else {
4747 ival = -1;
4848 }
4949
50 if (name.equalsIgnoreCase("FID")) {
50 if ("FID".equalsIgnoreCase(name)) {
5151 if (ival != -1)
5252 data.setFamilyId(ival);
53 } else if (name.equalsIgnoreCase("ProductCode")) {
53 } else if ("ProductCode".equalsIgnoreCase(name)) {
5454 if (ival != -1)
5555 data.setProductId(ival);
56 } else if (name.equalsIgnoreCase("CodePage")) {
56 } else if ("CodePage".equalsIgnoreCase(name)) {
5757 if (ival != -1 && data.getSort() == null) // ignore if --code-page
5858 data.setSort(SrtTextReader.sortForCodepage(ival));
5959 } else {
3737 if (commonKey(scanner, current, name, value))
3838 return;
3939
40 if (name.equalsIgnoreCase("UseOrientation")) {
40 if ("UseOrientation".equalsIgnoreCase(name)) {
4141 current.setUseOrientation(value.charAt(0) == 'Y');
42 } else if (name.equalsIgnoreCase("LineWidth")) {
42 } else if ("LineWidth".equalsIgnoreCase(name)) {
4343 try {
4444 int ival = Integer.decode(value);
4545 current.setLineWidth(ival);
4646 } catch (NumberFormatException e) {
4747 throw new SyntaxException(scanner, "Bad number for line width: " + value);
4848 }
49 } else if (name.equalsIgnoreCase("BorderWidth")) {
49 } else if ("BorderWidth".equalsIgnoreCase(name)) {
5050 try {
5151 int ival = Integer.decode(value);
5252 current.setBorderWidth(ival);
3333 if (commonKey(scanner, current, name, value))
3434 return;
3535
36 if (name.equalsIgnoreCase("DayXpm")) {
36 if ("DayXpm".equalsIgnoreCase(name)) {
3737 Xpm xpm = readXpm(scanner, value, current.simpleBitmap());
3838 current.setXpm(xpm);
3939
40 } else if (name.equalsIgnoreCase("NightXpm")) {
40 } else if ("NightXpm".equalsIgnoreCase(name)) {
4141 Xpm xpm = readXpm(scanner, value, current.simpleBitmap());
4242 current.setNightXpm(xpm);
4343
7272 String name = tok.getValue();
7373
7474 String sep = scanner.nextValue();
75 if (!sep.equals("=") && !sep.equals(":"))
75 if (!"=".equals(sep) && !":".equals(sep))
7676 throw new SyntaxException(scanner, "Expecting '=' or ':' instead of " + sep);
7777
7878 String value = scanner.readLine();
154154 * @return the resultList
155155 */
156156 public void get(Area bbox, Set<Element> resultSet) {
157 if (isEmpty() || bbox.intersects(bounds) == false) {
157 if (isEmpty() || !bbox.intersects(bounds)) {
158158 return;
159159 }
160160 if (elementList != null) {
1818 public static String getGpxBaseName() {
1919 String tilePath = (log.threadTag() == null ? "unknown" : log.threadTag());
2020
21 int tilenameStart = tilePath.lastIndexOf("/");
21 int tilenameStart = tilePath.lastIndexOf('/');
2222 // check the case if the tiles are defined without path
2323 tilenameStart = (tilenameStart < 0 ? 0 : tilenameStart+1);
2424
141141 }
142142 }
143143
144 if (polygonpoints != null && polygonpoints.isEmpty() == false) {
144 if (polygonpoints != null && !polygonpoints.isEmpty()) {
145145 pw.print("<trk><name>");
146146 pw.print(fname);
147147 pw.print("</name><trkseg>");
1212
1313 package uk.me.parabola.util;
1414
15 import java.util.ArrayList;
1615 import java.util.Collections;
1716 import java.util.IdentityHashMap;
1817 import java.util.LinkedList;
2423 /**
2524 * the empty list to be returned when there is key without values.
2625 */
27 private final List<V> emptyList = Collections.unmodifiableList(new ArrayList<V>(0));
28
26
2927 /**
3028 * Returns the list of values associated with the given key.
3129 *
3533 */
3634 public List<V> get(Object key) {
3735 List<V> result = super.get(key);
38 return result == null ? emptyList : result;
36 return result == null ? Collections.emptyList() : result;
3937 }
4038
4139
4240 public V add(K key, V value ) {
4341 List<V> values = super.get(key);
4442 if (values == null ) {
45 values = new LinkedList<V>();
43 values = new LinkedList<>();
4644 super.put( key, values );
4745 }
4846
3838 }
3939
4040 public List<Coord> get(Area bbox) {
41 return root.get(bbox, new ArrayList<Coord>(2000));
41 return root.get(bbox, new ArrayList<>(2000));
4242 }
4343
4444 public List<Coord> get(Collection<List<Coord>> polygons) {
45 return root.get(new QuadTreePolygon(polygons), new ArrayList<Coord>(2000));
45 return root.get(new QuadTreePolygon(polygons), new ArrayList<>(2000));
4646 }
4747
4848 public List<Coord> get(List<Coord> polygon) {
5656 if (!polygon.get(0).equals(polygon.get(polygon.size() - 1))) {
5757 throw new IllegalArgumentException("polygon is not closed");
5858 }
59 List<Coord> points = root.get(new QuadTreePolygon(polygon), new ArrayList<Coord>(2000));
59 List<Coord> points = root.get(new QuadTreePolygon(polygon), new ArrayList<>(2000));
6060 if (offset > 0) {
6161 ListIterator<Coord> pointIter = points.listIterator();
6262 while (pointIter.hasNext()) {
7070 break;
7171 case PathIterator.SEG_CLOSE:
7272 Path2D.Double segment = null;
73 if (clippingRect.contains(minX, minY) == false || clippingRect.contains(maxX,maxY) == false){
73 if (!clippingRect.contains(minX, minY) || !clippingRect.contains(maxX,maxY)) {
7474 Rectangle2D.Double bbox = new Rectangle2D.Double(minX,minY,maxX-minX,maxY-minY);
7575 segment = clipSinglePathWithSutherlandHodgman (points, num, clippingRect, bbox);
7676 } else
154154 * @return the clipped path as a Path2D.Double or null if the result is empty
155155 */
156156 public static Path2D.Double clipSinglePathWithSutherlandHodgman (double[] points, int num, Rectangle2D clippingRect, Rectangle2D.Double bbox) {
157 if (num <= 2 || bbox.intersects(clippingRect) == false){
157 if (num <= 2 || !bbox.intersects(clippingRect)) {
158158 return null;
159159 }
160160
9494
9595 getInitialBase();
9696
97 List<Numbers> numbers = new ArrayList<Numbers>();
97 List<Numbers> numbers = new ArrayList<>();
9898
9999 // To do this properly we need to know the number of nodes I think, this is the
100100 // best we can do: if there are more than 8 bits left, there must be another command
3232 * @author Steve Ratcliffe
3333 */
3434 public class StringStyleFileLoader extends StyleFileLoader {
35 private final Map<String, String> files = new HashMap<String, String>();
35 private final Map<String, String> files = new HashMap<>();
3636
3737 /**
3838 * Pass filename and file contents like so:
4444 * @author Steve Ratcliffe
4545 */
4646 public class TestUtils {
47 private static final List<String> files = new ArrayList<String>();
48 private static final Deque<Closeable> open = new ArrayDeque<Closeable>();
47 private static final List<String> files = new ArrayList<>();
48 private static final Deque<Closeable> open = new ArrayDeque<>();
4949
5050 static {
5151 files.add(Args.DEF_MAP_FILENAME);
108108 * @param in The arguments to use.
109109 */
110110 public static Outputs run(String ... in) {
111 List<String> args = new ArrayList<String>(Arrays.asList(in));
111 List<String> args = new ArrayList<>(Arrays.asList(in));
112112 args.add(0, Args.TEST_STYLE_ARG);
113113
114114 OutputStream outsink = new ByteArrayOutputStream();
4444 Random rand = new Random(8866028);
4545
4646 for (int iter = 0; iter < 1000000; iter++) {
47 List<String> sl = new ArrayList<String>();
47 List<String> sl = new ArrayList<>();
4848 for (int i = 0; i < 20; i++) {
4949 String n;
5050 do {
6868 }
6969
7070 private void run(String[] strings) {
71 List<Numbers> numbers = new ArrayList<Numbers>();
71 List<Numbers> numbers = new ArrayList<>();
7272 for (String s : strings) {
7373 Numbers n = new Numbers(s);
7474 n.setIndex(n.getPolishIndex());
283283 return list;
284284 }
285285
286 private List<Numbers> createList(String[] specs) {
286 private static List<Numbers> createList(String[] specs) {
287287 List<Numbers> numbers = new ArrayList<>();
288288 for (String s : specs) {
289289 Numbers n = new Numbers(s);
293293 return numbers;
294294 }
295295
296 private Matcher<Integer> lessThanOrEqual(final int val) {
296 private static Matcher<Integer> lessThanOrEqual(final int val) {
297297 return new BaseMatcher<Integer>() {
298298 public boolean matches(Object o) {
299299 return (Integer) o <= val;
2222
2323 @Test
2424 public void testGetObject() {
25 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
26 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 2, 2);
25 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
26 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 2, 2);
2727
2828 assertEquals("retrieve original object", HELLO1, ck1.getObject());
2929 }
3030
3131 @Test
3232 public void testCompletelyEqual() {
33 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
34 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 2, 2);
35 CombinedSortKey<String> ck2 = new CombinedSortKey<String>(k1, 2, 2);
33 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
34 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 2, 2);
35 CombinedSortKey<String> ck2 = new CombinedSortKey<>(k1, 2, 2);
3636
3737 assertEquals(0, ck1.compareTo(ck2));
3838 }
3939
4040 @Test
4141 public void testDifferentKey() {
42 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
43 IntegerSortKey<String> k2 = new IntegerSortKey<String>(HELLO1, 1, 2);
44 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 2, 2);
45 CombinedSortKey<String> ck2 = new CombinedSortKey<String>(k2, 2, 2);
42 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
43 IntegerSortKey<String> k2 = new IntegerSortKey<>(HELLO1, 1, 2);
44 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 2, 2);
45 CombinedSortKey<String> ck2 = new CombinedSortKey<>(k2, 2, 2);
4646
4747 assertEquals(-1, k1.compareTo(k2));
4848
5252
5353 @Test
5454 public void testDifferentFirst() {
55 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
56 IntegerSortKey<String> k2 = new IntegerSortKey<String>(HELLO1, 1, 1);
57 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 2, 2);
58 CombinedSortKey<String> ck2 = new CombinedSortKey<String>(k2, 3, 2);
55 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
56 IntegerSortKey<String> k2 = new IntegerSortKey<>(HELLO1, 1, 1);
57 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 2, 2);
58 CombinedSortKey<String> ck2 = new CombinedSortKey<>(k2, 3, 2);
5959
6060 assertEquals(0, k1.compareTo(k2));
6161
6565
6666 @Test
6767 public void testDifferentSecond() {
68 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
69 IntegerSortKey<String> k2 = new IntegerSortKey<String>(HELLO1, 1, 1);
70 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 2, 2);
71 CombinedSortKey<String> ck2 = new CombinedSortKey<String>(k2, 2, 3);
68 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
69 IntegerSortKey<String> k2 = new IntegerSortKey<>(HELLO1, 1, 1);
70 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 2, 2);
71 CombinedSortKey<String> ck2 = new CombinedSortKey<>(k2, 2, 3);
7272
7373 assertEquals(0, k1.compareTo(k2));
7474
7878
7979 @Test
8080 public void testKeyOverridesFirst() {
81 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
82 IntegerSortKey<String> k2 = new IntegerSortKey<String>(HELLO1, 2, 1);
83 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 3, 2);
84 CombinedSortKey<String> ck2 = new CombinedSortKey<String>(k2, 2, 2);
81 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
82 IntegerSortKey<String> k2 = new IntegerSortKey<>(HELLO1, 2, 1);
83 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 3, 2);
84 CombinedSortKey<String> ck2 = new CombinedSortKey<>(k2, 2, 2);
8585
8686 assertEquals(-1, k1.compareTo(k2));
8787
9191
9292 @Test
9393 public void testPrimaryOverridesSecond() {
94 IntegerSortKey<String> k1 = new IntegerSortKey<String>(HELLO1, 1, 1);
95 IntegerSortKey<String> k2 = new IntegerSortKey<String>(HELLO1, 1, 1);
96 CombinedSortKey<String> ck1 = new CombinedSortKey<String>(k1, 2, 3);
97 CombinedSortKey<String> ck2 = new CombinedSortKey<String>(k2, 3, 2);
94 IntegerSortKey<String> k1 = new IntegerSortKey<>(HELLO1, 1, 1);
95 IntegerSortKey<String> k2 = new IntegerSortKey<>(HELLO1, 1, 1);
96 CombinedSortKey<String> ck1 = new CombinedSortKey<>(k1, 2, 3);
97 CombinedSortKey<String> ck2 = new CombinedSortKey<>(k2, 3, 2);
9898
9999 assertEquals(0, k1.compareTo(k2));
100100
222222 }
223223 }
224224
225 private final List<FileArgs> files = new ArrayList<FileArgs>();
225 private final List<FileArgs> files = new ArrayList<>();
226226
227227 public void processOption(String opt, String val) {
228228 }
136136 @Test
137137 public void testAbsoluteFilenamesInFile() {
138138 String s, exp_dir;
139 if (PATH_SEP.equals("\\")) {
139 if ("\\".equals(PATH_SEP)) {
140140 s = "input-file: c:\\home\\foo\n";
141141 exp_dir = "c:\\home";
142142 }
2727
2828 public class ShapeMergeFilterTest {
2929 // create one Coord instance for each point in a small test grid
30 private static final HashMap<Integer,Coord> map = new HashMap<Integer,Coord>(){
31 /**
32 *
33 */
34 private static final long serialVersionUID = 1L;
35
36 {
37 for (int latHp = 0; latHp < 100; latHp +=5){
38 for (int lonHp = 0; lonHp < 100; lonHp += 5){
39 Coord co = Coord.makeHighPrecCoord(latHp, lonHp);
40 put(latHp*1000 + lonHp,co);
41 }
30 private static final HashMap<Integer, Coord> map = new HashMap<>();
31 static {
32 for (int latHp = 0; latHp < 100; latHp += 5) {
33 for (int lonHp = 0; lonHp < 100; lonHp += 5) {
34 Coord co = Coord.makeHighPrecCoord(latHp, lonHp);
35 map.put(latHp * 1000 + lonHp, co);
4236 }
4337 }
44 };
38
39 }
4540
4641 @Test
4742 public void testAreaTestVal(){
48 List<Coord> points = new ArrayList<Coord>(){/**
49 *
50 */
51 private static final long serialVersionUID = 1L;
52
53 {
54 add(getPoint(10,10));
55 add(getPoint(30,10));
56 add(getPoint(30,30));
57 add(getPoint(10,30));
58 add(getPoint(10,10)); // close
59
60 }};
61 assertEquals(2 * (20 * 20),ShapeMergeFilter.calcAreaSizeTestVal(points));
43 List<Coord> points = Arrays.asList(
44 getPoint(10,10),
45 getPoint(30,10),
46 getPoint(30,30),
47 getPoint(10,30),
48 getPoint(10,10)); // close
49
50 assertEquals(2L * (20 * 20), ShapeMergeFilter.calcAreaSizeTestVal(points));
6251 }
6352 /**
6453 * two simple shapes, sharing one point
6554 */
6655 @Test
6756 public void testSimpleSharingOne(){
68 List<Coord> points1 = new ArrayList<Coord>(){/**
69 *
70 */
71 private static final long serialVersionUID = 1L;
72
73 {
74 add(getPoint(15,10));
75 add(getPoint(30,25));
76 add(getPoint(25,30));
77 add(getPoint(10,30));
78 add(getPoint(5,20));
79 add(getPoint(15,10)); // close
80 }};
81
82 List<Coord> points2 = new ArrayList<Coord>(){/**
83 *
84 */
85 private static final long serialVersionUID = 1L;
86
87 {
88 add(getPoint(25,30));
89 add(getPoint(30,35));
90 add(getPoint(20,40));
91 add(getPoint(15,35));
92 add(getPoint(25,30));
93 }};
94 testVariants("simple shapes sharing one point", points1, points2,1,10);
57 List<Coord> points1 = Arrays.asList(
58 getPoint(15,10),
59 getPoint(30,25),
60 getPoint(25,30),
61 getPoint(10,30),
62 getPoint(5,20),
63 getPoint(15,10)); // close
64
65 List<Coord> points2 = Arrays.asList(
66 getPoint(25,30),
67 getPoint(30,35),
68 getPoint(20,40),
69 getPoint(15,35),
70 getPoint(25,30)); // close
71
72 testVariants("simple shapes sharing one point", points1, points2, 1, 10);
9573 }
9674
9775 /**
9977 */
10078 @Test
10179 public void testSimpleNonOverlapping(){
102 List<Coord> points1 = new ArrayList<Coord>(){/**
103 *
104 */
105 private static final long serialVersionUID = 1L;
106
107 {
108 add(getPoint(15,10));
109 add(getPoint(30,25));
110 add(getPoint(25,30));
111 add(getPoint(15,35));
112 add(getPoint(5,20));
113 add(getPoint(15,10)); // close
114 }};
115
116 List<Coord> points2 = new ArrayList<Coord>(){/**
117 *
118 */
119 private static final long serialVersionUID = 1L;
120
121 {
122 add(getPoint(25,30));
123 add(getPoint(30,35));
124 add(getPoint(20,40));
125 add(getPoint(15,35));
126 add(getPoint(25,30));
127 }};
128 testVariants("simple shapes", points1, points2,1,8);
80 List<Coord> points1 = Arrays.asList(
81 getPoint(15,10),
82 getPoint(30,25),
83 getPoint(25,30),
84 getPoint(15,35),
85 getPoint(5,20),
86 getPoint(15,10)); // close
87
88 List<Coord> points2 = Arrays.asList(
89 getPoint(25,30),
90 getPoint(30,35),
91 getPoint(20,40),
92 getPoint(15,35),
93 getPoint(25,30)); // close
94
95 testVariants("simple shapes", points1, points2, 1, 8);
12996 }
13097
13198 /**
134101
135102 @Test
136103 public void test3SharedPointsNonOverlapping(){
137 List<Coord> points1 = new ArrayList<Coord>(){/**
138 *
139 */
140 private static final long serialVersionUID = 1L;
141
142 {
143 add(getPoint(15,10));
144 add(getPoint(30,25));
145 add(getPoint(25,30));
146 add(getPoint(20,35));
147 add(getPoint(15,35));
148 add(getPoint(5,20));
149 add(getPoint(15,10));// close
150 }};
151
152 List<Coord> points2 = new ArrayList<Coord>(){/**
153 *
154 */
155 private static final long serialVersionUID = 1L;
156
157 {
158 add(getPoint(25,30));
159 add(getPoint(30,35));
160 add(getPoint(20,40));
161 add(getPoint(15,35));
162 add(getPoint(20,35));
163 add(getPoint(25,30));// close
164 }};
104 List<Coord> points1 = Arrays.asList(
105 getPoint(15,10),
106 getPoint(30,25),
107 getPoint(25,30),
108 getPoint(20,35),
109 getPoint(15,35),
110 getPoint(5,20),
111 getPoint(15,10));// close
112
113 List<Coord> points2 = Arrays.asList(
114 getPoint(25,30),
115 getPoint(30,35),
116 getPoint(20,40),
117 getPoint(15,35),
118 getPoint(20,35),
119 getPoint(25,30));// close
120
165121 testVariants("test 3 consecutive shared points", points1, points2, 1, 8);
166122 }
167123
171127
172128 @Test
173129 public void test2SharedPointsNoEdge(){
174 List<Coord> points1 = new ArrayList<Coord>(){/**
175 *
176 */
177 private static final long serialVersionUID = 1L;
178
179 {
180 add(getPoint(15,10));
181 add(getPoint(30,25));
182 add(getPoint(25,30));
183 add(getPoint(15,35));
184 add(getPoint(5,20));
185 add(getPoint(15,10));// close
186 }};
187
188 List<Coord> points2 = new ArrayList<Coord>(){/**
189 *
190 */
191 private static final long serialVersionUID = 1L;
192
193 {
194 add(getPoint(25,30));
195 add(getPoint(30,35));
196 add(getPoint(20,40));
197 add(getPoint(15,35));
198 add(getPoint(20,35));
199 add(getPoint(25,30));// close
200 }};
130 List<Coord> points1 = Arrays.asList(
131 getPoint(15,10),
132 getPoint(30,25),
133 getPoint(25,30),
134 getPoint(15,35),
135 getPoint(5,20),
136 getPoint(15,10));// close
137
138 List<Coord> points2 = Arrays.asList(
139 getPoint(25,30),
140 getPoint(30,35),
141 getPoint(20,40),
142 getPoint(15,35),
143 getPoint(20,35),
144 getPoint(25,30));// close
145
201146 testVariants("test 2 non-consecutive shared points", points1, points2, 1, 11);
202147 }
203148
208153
209154 @Test
210155 public void testCloseUFormed(){
211 List<Coord> points1 = new ArrayList<Coord>(){/**
212 *
213 */
214 private static final long serialVersionUID = 1L;
215
216 {
156 List<Coord> points1 = Arrays.asList(
217157 // u-formed shaped (open at top)
218 add(getPoint(15,50));
219 add(getPoint(30,50));
220 add(getPoint(30,55));
221 add(getPoint(20,55));
222 add(getPoint(20,65));
223 add(getPoint(30,65));
224 add(getPoint(30,70));
225 add(getPoint(15,70));
226 add(getPoint(15,50));// close
227 }};
228
229
230 List<Coord> points2 = new ArrayList<Coord>(){/**
231 *
232 */
233 private static final long serialVersionUID = 1L;
234
235 {
236 add(getPoint(35,50));
237 add(getPoint(35,70));
238 add(getPoint(30,70));
239 add(getPoint(30,65));
240 add(getPoint(30,55));
241 add(getPoint(30,50));
242 add(getPoint(35,50)); // close
243 }};
158 getPoint(15,50),
159 getPoint(30,50),
160 getPoint(30,55),
161 getPoint(20,55),
162 getPoint(20,65),
163 getPoint(30,65),
164 getPoint(30,70),
165 getPoint(15,70),
166 getPoint(15,50));// close
167
168 List<Coord> points2 = Arrays.asList(
169 getPoint(35,50),
170 getPoint(35,70),
171 getPoint(30,70),
172 getPoint(30,65),
173 getPoint(30,55),
174 getPoint(30,50),
175 getPoint(35,50)); // close
244176
245177 testVariants("test close U formed shape", points1, points2, 1, 11);
246178 }
251183
252184 @Test
253185 public void testFillUFormed(){
254 List<Coord> points1 = new ArrayList<Coord>(){/**
255 *
256 */
257 private static final long serialVersionUID = 1L;
258
259 {
186 List<Coord> points1 = Arrays.asList(
260187 // u-formed shaped (open at top)
261 add(getPoint(15,50));
262 add(getPoint(30,50));
263 add(getPoint(30,55));
264 add(getPoint(20,55));
265 add(getPoint(20,65));
266 add(getPoint(30,65));
267 add(getPoint(30,70));
268 add(getPoint(15,70));
269 add(getPoint(15,50)); // close
270 }};
271
272
273 List<Coord> points2 = new ArrayList<Coord>(){/**
274 *
275 */
276 private static final long serialVersionUID = 1L;
277
278 {
279 add(getPoint(30,55));
280 add(getPoint(30,65));
281 add(getPoint(20,65));
282 add(getPoint(20,55));
283 add(getPoint(30,55)); // close
284 }};
188 getPoint(15,50),
189 getPoint(30,50),
190 getPoint(30,55),
191 getPoint(20,55),
192 getPoint(20,65),
193 getPoint(30,65),
194 getPoint(30,70),
195 getPoint(15,70),
196 getPoint(15,50)); // close
197
198 List<Coord> points2 = Arrays.asList(
199 getPoint(30,55),
200 getPoint(30,65),
201 getPoint(20,65),
202 getPoint(20,55),
203 getPoint(30,55)); // close
204
285205 testVariants("test fill U-formed shape", points1, points2, 1, 5);
286206 }
287207
291211
292212 @Test
293213 public void testFillHole(){
294 List<Coord> points1 = new ArrayList<Coord>(){/**
295 *
296 */
297 private static final long serialVersionUID = 1L;
298
299 {
214 List<Coord> points1 = Arrays.asList(
300215 // a rectangle with a hole
301 add(getPoint(35,50));
302 add(getPoint(35,70));
303 add(getPoint(15,70));
304 add(getPoint(15,50));
305 add(getPoint(30,50));
306 add(getPoint(30,55));
307 add(getPoint(20,55));
308 add(getPoint(20,65));
309 add(getPoint(30,65));
310 add(getPoint(30,50));
311 add(getPoint(35,50));// close
312 }};
313
314 List<Coord> points2 = new ArrayList<Coord>(){/**
315 *
316 */
317 private static final long serialVersionUID = 1L;
318
319 {
320 add(getPoint(30,55));
321 add(getPoint(30,65));
322 add(getPoint(20,65));
323 add(getPoint(20,55));
324 add(getPoint(30,55)); // close
325 }};
216 getPoint(35,50),
217 getPoint(35,70),
218 getPoint(15,70),
219 getPoint(15,50),
220 getPoint(30,50),
221 getPoint(30,55),
222 getPoint(20,55),
223 getPoint(20,65),
224 getPoint(30,65),
225 getPoint(30,50),
226 getPoint(35,50));// close
227
228 List<Coord> points2 = Arrays.asList(
229 getPoint(30,55),
230 getPoint(30,65),
231 getPoint(20,65),
232 getPoint(20,55),
233 getPoint(30,55)); // close
234
326235 testVariants("test-fill-hole", points1, points2, 1, 6); // expect 8 points if spike is not removed
327236 }
328237
329238 @Test
330239 public void testDuplicate(){
331 List<Coord> points1 = new ArrayList<Coord>(){/**
332 *
333 */
334 private static final long serialVersionUID = 1L;
335
336 {
337 add(getPoint(30,55));
338 add(getPoint(30,65));
339 add(getPoint(20,65));
340 add(getPoint(20,55));
341 add(getPoint(30,55)); // close
342 }};
343 List<Coord> points2 = new ArrayList<Coord>(points1);
240 List<Coord> points1 = Arrays.asList(
241 getPoint(30,55),
242 getPoint(30,65),
243 getPoint(20,65),
244 getPoint(20,55),
245 getPoint(30,55)); // close
246
247 List<Coord> points2 = new ArrayList<>(points1);
344248
345249 testVariants("test duplicate", points1, points2, 1, 5);
346250 }
347251
348252 @Test
349253 public void testOverlap(){
350 List<Coord> points1 = new ArrayList<Coord>(){/**
351 *
352 */
353 private static final long serialVersionUID = 1L;
354
355 {
356 add(getPoint(30,55));
357 add(getPoint(30,65));
358 add(getPoint(20,65));
359 add(getPoint(20,55));
360 add(getPoint(30,55)); // close
361 }};
362
363 List<Coord> points2 = new ArrayList<Coord>(){/**
364 *
365 */
366 private static final long serialVersionUID = 1L;
367
368 {
369 add(getPoint(30,55));
370 add(getPoint(30,65));
371 add(getPoint(25,65));
372 add(getPoint(25,55));
373 add(getPoint(30,55)); // close
374 }};
254 List<Coord> points1 = Arrays.asList(
255 getPoint(30,55),
256 getPoint(30,65),
257 getPoint(20,65),
258 getPoint(20,55),
259 getPoint(30,55)); // close
260
261 List<Coord> points2 = Arrays.asList(
262 getPoint(30,55),
263 getPoint(30,65),
264 getPoint(25,65),
265 getPoint(25,55),
266 getPoint(30,55)); // close
267
375268 // no merge expected
376269 testVariants("test overlap", points1, points2, 2, 5);
377270 }
382275 */
383276 @Test
384277 public void testTwoWShaped(){
385 List<Coord> points1 = new ArrayList<Coord>(){/**
386 *
387 */
388 private static final long serialVersionUID = 1L;
389
390 {
391 add(getPoint(0,5));
392 add(getPoint(35,5));
393 add(getPoint(35,20));
394 add(getPoint(30,15));
395 add(getPoint(25,20));
396 add(getPoint(25,10));
397 add(getPoint(15,10));
398 add(getPoint(15,20));
399 add(getPoint(10,15));
400 add(getPoint(5,20));
401 add(getPoint(0,20));
402 add(getPoint(0,5)); // close
403 }};
404
405 List<Coord> points2 = new ArrayList<Coord>(){/**
406 *
407 */
408 private static final long serialVersionUID = 1L;
409
410 {
411 add(getPoint(35,35));
412 add(getPoint(35,20));
413 add(getPoint(30,15));
414 add(getPoint(25,20));
415 add(getPoint(25,25));
416 add(getPoint(15,25));
417 add(getPoint(15,20));
418 add(getPoint(10,15));
419 add(getPoint(5,20));
420 add(getPoint(0,20));
421 add(getPoint(5,35));
422 add(getPoint(35,35)); // close
423 }};
278 List<Coord> points1 = Arrays.asList(
279 getPoint(0,5),
280 getPoint(35,5),
281 getPoint(35,20),
282 getPoint(30,15),
283 getPoint(25,20),
284 getPoint(25,10),
285 getPoint(15,10),
286 getPoint(15,20),
287 getPoint(10,15),
288 getPoint(5,20),
289 getPoint(0,20),
290 getPoint(0,5)); // close
291
292 List<Coord> points2 = Arrays.asList(
293 getPoint(35,35),
294 getPoint(35,20),
295 getPoint(30,15),
296 getPoint(25,20),
297 getPoint(25,25),
298 getPoint(15,25),
299 getPoint(15,20),
300 getPoint(10,15),
301 getPoint(5,20),
302 getPoint(0,20),
303 getPoint(5,35),
304 getPoint(35,35)); // close
424305
425306 // wanted: merge that removes at least the longer shared sequence
426307 testVariants("test two w-shaped", points1, points2, 1, 16);
0 /*
1 * Copyright (C) 2020.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 3 or
5 * version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
11 */
12 package uk.me.parabola.mkgmap.osmstyle;
13
14 import static org.junit.Assert.assertEquals;
15
16 import java.util.Arrays;
17
18 import org.junit.Test;
19
20 import uk.me.parabola.imgfmt.app.Coord;
21 import uk.me.parabola.mkgmap.general.MapLine;
22 import uk.me.parabola.mkgmap.general.MapRoad;
23 import uk.me.parabola.util.EnhancedProperties;
24
25
26 /**
27 * Unit test for the code which implements the --road-name-config option
28 */
29 public class PrefixSuffixFilterTest {
30
31 @Test
32 public void testFilter() {
33 EnhancedProperties props = new EnhancedProperties();
34 props.put("road-name-config", "resources/roadNameConfig.txt");
35 PrefixSuffixFilter filter = new PrefixSuffixFilter(props);
36 MapRoad road;
37 road = genRoad("Rue de la Concorde", "FRA");
38 filter.filter(road);
39 assertEquals("Rue de la" + (char) 0x1e + "Concorde", road.getName());
40 road = genRoad("Place de l'Etoile", "FRA");
41 filter.filter(road);
42 assertEquals("Place de l'" + (char) 0x1b + "Etoile", road.getName());
43 road = genRoad("Rue de la Normandie", "DEU"); // no change in Germany
44 filter.filter(road);
45 assertEquals("Rue de la Normandie", road.getName());
46 road = genRoad("Karl-Mustermann-Straße", "DEU");
47 filter.filter(road);
48 assertEquals("Karl-Mustermann" + (char) 0x1c + "-Straße", road.getName());
49 road = genRoad("Karl-Mustermann-Straße", "DEU");
50 filter.filter(road);
51 assertEquals("Karl-Mustermann" + (char) 0x1c + "-Straße", road.getName());
52
53 }
54
55 private static MapRoad genRoad(String name, String isoCountry) {
56 MapLine line = new MapLine();
57 line.setPoints(Arrays.asList(new Coord(2.0,2.0), new Coord(2.01, 2.0)));
58 MapRoad r = new MapRoad(1, 1, line);
59 r.setName(name);
60 r.setCountry(isoCountry);
61 return r;
62 }
63
64 }
395395 }
396396
397397 private List<GType> resolveList(RuleSet rs, Way el) {
398 final List<GType> list = new ArrayList<GType>();
398 final List<GType> list = new ArrayList<>();
399399 rs.resolveType(el, new TypeResult() {
400400 public void add(Element el, GType type) {
401401 list.add(type);
405405 }
406406
407407 private GType getFirstType(Rule rs, Element el) {
408 final List<GType> types = new ArrayList<GType>();
408 final List<GType> types = new ArrayList<>();
409409 rs.resolveType(el, new TypeResult() {
410410 public void add(Element el, GType type) {
411411 types.add(type);
4444 public class StyledConverterTest {
4545 private static final String LOC = "classpath:teststyles";
4646 private OsmConverter converter;
47 private final List<MapLine> lines = new ArrayList<MapLine>();
47 private final List<MapLine> lines = new ArrayList<>();
4848
4949 @Test
5050 public void testConvertWay() throws FileNotFoundException {
3434 el.addTag("b", "2");
3535 el.addTag("c", "3");
3636
37 List<String> keys = new ArrayList<String>();
38 List<String> values = new ArrayList<String>();
37 List<String> keys = new ArrayList<>();
38 List<String> values = new ArrayList<>();
3939
4040 for (Map.Entry<String, String> ent : el.getTagEntryIterator()) {
4141 keys.add(ent.getKey());
1212 package uk.me.parabola.util;
1313
1414 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.assertFalse;
1516 import static org.junit.Assert.assertTrue;
1617
1718 import java.awt.geom.Area;
3233 */
3334 @Test
3435 public void testPolygonConversion() throws Exception {
35 List<Coord> polygon = new ArrayList<Coord>();
36 List<Coord> polygon = new ArrayList<>();
3637 polygon.add(new Coord(0,0));
3738 polygon.add(new Coord(100,10));
3839 polygon.add(new Coord(120,89));
8485 List<Coord> singularPolygon2 = Java2DConverter.singularAreaToPoints(a2);
8586 List<Coord> singularPolygon3 = Java2DConverter.singularAreaToPoints(a3);
8687
87 assertTrue(a1.equals(a2) == false);
88 assertTrue(a1.equals(a3) == false);
89 assertTrue(a2.equals(a3) == false);
88 assertFalse(a1.equals(a2));
89 assertFalse(a1.equals(a3));
90 assertFalse(a2.equals(a3));
9091 assertEquals(1, convPolygon1.size());
9192 assertEquals(1, convPolygon2.size());
9293 assertEquals(1, convPolygon3.size());
93 assertTrue(Arrays.deepEquals(convPolygon1.toArray(), convPolygon2.toArray()) == true);
94 assertTrue(Arrays.deepEquals(convPolygon1.toArray(), convPolygon3.toArray()) == false);
94 assertTrue(Arrays.deepEquals(convPolygon1.toArray(), convPolygon2.toArray()));
95 assertFalse(Arrays.deepEquals(convPolygon1.toArray(), convPolygon3.toArray()));
9596 assertEquals(4, convPolygon1.get(0).size());
9697 assertEquals(4, convPolygon2.get(0).size());
9798 assertEquals(5, convPolygon3.get(0).size());