Codebase list mkgmap / upstream/0.0.0+svn4885
New upstream version 0.0.0+svn4885 Bas Couwenberg 2 years ago
21 changed file(s) with 295 addition(s) and 169 deletion(s). Raw diff Collapse all Expand all
2727 file for each tile, then merges all the tiles together into a
2828 single gmapsupp.img file.
2929
30 Regional extracts of OpenStreetMap data can be obtained from
31 [https://download.geofabrik.de/ '''download.geofabrik.de'''].
32
3033 == Documentation ==
3134 The documentation that is currently available on this site is listed
3235 below.
4952
5053 [/doc/tuning '''Tuning''']
5154 Instructions on how to minimise execution time and avoid running out of memory.
52
53 Further information can be found in the
54 [http://wiki.openstreetmap.org/wiki/Mkgmap '''Open Street Map wiki''']
219219 ;--mdr7-excl=name[,name...]
220220 : Specify words which should be omitted from the road index.
221221 It was developed before option --road-name-config and is probably no longer needed.
222 Matching is done case insensitive.
222 Matching is case insensitive.
223223 : Example usage: --mdr7-excl="Road, Street, Weg"
224224
225225 ;--mdr7-del=name[,name...]
226226 : Use this option if your style adds strings to the labels of roads which you
227227 want to see in the map but which should not appear in the result list
228 of a road name / address search. The list is used like this:
229 For each road label, mkgmap searches for the last blank. If one is found, it checks
230 whether the word after it appears in the given list. If so, the word is removed
231 and the search is repeated. The remaining string is used to create the index.
232 The search is done case insensitive.
228 of a road name / address search.
229 When creating the index, words in the given list are removed from the end of road labels.
230 Any word not found in the given list is considerered to be a valid label; words
231 before the last valid label are not removed.
232 If the label consists of a single word in the given list, or all the words in the
233 label are contained in the given list, then the label is omitted from the index.
234 The comparison is case insensitive and words are separated by a space.
233235 : Example: Assume your style adds surface attributes like 'pav.' or 'unp.' to a road
234236 label. You can use --mdr7-del="pav.,unp." to remove these suffixes from the index.
235237
1919 // see [[Mkgmap/help/Tags]]
2020
2121 .Tags for routable roads
22 [options="header"]
22 [cols="4,6,3,7",options="header"]
2323 |=========================================================
2424 | Attribute | mkgmap tag | Example | Notes
2525 | Labels | +mkgmap:label:1+ +
5555 |=========================================================
5656
5757 .Tags that control the treatment of roads
58 [options="header"]
58 [cols="8,7,5",options="header"]
5959 |=========================================================
6060 | Tag | Description | Required mkgmap option
6161 | +mkgmap:way-has-pois+ | +true+ for ways that have at least one point with a tag +access=\*+, +barrier=\*+, or +highway=*+ | 'link-pois-to-ways'
11 // This is the list of variable filters.
22 //
33 .List of all substitution filters
4 [width="100%",grid="rows",cols="<1,<1,3a",options="header"]
4 [width="100%",grid="rows",cols="<1,<1,4a",options="header"]
55 |=======
66 | Name | Arguments | Description
77 | def | `default` |
99 This means that the variable will never be `unset' in places where that
1010 matters.
1111
12 `${oneway\|def:no}`
12 `${oneway\|def:"no"}`
1313
1414 | conv | `m=>ft` |
1515 Use for conversions between units.
4848 | part | `separator operator partnumber` |
4949 Split a value in parts and returns one or more part(s) of it. If +partnumber+ is negative, the part returned is counted from the end of the split
5050
51 If not specified, the default separator is ';' and the first part is returned (i.e. `${name\|part:}`=`${name\|part:;:1}`).
51 If not specified, the default separator is ';' and the first part is returned (i.e. `${name\|part:""}`=`${name\|part:";:1"}`).
5252
5353 If the operator is `:` the part specified by +partnumber+ is returned.
5454
7979 Prepares the value as a highway reference such as "A21" "I-80" and so
8080 on.
8181 A code is added to the front of the string so that a highway shield is
82 displayed, spaces are removed and the text is truncated so as not to overflow the
83 symbol.
82 displayed, any ";" are replaced by "/" and spaces are removed.
8483
8584 `${ref\|highway-symbol:"box:4:8"}`
8685
9089 references that contain numbers and letters.
9190 The second is the maximum length of references that do not contain numbers.
9291 If there is just the one number then it is used in both cases.
92 If no numbers are given, the default value 8 is used.
93
94 If the reference, after spaces have been removed, is longer than the maximum length
95 then the filter passes the string onwards unchanged; the highway-symbol is not prepended.
9396
9497 | height | `m=>ft` |
9598 This is exactly the same as the +conv+ filter, except that it prepends a special
103106 | country-ISO | |
104107 Use to normalize country names to the 3 character ISO 1366 code.
105108 The filter has no arguments. It uses the list in LocatorConfig.xml.
106 Possible arguments are country names, or ISO codes in 2 or 3 characters,
109 Possible inputs are country names, or ISO codes in 2 or 3 characters,
107110 for example "Deutschland", "Germany", "Bundesrepublik Deutschland", or "DE"
108111 will all return "DEU", also different cases like "GERMANY" or " germany "
109112 will work.
117120
118121 ....
119122 place=* {
120 name '${name} (${int_name\|not-equal:name})'
123 name '${name} (${int_name\|not-equal:"name"})'
121124 \| '${name}'
122125 }
123126 ....
129132 Extract part of the string. The start and end positions
130133 are counted starting from zero and the end position is not included.
131134
132 `${name\|substring:2:5}`
135 `${name\|substring:"2:5"}`
133136 If the "name" was "Dorset Lane", then the result is "rse". If there is just the one number,
134137 then the substring starts from that character until the end of the string.
135138
136 | not-contained | `separator tag` |
139 | not-contained | `separator:tag` |
137140 Used to check for duplicate values. If the value of this tag is contained in the list being
138141 the value of the tag named as the argument to +not-contained+, then value
139142 of this tag is set to undefined.
141144 ....
142145 type=route & route=bus & ref=* {
143146 apply {
144 set route_ref='$(route_ref),${ref\|not-contained:,:route_ref}' \| '$(route_ref)' \| '${ref}';
147 set route_ref=
148 '$(route_ref),${ref\|not-contained:",:route_ref"}'
149 \| '$(route_ref)' \| '${ref}';
145150 }
146151 }
147152 ....
577577 one argument required then they are usually separated by colons too, but
578578 that is not a rule.
579579
580 +${tagname|filter:arg1:arg2}+
580 +${tagname|filter:"arg1:arg2"}+
581581
582582 You can apply as many filter expressions to a substitution as you like.
583583
584584
585 +${tagname|filter1:arg|filter2:arg}+
586
587 If the argument contains spaces or symbols it should be quoted.
588
589 +${tagname|filter1:"arg with spaces"}+
590
591 For backward compatibility, most cases where you have spaces or symbols
585 +${tagname|filter1:"f1Args"|filter2:"f2Args"}+
586
587 For backward compatibility, most argument strings
592588 do not actually need to be quoted, however we would recommend that you
593589 do for clarity. If you need a pipe symbol or a closing curly backet,
594590 then you must use quotes.
713709 connect different regions, down to class 0 which is used for residential
714710 streets and other roads that you would only use for local travel.
715711
716 It is important for routing to work well that most roads are class 0 and
712 It is important for routing to work well that most roads have lower classes and
717713 there are fewer and fewer roads in each of the higher classes.
714 Also, the class of connector roads (links, roundabouts, ramps) matches
715 the class of the highest class of roads being connected.
718716
719717 .Road classes
720718 [width="40%",frame="topbot",grid="rows",cols="<1,<4",options="header"]
723721 | 4 | Major HW/Ramp
724722 | 3 | Principal HW
725723 | 2 | Arterial St / Other HW
726 | 1 | Roundabout / Collector
724 | 1 | Minor or Service road
727725 | 0 | Residential Street / Unpaved road / Trail
728726 |=====
729727
218218 --mdr7-excl=name[,name...]
219219 Specify words which should be omitted from the road index. It was developed
220220 before option --road-name-config and is probably no longer needed. Matching
221 is done case insensitive.
221 is case insensitive.
222222 Example usage: --mdr7-excl="Road, Street, Weg"
223223
224224 --mdr7-del=name[,name...]
225225 Use this option if your style adds strings to the labels of roads which you
226226 want to see in the map but which should not appear in the result list of a
227 road name / address search. The list is used like this: For each road
228 label, mkgmap searches for the last blank. If one is found, it checks
229 whether the word after it appears in the given list. If so, the word is
230 removed and the search is repeated. The remaining string is used to create
231 the index. The search is done case insensitive.
227 road name / address search. When creating the index, words in the given
228 list are removed from the end of road labels. Any word not found in the
229 given list is considerered to be a valid label; words before the last valid
230 label are not removed. If the label consists of a single word in the given
231 list, or all the words in the label are contained in the given list, then
232 the label is omitted from the index. The comparison is case insensitive and
233 words are separated by a space.
232234 Example: Assume your style adds surface attributes like 'pav.' or 'unp.' to
233235 a road label. You can use --mdr7-del="pav.,unp." to remove these suffixes
234236 from the index.
0 svn.version: 4839
1 build.timestamp: 2021-12-30T15:30:14+0000
0 svn.version: 4885
1 build.timestamp: 2022-01-30T08:18:40+0000
4444
4545 // We keep our own idea of the file position.
4646 private long position;
47 // the position of the header, normally 0, but not in GMP format
48 private final int gmpOffset;
4749
4850 public BufferedImgFileReader(ImgChannel chan) {
51 this(chan, 0);
52 }
53
54 public BufferedImgFileReader(ImgChannel chan, int gmpOffset) {
4955 this.chan = chan;
56 this.gmpOffset = gmpOffset;
5057 // buf.order(ByteOrder.LITTLE_ENDIAN);
5158 // could use getShort/getChar/getInt if sure buffer loaded
59 }
60
61 @Override
62 public int getGMPOffset() {
63 return gmpOffset;
5264 }
5365
5466 /**
4040
4141 // Set to 0x80 on locked maps. We are not interested in creating locked
4242 // maps, but may be useful to recognise them for completeness.
43 // private byte lockFlag;
43 private byte lockFlag;
4444
4545 // A date of creation.
4646 private Date creationDate;
7777 * @param reader Used to read the header.
7878 */
7979 public final void readHeader(ImgFileReader reader) throws ReadFailedException {
80 reader.position(0);
80 reader.position(reader.getGMPOffset());
8181 headerLength = reader.get2u();
8282 byte[] bytes = reader.get(TYPE_LEN);
8383 type = new String(bytes, StandardCharsets.US_ASCII);
8484 reader.get(); // ignore
85 reader.get(); // ignore
85 this.lockFlag = reader.get(); // 0x80 if locked
8686
8787 byte[] date = reader.get(7);
8888 creationDate = Utils.makeCreationTime(date);
115115 if (creationDate == null)
116116 creationDate = new Date();
117117 }
118
119 /**
120 * @return the lockFlag
121 */
122 public byte getLockFlag() {
123 return lockFlag;
124 }
118125 }
1616 package uk.me.parabola.imgfmt.app;
1717
1818 import java.io.Closeable;
19 import java.util.concurrent.atomic.AtomicInteger;
1920
2021 import uk.me.parabola.imgfmt.ReadFailedException;
2122
108109 * @return A phone number possibly containing the delimiter character.
109110 */
110111 public String getBase11str(byte firstChar, char delimiter);
112
113 /**
114 * Get the offset in the GMP file.
115 * @return the offset, 0 if not a GMP file
116 */
117 default int getGMPOffset() {
118 return 0;
119 }
120
121 /**
122 * Read varying length integer where first byte also gives number of following bytes.
123 * See also imgfmt/app/mdr/MdrUtils.writeVarLength() and ./MdrDisplay.printSect17()
124 *
125 * @return the length
126 */
127 default int readVarLength(AtomicInteger varLength) {
128 int peek = get1u();
129 if ((peek & 0x1) == 0x1) {
130 varLength.set(1);
131 return peek >> 1;
132 } else if ((peek & 0x3) == 0x2) {
133 varLength.set(2);
134 return peek >> 2 | get1u() << 6;
135 } else if ((peek & 0x7) == 0x4) {
136 varLength.set(3);
137 return peek >> 3 | get2u() << 5;
138 } else { // bottom 3 bits clear so assume:
139 varLength.set(4);
140 return peek >> 4 | get3u() << 4; // mkgmap writeVarLength does this
141 }
142 }
111143 }
1111 */
1212 package uk.me.parabola.imgfmt.app.lbl;
1313
14 import static uk.me.parabola.imgfmt.app.Label.NULL_LABEL;
15
1416 import java.util.ArrayList;
1517 import java.util.Collections;
1618 import java.util.HashMap;
2325 import uk.me.parabola.imgfmt.app.ImgFile;
2426 import uk.me.parabola.imgfmt.app.ImgFileReader;
2527 import uk.me.parabola.imgfmt.app.Label;
28 import uk.me.parabola.imgfmt.app.Section;
2629 import uk.me.parabola.imgfmt.app.labelenc.CharacterDecoder;
2730 import uk.me.parabola.imgfmt.app.labelenc.CodeFunctions;
2831 import uk.me.parabola.imgfmt.app.labelenc.DecodedText;
2932 import uk.me.parabola.imgfmt.app.trergn.Subdivision;
3033 import uk.me.parabola.imgfmt.fs.ImgChannel;
3134 import uk.me.parabola.log.Logger;
32
33 import static uk.me.parabola.imgfmt.app.Label.NULL_LABEL;
3435
3536 /**
3637 * The file that holds all the labels for the map.
5859 private final List<City> cities = new ArrayList<>();
5960
6061 public LBLFileReader(ImgChannel chan) {
61 this(chan, true);
62 this(chan, true, 0);
6263 }
6364
64 public LBLFileReader(ImgChannel chan, boolean fullData) {
65 public LBLFileReader(ImgChannel chan, boolean fullData, int gmpOffset) {
6566 setHeader(header);
6667
67 setReader(new BufferedImgFileReader(chan));
68 setReader(new BufferedImgFileReader(chan, gmpOffset));
6869 header.readHeader(getReader());
6970 if (!fullData)
7071 return;
139140 PlacesHeader placeHeader = header.getPlaceHeader();
140141
141142 countries.add(null); // 1 based indexes
142
143 int start = placeHeader.getCountriesStart();
144 int end = placeHeader.getCountriesEnd();
145
143 Section s = placeHeader.getCountrySection();
144 int start = s.getPosition();
145 int end = s.getEndPos();
146 int recSize = s.getItemSize();
147 int unkSize = recSize - 3; // new maps may have extra data
146148 reader.position(start);
147149 int index = 1;
148150 while (reader.position() < end) {
149151 int offset = reader.get3u();
150 Label label = fetchLabel(offset);
152 Label label = fetchLabel(offset & 0x3fffff);
153 if (unkSize > 0)
154 reader.get(unkSize);
151155
152156 if (label != null) {
153157 Country country = new Country(index, label);
164168 ImgFileReader reader = getReader();
165169
166170 PlacesHeader placeHeader = header.getPlaceHeader();
167
168 int start = placeHeader.getRegionsStart();
169 int end = placeHeader.getRegionsEnd();
171 Section s = placeHeader.getRegionSection();
172 int start = s.getPosition();
173 int end = s.getEndPos();
174 int recSize = s.getItemSize();
175 int unkSize = recSize - 5; // new maps may have extra data
170176
171177 regions.add(null);
172178
175181 while (reader.position() < end) {
176182 int country = reader.get2u();
177183 int offset = reader.get3u();
178 Label label = fetchLabel(offset);
184 if (unkSize > 0)
185 reader.get(unkSize);
186 Label label = fetchLabel(offset & 0x3fffff);
179187 if (label != null) {
180188 Region region = new Region(countries.get(country), label);
181189 region.setIndex(index);
193201 */
194202 private void readCities() {
195203 PlacesHeader placeHeader = header.getPlaceHeader();
196 int start = placeHeader.getCitiesStart();
197 int end = placeHeader.getCitiesEnd();
198
204 Section s = placeHeader.getCitySection();
205 int start = s.getPosition();
206 int end = s.getEndPos();
207 int recSize = s.getItemSize();
208 int unkSize = recSize - 5; // new maps may have extra data
199209 ImgFileReader reader = getReader();
200210
201211 // Since cities are indexed starting from 1, we add a null one at index 0
207217 // don't know until we have read further
208218 int label = reader.get3u();
209219 int info = reader.get2u();
220 if (unkSize > 0)
221 reader.get(unkSize);
210222
211223 City city;
212224 if ((info & 0x4000) == 0) {
220232 city.setIndex(index);
221233 if ((info & 0x8000) == 0) {
222234 city.setSubdivision(Subdivision.createEmptySubdivision(1));
223 Label label1 = labels.get(label & 0x3fffff);
235 Label label1 = fetchLabel(label & 0x3fffff);
224236 city.setLabel(label1);
225237 } else {
226238 // Has subdiv/point index
309321 ImgFileReader reader = getReader();
310322
311323 PlacesHeader placeHeader = header.getPlaceHeader();
312 int start = placeHeader.getZipsStart();
313 int end = placeHeader.getZipsEnd();
324 Section s = placeHeader.getZipSection();
325 int start = s.getPosition();
326 int end = s.getEndPos();
327 int recSize = s.getItemSize();
328 int unkSize = recSize - 3; // new maps may have extra data
314329
315330 reader.position(start);
316331
317332 int zipIndex = 1;
318333 while (reader.position() < end) {
319334 int lblOffset = reader.get3u();
320
321 Zip zip = new Zip(fetchLabel(lblOffset));
335 if (unkSize > 0)
336 reader.get(unkSize);
337
338 Zip zip = new Zip(fetchLabel(lblOffset & 0x3fffff));
322339 zip.setIndex(zipIndex);
323340
324341 zips.put(zip.getIndex(), zip);
121121 }
122122
123123 void readFileHeader(ImgFileReader reader) {
124 reader.position(0x1f);
124 reader.position(0x1fL + reader.getGMPOffset());
125125
126126 country.readSectionInfo(reader, true);
127127 reader.get4();
222222 return poiProperties.getEndPos();
223223 }
224224
225 public int getCitiesStart() {
226 return city.getPosition();
227 }
228 public int getCitiesEnd() {
229 return city.getEndPos();
230 }
231
225 public Section getCitySection() {
226 return city;
227 }
232228 public int getNumExits() {
233229 return exitFacility.getNumItems();
234230 }
235231
236 public int getCountriesStart() {
237 return country.getPosition();
238 }
239
240 public int getCountriesEnd() {
241 return country.getEndPos();
242 }
243
244 public int getRegionsStart() {
245 return region.getPosition();
246 }
247
248 public int getRegionsEnd() {
249 return region.getEndPos();
232 public Section getCountrySection() {
233 return country;
234 }
235
236 public Section getRegionSection() {
237 return region;
250238 }
251239
252240 public int getNumHighways() {
253241 return highway.getNumItems();
254242 }
255243
256 public int getZipsStart() {
257 return zip.getPosition();
258 }
259
260 public int getZipsEnd() {
261 return zip.getEndPos();
262 }
244 public Section getZipSection() {
245 return zip;
246 }
263247 }
1717
1818 import java.util.Arrays;
1919
20 import uk.me.parabola.imgfmt.Utils;
2021 import uk.me.parabola.imgfmt.app.CommonHeader;
2122 import uk.me.parabola.imgfmt.app.ImgFileReader;
2223 import uk.me.parabola.imgfmt.app.ImgFileWriter;
4041 private final Section boundary = new Section(roads, BOUNDARY_ITEM_SIZE);
4142 private final Section highClassBoundary = new Section(boundary);
4243 private final int[] classBoundaries = new int[5];
44 private final Section nod5 = new Section();
45 private final Section nod6 = new Section();
4346
4447 private int flags;
4548 private int align;
4649 private int mult1;
4750 private int tableARecordLen;
4851 private boolean driveOnLeft;
52
53 private int indexIdSize;
4954
5055 public NODHeader() {
5156 super(HEADER_LEN, "GARMIN NOD");
7984 classBoundaries[3] = classBoundaries[2] + reader.get4();
8085 classBoundaries[4] = classBoundaries[3] + reader.get4();
8186 }
87 if (getHeaderLength() >= 0x7f) {
88 reader.position(reader.getGMPOffset() + 0x67);
89 nod5.readSectionInfo(reader, false);
90 reader.get2u();
91 nod6.readSectionInfo(reader, true);
92 indexIdSize = Utils.numberToPointerSize(nod6.getNumItems());
93 }
94
8295 }
8396
8497 /**
198211 public int getTableARecordLen() {
199212 return tableARecordLen;
200213 }
214
215 /**
216 * @return the indexIdSize
217 */
218 public int getIndexIdSize() {
219 return indexIdSize;
220 }
201221 }
4949 private int indPointPtrOff;
5050 private int polylinePtrOff;
5151 private int polygonPtrOff;
52 private ByteArrayOutputStream extTypePointsData;
53 private ByteArrayOutputStream extTypeLinesData;
54 private ByteArrayOutputStream extTypeAreasData;
52 private final ByteArrayOutputStream extTypePointsData = new ByteArrayOutputStream();
53 private final ByteArrayOutputStream extTypeLinesData = new ByteArrayOutputStream();
54 private final ByteArrayOutputStream extTypeAreasData = new ByteArrayOutputStream();
5555
5656 public RGNFile(ImgChannel chan) {
5757 setHeader(header);
6868
6969 header.setDataSize(position() - HEADER_LEN);
7070
71 if(extTypeAreasData != null) {
71 if(extTypeAreasData.size() > 0) {
7272 header.setExtTypeAreasInfo(position(), extTypeAreasData.size());
7373 getWriter().put(extTypeAreasData.toByteArray());
7474 }
75 if(extTypeLinesData != null) {
75 if(extTypeLinesData.size() > 0) {
7676 header.setExtTypeLinesInfo(position(), extTypeLinesData.size());
7777 getWriter().put(extTypeLinesData.toByteArray());
7878 }
79 if(extTypePointsData != null) {
79 if(extTypePointsData.size() > 0) {
8080 header.setExtTypePointsInfo(position(), extTypePointsData.size());
8181 getWriter().put(extTypePointsData.toByteArray());
8282 }
9494 // needed as it will always be first if present.
9595 if (sd.needsIndPointPtr()) {
9696 indPointPtrOff = position();
97 position(position() + 2);
97 position(position() + 2L);
9898 }
9999
100100 if (sd.needsPolylinePtr()) {
101101 polylinePtrOff = position();
102 position(position() + 2);
102 position(position() + 2L);
103103 }
104104
105105 if (sd.needsPolygonPtr()) {
106106 polygonPtrOff = position();
107 position(position() + 2);
107 position(position() + 2L);
108108 }
109109
110110 currentDivision = sd;
111111 }
112112
113113 public void addMapObject(MapObject item) {
114 if(item.hasExtendedType()) {
114 if (item.hasExtendedType()) {
115115 try {
116 if(item instanceof Point) {
117 if(extTypePointsData == null)
118 extTypePointsData = new ByteArrayOutputStream();
116 if (item instanceof Point) {
119117 item.write(extTypePointsData);
120 }
121 else if(item instanceof Polygon) {
122 if(extTypeAreasData == null)
123 extTypeAreasData = new ByteArrayOutputStream();
118 } else if (item instanceof Polygon) {
124119 item.write(extTypeAreasData);
125 }
126 else if(item instanceof Polyline) {
127 if(extTypeLinesData == null)
128 extTypeLinesData = new ByteArrayOutputStream();
120 } else if (item instanceof Polyline) {
129121 item.write(extTypeLinesData);
130 }
131 else
122 } else
132123 log.error("Can't add object of type " + item.getClass());
133 }
134 catch (IOException ioe) {
124 } catch (IOException ioe) {
135125 log.error("Error writing extended type object: " + ioe.getMessage());
136126 }
137 }
138 else {
127 } else {
139128 item.write(getWriter());
140129 }
141130 }
191180 }
192181
193182 public int getExtTypePointsSize() {
194 return (extTypePointsData == null)? 0 : extTypePointsData.size();
183 return extTypePointsData.size();
195184 }
196185
197186 public int getExtTypeLinesSize() {
198 return (extTypeLinesData == null)? 0 : extTypeLinesData.size();
187 return extTypeLinesData.size();
199188 }
200189
201190 public int getExtTypeAreasSize() {
202 return (extTypeAreasData == null)? 0 : extTypeAreasData.size();
191 return extTypeAreasData.size();
203192 }
204193
205194 public boolean haveExtendedTypes() {
206 return extTypePointsData != null ||
207 extTypeLinesData != null ||
208 extTypeAreasData != null;
195 return extTypeAreasData.size() != 0 ||
196 extTypeLinesData.size() != 0 ||
197 extTypePointsData.size() != 0;
209198 }
210199 }
8484 fetchPointsCommon(sd, rgnOffsets.getPointStart(), rgnOffsets.getPointEnd(), list);
8585 }
8686 if (withExtType && sd.getExtTypePointsSize() > 0)
87 fetchPointsCommonExtType(sd, rgnHeader.getExtTypePointsOffset() + sd.getExtTypePointsOffset(), sd.getExtTypePointsSize(), list);
87 fetchPointsExtType(sd, list);
8888
8989 return list;
9090 }
127127 if (hasSubtype) {
128128 int st = reader.get1u();
129129 t |= st;
130 //p.setHasSubtype(true);
131130 }
132131 p.setType(t);
133132
137136 }
138137
139138 /**
140 * The indexed points and the points sections are both read just the same.
141 */
142 private void fetchPointsCommonExtType(Subdivision sd, long start, long end, List<Point> points) {
139 * The points with extended types
140 */
141 private void fetchPointsExtType(Subdivision sd, List<Point> points) {
142 long start = rgnHeader.getExtTypePointsOffset() + sd.getExtTypePointsOffset();
143 long end = start + sd.getExtTypePointsSize();
143144 position(start);
144145 ImgFileReader reader = getReader();
145146
338339
339340 if (hasLabel){
340341 int labelOffset = reader.get3u();
341 Label label;
342 if ((labelOffset & 0x800000) == 0) {
343 label = lblFile.fetchLabel(labelOffset & 0x7fffff);
344 } else {
345 int netoff = labelOffset & 0x3fffff;
346 labelOffset = netFile.getLabelOffset(netoff);
347 label = lblFile.fetchLabel(labelOffset);
348 RoadDef roadDef = new RoadDef(0, netoff, label.getText());
349 line.setRoadDef(roadDef);
350 }
342 Label label = lblFile.fetchLabel(labelOffset & 0x3fffff);
351343 line.setLabel(label);
352344 }
353345 if (hasExtraBytes){
6767 private boolean hasIndPoints;
6868 private boolean hasPolylines;
6969 private boolean hasPolygons;
70 private boolean hasRoadRefs;
7071
7172 private int numPolylines;
7273
166167 setHasPolylines(true);
167168 if ((elem & 0x80) != 0)
168169 setHasPolygons(true);
169 }
170 if ((elem & 0x1) != 0) // from top bit in height
171 setHasRoadRefs(true);
172 }
170173
171174 /**
172175 * Create a subdivision at a given zoom level.
398401 this.hasPolygons = hasPolygons;
399402 }
400403
404 public void setHasRoadRefs(boolean hasRoeadRefs) {
405 this.hasRoadRefs = hasRoeadRefs;
406 }
407
401408 public boolean hasPoints() {
402409 return hasPoints;
403410 }
412419
413420 public boolean hasPolygons() {
414421 return hasPolygons;
422 }
423
424 public boolean hasRoadRefs() {
425 return hasRoadRefs;
415426 }
416427
417428 /**
433444 }
434445
435446 /**
447 * Needed if it exists and is not first, ie there is a points or
448 * indexed points or polyline section.
449 * @return true if pointer needed.
450 */
451 public boolean needsPolygonPtr() {
452 return hasPolygons && (hasPoints || hasIndPoints || hasPolylines);
453 }
454
455 /**
436456 * As this is last in the list it is needed if it exists and there
437457 * is another section.
438458 * @return true if pointer needed.
439459 */
440 public boolean needsPolygonPtr() {
441 return hasPolygons && (hasPoints || hasIndPoints || hasPolylines);
442 }
443
460 public boolean needsRoadRefPtr() {
461 return hasRoadRefs && (hasPoints || hasIndPoints || hasPolylines || hasPolygons);
462 }
463
444464 public String toString() {
445465 return "Sub" + zoomLevel + '(' + getCenter().toOSMURL() + ')';
446466 }
4949
5050
5151 public TREFileReader(ImgChannel chan) {
52 this(chan, 0);
53 }
54
55 public TREFileReader(ImgChannel chan, int gmpOffset) {
5256 setHeader(header);
5357
54 setReader(new BufferedImgFileReader(chan));
58 setReader(new BufferedImgFileReader(chan, gmpOffset));
5559 header.readHeader(getReader());
5660 readMapLevels();
5761 readSubdivs();
107111 int lat = reader.get3s();
108112 int width = reader.get2u() & 0x7fff;
109113 int height = reader.get2u();
114 int extFlags = flags;
115 if (reader.getGMPOffset() > 0 && (height & 0x8000) != 0) {
116 extFlags |= 0x1;
117 height &= 0x7fff;
118 }
110119
111120 if (count < levelDivs.length-1)
112121 reader.get2u();
113122
114123 int endRgnOffset = reader.get3u();
115124
116 SubdivData subdivData = new SubdivData(flags,
117 // why??? lat, lon, 2*width, 2*height,
125 SubdivData subdivData = new SubdivData(extFlags,
118126 lat, lon, width, height,
119127 lastRgnOffset, endRgnOffset);
120128
169177 int levelsPos = header.getMapLevelsPos();
170178 int levelsSize = header.getMapLevelsSize();
171179 reader.position(levelsPos);
180 byte[] levelsData = reader.get(levelsSize);
172181
173182 List<Subdivision[]> levelDivsList = new ArrayList<>();
174183 List<Zoom> mapLevelsList = new ArrayList<>();
175 int end = levelsPos + levelsSize;
176 while (reader.position() < end) {
177 int level = reader.get1u();
178 int nbits = reader.get1u();
179 int ndivs = reader.get2u();
180
184 int key = 0;
185 if (header.getLockFlag() != 0) {
186 long pos = reader.position();
187 if (header.getHeaderLength() >= 0xAA) {
188 reader.position((reader.getGMPOffset() + 0xAA));
189 key = reader.get4();
190
191 }
192 reader.position(pos);
193
194 demangle(levelsData, levelsSize, key);
195 }
196
197 int used = 0;
198 while (used < levelsSize) {
199 int level = levelsData[used++] & 0xff;
200 int nbits = levelsData[used++] & 0xff;
201 byte b1 = levelsData[used++];
202 byte b2 = levelsData[used++];
203 int ndivs = (b1 & 0xff) | ((b2 & 0xff) << 8);
181204 Subdivision[] divs = new Subdivision[ndivs];
182205 levelDivsList.add(divs);
183206 level &= 0x7f;
237260 }
238261 return msgs.toArray(new String[msgs.size()]);
239262 }
263
264
265 // code taken from GPXsee
266 // origin is much older:
267 // https://github.com/wuyongzheng/gimgtools/blob/92d015749e105c5fb8eb704ae503a5c7e51af2bd/gimgunlock.c#L15
268 private static void demangle(byte[] data, int size, int key) {
269 final byte[] shuf = {
270 0xb, 0xc, 0xa, 0x0,
271 0x8, 0xf, 0x2, 0x1,
272 0x6, 0x4, 0x9, 0x3,
273 0xd, 0x5, 0x7, 0xe
274 };
275
276 int sum = shuf[((key >> 24) + (key >> 16) + (key >> 8) + key) & 0xf];
277 int ringctr = 16;
278 for (int i = 0; i < size; i++) {
279 int upper = data[i] >> 4;
280 int lower = data[i];
281
282 upper -= sum;
283 upper -= key >> ringctr;
284 upper -= shuf[(key >> ringctr) & 0xf];
285 ringctr = ringctr != 0 ? ringctr - 4 : 16;
286
287 lower -= sum;
288 lower -= key >> ringctr;
289 lower -= shuf[(key >> ringctr) & 0xf];
290 ringctr = ringctr != 0 ? ringctr - 4 : 16;
291
292 data[i] = (byte) (((upper << 4) & 0xf0) | (lower & 0xf));
293 }
294 }
295
240296 }
106106 * @param reader The header is read from here.
107107 */
108108 protected void readFileHeader(ImgFileReader reader) throws ReadFailedException {
109 if (reader.position() != COMMON_HEADER_LEN) {
109 if (reader.position() - reader.getGMPOffset() != COMMON_HEADER_LEN) {
110110 throw new ReadFailedException("Reader position not at expected header length", new IOException());
111111 }
112112 int maxLat = reader.get3s();
357357 }
358358
359359 /**
360 * Convert a string to a byte array.
361 * @param s The string
362 * @return A byte array.
363 */
364 private static byte[] toByte(String s) {
365 // NB: what character set should be used?
366 return s.getBytes();
367 }
368
369 /**
370360 * Convert to the one byte code that is used for the year.
371361 * If the year is in the 1900, then subtract 1900 and add the result to 0x63,
372362 * else subtract 2000.
3636
3737 private final MapDataSource mapSource;
3838
39 // There is an absolute largest size as offsets are in 16 bits, we are
40 // staying safely inside it however.
39 // There is an absolute largest size as offsets are in 16 bits, but
40 // the top bit of height is probably a flag for GMP "RoadRef" data.
4141 public static final int MAX_DIVISION_SIZE = 0x7fff;
4242
4343 // Not a real limit. Note that the offset to the start of a section
335335 }
336336
337337 private static void lblInfo(ImgChannel chan, FileInfo info) {
338 try (LBLFileReader lblFile = new LBLFileReader(chan, false)) {
338 try (LBLFileReader lblFile = new LBLFileReader(chan, false, 0)) {
339339 info.setCodePage(lblFile.getCodePage());
340340 info.setSortOrderId(lblFile.getSortOrderId());
341341 }