Skip to content
GitLab
Explore
Sign in
Register
Commits on Source (4)
New upstream version 0.0.0+svn4223
· 5ad77dbd
Bas Couwenberg
authored
Sep 01, 2018
5ad77dbd
Merge tag 'upstream/0.0.0+svn4223'
· a9dbb870
Bas Couwenberg
authored
Sep 01, 2018
Upstream version 0.0.0+svn4223
a9dbb870
New upstream SVN snapshot.
· 2e5587ee
Bas Couwenberg
authored
Sep 01, 2018
2e5587ee
Set distribution to unstable.
· 4697e867
Bas Couwenberg
authored
Sep 01, 2018
4697e867
Show whitespace changes
Inline
Side-by-side
debian/changelog
View file @
4697e867
mkgmap (0.0.0+svn42
14-2) UNRELEASED
; urgency=medium
mkgmap (0.0.0+svn42
23-1) unstable
; urgency=medium
* New upstream SVN snapshot.
* Bump Standards-Version to 4.2.1, no changes.
-- Bas Couwenberg <sebastic@debian.org> S
un
, 0
5 Aug
2018
2
0:3
0
:4
5
+0200
-- Bas Couwenberg <sebastic@debian.org> S
at
, 0
1 Sep
2018 0
8
:3
6
:4
4
+0200
mkgmap (0.0.0+svn4214-1) unstable; urgency=medium
...
...
doc/options.txt
View file @
4697e867
...
...
@@ -534,6 +534,24 @@ not need a map that supports routing.
that support routing. If you specify this option, you do not need
to specify --net.
<p>
;--add-boundary-nodes-at-admin-boundaries=NUM
: This option controls how mkgmap calculates special routing nodes which
are needed by Garmin software to allow routing between different map tiles.
These nodes are written to section 3 and 4 in the NOD file.
When a road crosses the tile boundary (bbox), the road is split at this
point and such a special node is written. This allows routing between
one set of tiles produced by splitter.jar. However, if you create a map
from different sets of tiles, those tiles are likely to overlap.
For the overlapping tiles, none of the entries in NOD3 match and thus
routing across tile border doesn't work when the route is not fully
covered by one of the tiles.
The option tells mkgmap to add special nodes whereever a road touches or
crosses an administratve boundary. The NUM parameter specifies a filter
for the admin_level. Boundaries with a higher admin_level value are ignored.
The default value is 2 (country borders). Another reasonable value might
be 4. A value less or equal to 0 tells mkgmap to ignore intersections at
administrative boundaries.
<p>
;--drive-on=left|right|detect|detect,left|detect,right
: Explicitly specify which side of the road vehicles are
expected to drive on.
...
...
resources/help/en/options
View file @
4697e867
...
...
@@ -540,6 +540,24 @@ Miscellaneous options:
Use the --bounds option to make sure that the detection
finds the correct country.
--add-boundary-nodes-at-admin-boundaries=NUM
This option controls how mkgmap calculates special routing nodes which
are needed by Garmin software to allow routing between different map tiles.
These nodes are written to section 3 and 4 in the NOD file.
When a road crosses the tile boundary (bbox), the road is split at this
point and such a special node is written. This allows routing between
one set of tiles produced by splitter.jar. However, if you create a map
from different sets of tiles, those tiles are likely to overlap.
For the overlapping tiles, none of the entries in NOD3 match and thus
routing across tile border doesn't work when the route is not fully
covered by one of the tiles.
The option tells mkgmap to add special nodes whereever a road touches or
crosses an administratve boundary. The NUM parameter specifies a filter
for the admin_level. Boundaries with a higher admin_level value are ignored.
The default value is 2 (country borders). Another reasonable value might
be 4. A value less or equal to 0 tells mkgmap to ignore intersections at
administrative boundaries.
--check-roundabouts
Check that roundabouts have the expected direction (clockwise
when vehicles drive on the left). Roundabouts that are complete
...
...
resources/mkgmap-version.properties
View file @
4697e867
svn.version
:
42
14
build.timestamp
:
2018-0
7-28
T0
9
:2
9:55
+0100
svn.version
:
42
23
build.timestamp
:
2018-0
8-13
T0
7
:2
1:37
+0100
resources/styles/default/inc/address
View file @
4697e867
src/uk/me/parabola/imgfmt/Utils.java
View file @
4697e867
...
...
@@ -16,6 +16,7 @@
*/
package
uk.me.parabola.imgfmt
;
import
java.awt.geom.Line2D
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.FileInputStream
;
...
...
@@ -28,8 +29,8 @@ import java.util.Calendar;
import
java.util.Date
;
import
java.util.zip.GZIPInputStream
;
import
uk.me.parabola.imgfmt.app.ImgFileWriter
;
import
uk.me.parabola.imgfmt.app.Coord
;
import
uk.me.parabola.imgfmt.app.ImgFileWriter
;
/**
* Some miscellaneous functions that are used within the .img code.
*
...
...
@@ -389,49 +390,6 @@ public class Utils {
return
(
long
)(
latHp
&
0xffffffff
L
)
<<
32
|
(
lonHp
&
0xffffffff
L
);
}
/**
* Check if the line p1_1 to p1_2 cuts line p2_1 to p2_2 in two pieces and vice versa.
* This is a form of intersection check where it is allowed that one line ends on the
* other line or that the two lines overlap.
* @param p1_1 first point of line 1
* @param p1_2 second point of line 1
* @param p2_1 first point of line 2
* @param p2_2 second point of line 2
* @return true if both lines intersect somewhere in the middle of each other
*/
public
static
boolean
linesCutEachOther
(
Coord
p1_1
,
Coord
p1_2
,
Coord
p2_1
,
Coord
p2_2
)
{
int
width1
=
p1_2
.
getHighPrecLon
()
-
p1_1
.
getHighPrecLon
();
int
width2
=
p2_2
.
getHighPrecLon
()
-
p2_1
.
getHighPrecLon
();
int
height1
=
p1_2
.
getHighPrecLat
()
-
p1_1
.
getHighPrecLat
();
int
height2
=
p2_2
.
getHighPrecLat
()
-
p2_1
.
getHighPrecLat
();
int
denominator
=
((
height2
*
width1
)
-
(
width2
*
height1
));
if
(
denominator
==
0
)
{
// the lines are parallel
// they might overlap but this is ok for this test
return
false
;
}
int
x1Mx3
=
p1_1
.
getHighPrecLon
()
-
p2_1
.
getHighPrecLon
();
int
y1My3
=
p1_1
.
getHighPrecLat
()
-
p2_1
.
getHighPrecLat
();
double
isx
=
(
double
)((
width2
*
y1My3
)
-
(
height2
*
x1Mx3
))
/
denominator
;
if
(
isx
<=
0
||
isx
>=
1
)
{
return
false
;
}
double
isy
=
(
double
)((
width1
*
y1My3
)
-
(
height1
*
x1Mx3
))
/
denominator
;
if
(
isy
<=
0
||
isy
>=
1
)
{
return
false
;
}
return
true
;
}
public
static
int
numberToPointerSize
(
int
n
)
{
// moved from imgfmt/app/mdr/MdrSection.java and app/typ/TYPFile.java
if
(
n
<=
0xff
)
...
...
@@ -452,4 +410,40 @@ public class Utils {
writer
.
put3s
(
longitude
);
}
/**
* Finds the intersection of two line segments, also if one ends on the other
* See https://de.wikipedia.org/wiki/Geod%C3%A4tisches_Rechnen#Geradenschnitt
* @param p1_1 the coordinates of the start point of the first specified line segment
* @param p1_2 the coordinates of the end point of the first specified line segment
* @param p2_1 the coordinates of the start point of the second specified line segment
* @param p2_2 the coordinates of the end point of the second specified line segment
* @return null if no intersection was found, else a new Coord instance with the coordinates of the intersection
*/
public
static
Coord
getSegmentSegmentIntersection
(
Coord
p1_1
,
Coord
p1_2
,
Coord
p2_1
,
Coord
p2_2
)
{
long
x1
=
p1_1
.
getHighPrecLon
();
long
y1
=
p1_1
.
getHighPrecLat
();
long
x2
=
p1_2
.
getHighPrecLon
();
long
y2
=
p1_2
.
getHighPrecLat
();
long
x3
=
p2_1
.
getHighPrecLon
();
long
y3
=
p2_1
.
getHighPrecLat
();
long
x4
=
p2_2
.
getHighPrecLon
();
long
y4
=
p2_2
.
getHighPrecLat
();
if
(!
Line2D
.
linesIntersect
(
x1
,
y1
,
x2
,
y2
,
x3
,
y3
,
x4
,
y4
))
return
null
;
long
divider
=
(
y2
-
y1
)
*
(
x4
-
x3
)
-
(
y4
-
y3
)
*
(
x2
-
x1
);
if
(
divider
==
0
)
return
null
;
// parallel
double
h
=
((
y4
-
y3
)
*
(
x1
-
x4
)
-
(
y1
-
y4
)
*
(
x4
-
x3
))
/
(
double
)
divider
;
if
(
h
<
0
||
h
>
1
)
{
return
null
;
// intersection of lines is not on given segment
}
double
xs
=
x1
+
h
*
(
x2
-
x1
);
double
ys
=
y1
+
h
*
(
y2
-
y1
);
return
Coord
.
makeHighPrecCoord
((
int
)
Math
.
round
(
ys
),
(
int
)
Math
.
round
(
xs
));
}
}
src/uk/me/parabola/imgfmt/app/Area.java
View file @
4697e867
...
...
@@ -69,6 +69,28 @@ public class Area {
,
Utils
.
toMapUnit
(
maxLat
),
Utils
.
toMapUnit
(
maxLong
));
}
public
static
Area
getBBox
(
List
<
Coord
>
points
)
{
int
tmpMinLat
=
Integer
.
MAX_VALUE
;
int
tmpMaxLat
=
Integer
.
MIN_VALUE
;
int
tmpMinLong
=
Integer
.
MAX_VALUE
;
int
tmpMaxLong
=
Integer
.
MIN_VALUE
;
for
(
Coord
co
:
points
)
{
int
lat
=
co
.
getLatitude
();
if
(
lat
<
tmpMinLat
)
tmpMinLat
=
lat
;
if
(
lat
>
tmpMaxLat
)
tmpMaxLat
=
lat
;
int
lon
=
co
.
getLongitude
();
if
(
lon
<
tmpMinLong
)
tmpMinLong
=
lon
;
if
(
lon
>
tmpMaxLong
)
tmpMaxLong
=
lon
;
}
return
new
Area
(
tmpMinLat
,
tmpMinLong
,
tmpMaxLat
,
tmpMaxLong
);
}
public
int
getMinLat
()
{
return
minLat
;
}
...
...
src/uk/me/parabola/imgfmt/app/Coord.java
View file @
4697e867
...
...
@@ -50,6 +50,7 @@ public class Coord implements Comparable<Coord> {
private
final
static
short
END_OF_WAY
=
0x0200
;
// use only in WrongAngleFixer
private
final
static
short
HOUSENUMBER_NODE
=
0x0400
;
// start/end of house number interval
private
final
static
short
ADDED_HOUSENUMBER_NODE
=
0x0800
;
// node was added for house numbers
private
final
static
short
ON_COUNTRY_BORDER
=
0x1000
;
// node is on a country border
private
final
static
int
HIGH_PREC_BITS
=
30
;
public
final
static
int
DELTA_SHIFT
=
HIGH_PREC_BITS
-
24
;
...
...
@@ -340,7 +341,7 @@ public class Coord implements Comparable<Coord> {
}
/**
* @return if this is the beginning/end of a house number interval
* @return
true
if this is the beginning/end of a house number interval
*/
public
boolean
isNumberNode
(){
return
(
flags
&
HOUSENUMBER_NODE
)
!=
0
;
...
...
@@ -357,7 +358,7 @@ public class Coord implements Comparable<Coord> {
}
/**
* @return if this
is the beginning/end of a
house
number
interval
* @return
true
if this
was added by the
housenumber
processing
*/
public
boolean
isAddedNumberNode
(){
return
(
flags
&
ADDED_HOUSENUMBER_NODE
)
!=
0
;
...
...
@@ -373,6 +374,24 @@ public class Coord implements Comparable<Coord> {
this
.
flags
&=
~
ADDED_HOUSENUMBER_NODE
;
}
/**
* @return true if this was marked as place on country border.
*/
public
boolean
getOnCountryBorder
()
{
return
(
flags
&
ON_COUNTRY_BORDER
)
!=
0
;
}
/**
* Mark as place on a country border
* @param onCountryBorder
*/
public
void
setOnCountryBorder
(
boolean
onCountryBorder
)
{
if
(
onCountryBorder
)
this
.
flags
|=
ON_COUNTRY_BORDER
;
else
this
.
flags
&=
~
ON_COUNTRY_BORDER
;
}
public
int
hashCode
()
{
// Use a factor for latitude to span over the whole integer range:
// max lat: 4194304
...
...
@@ -682,7 +701,7 @@ public class Coord implements Comparable<Coord> {
* @return true if rounding error is large.
*/
public
boolean
hasAlternativePos
(){
if
(
getOnBoundary
())
if
(
getOnBoundary
()
||
getOnCountryBorder
()
)
return
false
;
return
(
Math
.
abs
(
latDelta
)
>
MAX_DELTA
||
Math
.
abs
(
lonDelta
)
>
MAX_DELTA
);
}
...
...
@@ -694,7 +713,7 @@ public class Coord implements Comparable<Coord> {
*/
public
List
<
Coord
>
getAlternativePositions
(){
ArrayList
<
Coord
>
list
=
new
ArrayList
<>();
if
(
getOnBoundary
())
if
(
getOnBoundary
()
||
getOnCountryBorder
()
)
return
list
;
int
modLatDelta
=
0
;
int
modLonDelta
=
0
;
...
...
src/uk/me/parabola/imgfmt/app/CoordNode.java
View file @
4697e867
...
...
@@ -32,19 +32,22 @@ public class CoordNode extends Coord {
* @param longitude The longitude in map units.
* @param id The ID of this routing node.
* @param boundary This is a routing node on the boundary.
* @param onCountryBorder This is a routing node on a country boundary.
*/
public
CoordNode
(
int
latitude
,
int
longitude
,
int
id
,
boolean
boundary
)
{
public
CoordNode
(
int
latitude
,
int
longitude
,
int
id
,
boolean
boundary
,
boolean
onCountryBorder
)
{
super
(
latitude
,
longitude
);
this
.
id
=
id
;
setOnBoundary
(
boundary
);
setOnCountryBorder
(
onCountryBorder
);
setNumberNode
(
true
);
preserved
(
true
);
}
public
CoordNode
(
Coord
other
,
int
id
,
boolean
boundary
)
{
public
CoordNode
(
Coord
other
,
int
id
,
boolean
boundary
,
boolean
onCountryBorder
)
{
super
(
other
);
this
.
id
=
id
;
setOnBoundary
(
boundary
);
setOnCountryBorder
(
onCountryBorder
);
setNumberNode
(
true
);
preserved
(
true
);
...
...
src/uk/me/parabola/imgfmt/app/net/RouteNode.java
View file @
4697e867
...
...
@@ -76,7 +76,7 @@ public class RouteNode implements Comparable<RouteNode> {
public
RouteNode
(
Coord
coord
)
{
this
.
coord
=
(
CoordNode
)
coord
;
setBoundary
(
this
.
coord
.
getOnBoundary
());
setBoundary
(
this
.
coord
.
getOnBoundary
()
||
this
.
coord
.
getOnCountryBorder
()
);
}
private
boolean
haveLargeOffsets
()
{
...
...
src/uk/me/parabola/imgfmt/app/trergn/RGNFileReader.java
View file @
4697e867
...
...
@@ -403,7 +403,7 @@ public class RGNFileReader extends ImgReader {
Utils
.
toDegrees
(
currLon
)));
if
(
extra
)
line
.
addCoord
(
new
CoordNode
(
currLat
,
currLon
,
0
/* XXX */
,
false
));
line
.
addCoord
(
new
CoordNode
(
currLat
,
currLon
,
0
/* XXX */
,
false
,
false
));
else
line
.
addCoord
(
new
Coord
(
currLat
,
currLon
));
...
...
@@ -478,7 +478,7 @@ public class RGNFileReader extends ImgReader {
currLon
+=
dx
<<
(
24
-
div
.
getResolution
());
Coord
coord
;
if
(
isnode
)
coord
=
new
CoordNode
(
currLat
,
currLon
,
0
/* XXX */
,
false
);
coord
=
new
CoordNode
(
currLat
,
currLon
,
0
/* XXX */
,
false
,
false
);
else
coord
=
new
Coord
(
currLat
,
currLon
);
...
...
src/uk/me/parabola/mkgmap/filters/DouglasPeuckerFilter.java
View file @
4697e867
...
...
@@ -68,10 +68,6 @@ public class DouglasPeuckerFilter implements MapFilter {
int
endIndex
=
coords
.
size
()-
1
;
for
(
int
i
=
endIndex
-
1
;
i
>
0
;
i
--)
{
Coord
p
=
coords
.
get
(
i
);
//int highwayCount = p.getHighwayCount();
// If a node in the line use the douglas peucker algorithm for upper segment
// TODO: Should consider only nodes connected to roads visible at current resolution.
if
(
p
.
preserved
())
{
// point is "preserved", don't remove it
douglasPeucker
(
coords
,
i
,
endIndex
,
maxErrorDistance
);
...
...
@@ -100,8 +96,9 @@ public class DouglasPeuckerFilter implements MapFilter {
*/
public
static
void
douglasPeucker
(
List
<
Coord
>
points
,
int
startIndex
,
int
endIndex
,
double
allowedError
)
{
if
(
startIndex
>
=
endIndex
)
if
(
endIndex
-
startIndex
<
=
1
)
{
return
;
}
double
maxDistance
=
0
;
//Highest distance
int
maxIndex
=
endIndex
;
//Index of highest distance
...
...
@@ -109,9 +106,7 @@ public class DouglasPeuckerFilter implements MapFilter {
Coord
a
=
points
.
get
(
startIndex
);
Coord
b
=
points
.
get
(
endIndex
);
// Find point with highest distance to line between start- and end-point.
// handle also closed or nearly closed lines and spikes on straight lines
for
(
int
i
=
endIndex
-
1
;
i
>
startIndex
;
i
--)
{
Coord
p
=
points
.
get
(
i
);
double
distance
=
p
.
shortestDistToLineSegment
(
a
,
b
);
...
...
@@ -127,20 +122,14 @@ public class DouglasPeuckerFilter implements MapFilter {
}
else
{
// All points in tolerance, delete all of them.
// Remove the end-point if it is the same as the start point
if
(
a
.
highPrecEquals
(
b
)
&&
points
.
get
(
endIndex
).
preserved
()
==
false
)
endIndex
++;
if
(
endIndex
-
startIndex
>
4
)
{
// faster than many repeated remove actions
points
.
subList
(
startIndex
+
1
,
endIndex
).
clear
();
return
;
}
// Remove the points in between
}
else
{
for
(
int
i
=
endIndex
-
1
;
i
>
startIndex
;
i
--)
{
points
.
remove
(
i
);
}
}
}
}
}
src/uk/me/parabola/mkgmap/filters/RoundCoordsFilter.java
View file @
4697e867
...
...
@@ -55,7 +55,7 @@ public class RoundCoordsFilter implements MapFilter {
Coord
newP
;
if
(
p
instanceof
CoordNode
&&
checkRouting
)
newP
=
new
CoordNode
(
lat
,
lon
,
p
.
getId
(),
p
.
getOnBoundary
());
newP
=
new
CoordNode
(
lat
,
lon
,
p
.
getId
(),
p
.
getOnBoundary
()
,
p
.
getOnCountryBorder
()
);
else
newP
=
new
Coord
(
lat
,
lon
);
newP
.
preserved
(
p
.
preserved
());
...
...
src/uk/me/parabola/mkgmap/osmstyle/StyledConverter.java
View file @
4697e867
...
...
@@ -25,12 +25,16 @@ import java.util.HashSet;
import
java.util.IdentityHashMap
;
import
java.util.Iterator
;
import
java.util.LinkedHashMap
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.Map.Entry
;
import
java.util.logging.Level
;
import
it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
;
import
uk.me.parabola.imgfmt.ExitException
;
import
uk.me.parabola.imgfmt.Utils
;
import
uk.me.parabola.imgfmt.app.Area
;
import
uk.me.parabola.imgfmt.app.Coord
;
import
uk.me.parabola.imgfmt.app.CoordNode
;
...
...
@@ -60,6 +64,7 @@ import uk.me.parabola.mkgmap.reader.osm.Element;
import
uk.me.parabola.mkgmap.reader.osm.FakeIdGenerator
;
import
uk.me.parabola.mkgmap.reader.osm.FeatureKind
;
import
uk.me.parabola.mkgmap.reader.osm.GType
;
import
uk.me.parabola.mkgmap.reader.osm.MultiPolygonRelation
;
import
uk.me.parabola.mkgmap.reader.osm.Node
;
import
uk.me.parabola.mkgmap.reader.osm.OsmConverter
;
import
uk.me.parabola.mkgmap.reader.osm.Relation
;
...
...
@@ -70,6 +75,7 @@ import uk.me.parabola.mkgmap.reader.osm.TagDict;
import
uk.me.parabola.mkgmap.reader.osm.Tags
;
import
uk.me.parabola.mkgmap.reader.osm.TypeResult
;
import
uk.me.parabola.mkgmap.reader.osm.Way
;
import
uk.me.parabola.util.ElementQuadTree
;
import
uk.me.parabola.util.EnhancedProperties
;
import
uk.me.parabola.util.MultiHashMap
;
...
...
@@ -113,6 +119,12 @@ public class StyledConverter implements OsmConverter {
public
final
static
String
WAY_POI_NODE_IDS
=
"mkgmap:way-poi-node-ids"
;
private
final
HashMap
<
Integer
,
Map
<
String
,
MapPoint
>>
pointMap
;
/** boundary ways with admin_level=2 */
private
final
Set
<
Way
>
borders
=
new
LinkedHashSet
<>();
private
boolean
addBoundaryNodesAtAdminBoundaries
;
private
int
admLevelNod3
;
private
List
<
ConvertedWay
>
roads
=
new
ArrayList
<>();
private
List
<
ConvertedWay
>
lines
=
new
ArrayList
<>();
private
HashMap
<
Long
,
ConvertedWay
>
modifiedRoads
=
new
HashMap
<>();
...
...
@@ -210,6 +222,10 @@ public class StyledConverter implements OsmConverter {
String
styleOption
=
props
.
getProperty
(
"style-option"
,
null
);
styleOptionTags
=
parseStyleOption
(
styleOption
);
prefixSuffixFilter
=
new
PrefixSuffixFilter
(
props
);
// control calculation of extra nodes in NOD3 / NOD4
admLevelNod3
=
props
.
getProperty
(
"add-boundary-nodes-at-admin-boundaries"
,
2
);
addBoundaryNodesAtAdminBoundaries
=
routable
&&
admLevelNod3
>
0
;
}
/**
...
...
@@ -308,6 +324,13 @@ public class StyledConverter implements OsmConverter {
removeRestrictionsWithWay
(
Level
.
WARNING
,
way
,
"is ignored"
);
return
;
}
if
(
addBoundaryNodesAtAdminBoundaries
)
{
// is this a country border ?
if
(!
FakeIdGenerator
.
isFakeId
(
way
.
getId
())
&&
isNod3Border
(
way
))
{
borders
.
add
(
way
);
}
}
preConvertRules
(
way
);
String
styleFilterTag
=
way
.
getTag
(
styleFilterTagKey
);
...
...
@@ -372,6 +395,20 @@ public class StyledConverter implements OsmConverter {
}
}
private
boolean
isNod3Border
(
Element
el
)
{
if
(
"administrative"
.
equals
(
el
.
getTag
(
"boundary"
)))
{
String
admLevelString
=
el
.
getTag
(
"admin_level"
);
if
(
admLevelString
!=
null
)
{
try
{
int
al
=
Integer
.
valueOf
(
admLevelString
);
return
al
<=
admLevelNod3
;
}
catch
(
NumberFormatException
e
)
{
}
}
}
return
false
;
}
private
int
lineIndex
=
0
;
private
final
static
short
onewayTagKey
=
TagDict
.
getInstance
().
xlate
(
"oneway"
);
private
void
addConvertedWay
(
Way
way
,
GType
foundType
)
{
...
...
@@ -431,6 +468,7 @@ public class StyledConverter implements OsmConverter {
/** One type result for nodes to avoid recreating one for each node. */
private
NodeTypeResult
nodeTypeResult
=
new
NodeTypeResult
();
private
class
NodeTypeResult
implements
TypeResult
{
private
Node
node
;
/** flag if the rule was fired */
...
...
@@ -582,6 +620,124 @@ public class StyledConverter implements OsmConverter {
}
}
/**
* Find all intersections of roads with country borders.
* If a node exists close to the intersection, use the existing node, else add one. The nodes will be
* written as external nodes to NOD file (NOD3 + NOD4)
*/
private
void
checkRoutingNodesAtAdminBoundaries
()
{
if
(!
addBoundaryNodesAtAdminBoundaries
||
borders
.
isEmpty
())
return
;
long
t1
=
System
.
currentTimeMillis
();
// prepare boundary ways so that we can search them
List
<
Element
>
clippedBorders
=
new
ArrayList
<>();
for
(
Way
b
:
borders
)
{
List
<
List
<
Coord
>>
clipped
=
LineClipper
.
clip
(
bbox
,
b
.
getPoints
());
if
(
clipped
==
null
)
{
splitBoundary
(
clippedBorders
,
b
,
b
.
getPoints
());
}
else
{
for
(
List
<
Coord
>
lco
:
clipped
)
{
splitBoundary
(
clippedBorders
,
b
,
lco
);
}
}
}
ElementQuadTree
qt
=
new
ElementQuadTree
(
bbox
,
clippedBorders
);
long
countChg
=
0
;
Long2ObjectOpenHashMap
<
Coord
>
commonCoordMap
=
new
Long2ObjectOpenHashMap
<>();
for
(
ConvertedWay
r
:
roads
)
{
if
(!
r
.
isValid
())
continue
;
Way
way
=
r
.
getWay
();
Area
searchRect
=
Area
.
getBBox
(
way
.
getPoints
());
Set
<
Element
>
boundaries
=
qt
.
get
(
searchRect
);
if
(
boundaries
.
isEmpty
())
continue
;
// the bounding box of the road intersects with one or more bounding boxes of borders
Coord
pw1
=
way
.
getPoints
().
get
(
0
);
int
pos
=
1
;
while
(
pos
<
way
.
getPoints
().
size
())
{
boolean
changed
=
false
;
Coord
pw2
=
way
.
getPoints
().
get
(
pos
);
for
(
Element
el
:
boundaries
)
{
List
<
Coord
>
b
=
((
Way
)
el
).
getPoints
();
for
(
int
i
=
0
;
i
<
b
.
size
()
-
1
;
i
++)
{
Coord
pb1
=
b
.
get
(
i
);
Coord
pb2
=
b
.
get
(
i
+
1
);
Coord
is
=
Utils
.
getSegmentSegmentIntersection
(
pw1
,
pw2
,
pb1
,
pb2
);
if
(
is
!=
null
)
{
// intersection can be equal to given nodes on road or close to it
double
dist1
=
is
.
distance
(
pw1
);
double
dist2
=
is
.
distance
(
pw2
);
if
(
dist1
<
dist2
&&
dist1
<
1
)
{
if
(!
pw1
.
getOnCountryBorder
())
{
++
countChg
;
if
(!
pw1
.
getOnBoundary
())
log
.
info
(
"road intersects admin boundary, changing existing node to external routing node at"
,
pw1
.
toDegreeString
());
}
pw1
.
setOnCountryBorder
(
true
);
}
else
if
(
dist2
<
dist1
&&
dist2
<
1
)
{
if
(!
pw2
.
getOnCountryBorder
())
{
++
countChg
;
if
(!
pw2
.
getOnBoundary
())
log
.
info
(
"road intersects admin boundary, changing existing node to external routing node at"
,
pw2
.
toDegreeString
());
}
pw2
.
setOnCountryBorder
(
true
);
}
else
{
long
key
=
Utils
.
coord2Long
(
is
);
Coord
replacement
=
commonCoordMap
.
get
(
key
);
if
(
replacement
==
null
)
{
commonCoordMap
.
put
(
key
,
is
);
}
else
{
assert
is
.
highPrecEquals
(
replacement
);
is
=
replacement
;
}
is
.
setOnCountryBorder
(
true
);
log
.
info
(
"road intersects admin boundary, adding external routing node at"
,
is
.
toDegreeString
());
way
.
getPoints
().
add
(
pos
,
is
);
changed
=
true
;
pw2
=
is
;
}
}
}
}
if
(!
changed
)
{
++
pos
;
pw1
=
pw2
;
}
}
}
long
t2
=
System
.
currentTimeMillis
()
-
t1
;
log
.
info
(
"added"
,
commonCoordMap
.
size
(),
"new nodes at country borders"
);
log
.
info
(
"marked"
,
countChg
,
"existing nodes at country borders"
);
log
.
info
(
"adding country border routing nodes took "
+
t2
+
" ms"
);
}
/**
* Split complex border ways into smaller portions.
* @param clippedBorders
* @param orig
* @param points
*/
private
void
splitBoundary
(
List
<
Element
>
clippedBorders
,
Way
orig
,
List
<
Coord
>
points
)
{
int
pos
=
0
;
final
int
max
=
20
;
// seems to be a good compromise
while
(
pos
<
points
.
size
())
{
int
right
=
Math
.
min
(
points
.
size
(),
pos
+
max
);
Way
w
=
new
Way
(
orig
.
getId
(),
points
.
subList
(
pos
,
right
));
w
.
setFakeId
();
clippedBorders
.
add
(
w
);
pos
+=
max
-
1
;
if
(
pos
+
1
==
points
.
size
())
pos
--;
}
}
/**
* Merges roads with identical attributes (GType, OSM tags) to reduce the size of the
* road network.
...
...
@@ -601,6 +757,9 @@ public class StyledConverter implements OsmConverter {
style
.
reportStats
();
driveOnLeft
=
calcDrivingSide
();
checkRoutingNodesAtAdminBoundaries
();
borders
.
clear
();
setHighwayCounts
();
findUnconnectedRoads
();
rotateClosedWaysToFirstNode
();
...
...
@@ -636,7 +795,6 @@ public class StyledConverter implements OsmConverter {
}
deletedRoads
=
null
;
modifiedRoads
=
null
;
mergeRoads
();
resetHighwayCounts
();
...
...
@@ -961,6 +1119,17 @@ public class StyledConverter implements OsmConverter {
}
else
if
(
"through_route"
.
equals
(
relation
.
getTag
(
"type"
)))
{
throughRouteRelations
.
add
(
relation
);
}
else
if
(
addBoundaryNodesAtAdminBoundaries
)
{
if
(
relation
instanceof
MultiPolygonRelation
||
"boundary"
.
equals
(
relation
.
getTag
(
"type"
)))
{
if
(
isNod3Border
(
relation
))
{
for
(
Entry
<
String
,
Element
>
e
:
relation
.
getElements
())
{
if
(
FakeIdGenerator
.
isFakeId
(
e
.
getValue
().
getId
()))
continue
;
if
(
e
.
getValue
()
instanceof
Way
)
borders
.
add
((
Way
)
e
.
getValue
());
}
}
}
}
}
...
...
@@ -1361,7 +1530,7 @@ public class StyledConverter implements OsmConverter {
nWay
.
copyTags
(
way
);
for
(
Coord
co
:
lco
)
{
nWay
.
addPoint
(
co
);
if
(
co
.
getOnBoundary
())
{
if
(
co
.
getOnBoundary
()
||
co
.
getOnCountryBorder
()
)
{
// this point lies on a boundary
// make sure it becomes a node
co
.
incHighwayCount
();
...
...
@@ -1631,12 +1800,12 @@ public class StyledConverter implements OsmConverter {
arcLength
+=
d
;
}
}
if
(
p
.
getHighwayCount
()
>
1
)
{
if
(
p
.
getHighwayCount
()
>
1
||
p
.
getOnCountryBorder
()
)
{
// this point is a node connecting highways
CoordNode
coordNode
=
nodeIdMap
.
get
(
p
);
if
(
coordNode
==
null
)
{
// assign a node id
coordNode
=
new
CoordNode
(
p
,
nextNodeId
++,
p
.
getOnBoundary
());
coordNode
=
new
CoordNode
(
p
,
nextNodeId
++,
p
.
getOnBoundary
()
,
p
.
getOnCountryBorder
()
);
nodeIdMap
.
put
(
p
,
coordNode
);
}
...
...
@@ -1751,7 +1920,7 @@ public class StyledConverter implements OsmConverter {
Coord
coord
=
points
.
get
(
n
);
CoordNode
thisCoordNode
=
nodeIdMap
.
get
(
coord
);
assert
thisCoordNode
!=
null
:
"Way "
+
debugWayName
+
" node "
+
i
+
" (point index "
+
n
+
") at "
+
coord
.
toOSMURL
()
+
" yields a null coord node"
;
boolean
boundary
=
coord
.
getOnBoundary
();
boolean
boundary
=
coord
.
getOnBoundary
()
||
coord
.
getOnCountryBorder
()
;
if
(
boundary
&&
log
.
isInfoEnabled
())
{
log
.
info
(
"Way"
,
debugWayName
+
"'s point #"
+
n
,
"at"
,
coord
.
toOSMURL
(),
"is a boundary node"
);
}
...
...
src/uk/me/parabola/mkgmap/osmstyle/WrongAngleFixer.java
View file @
4697e867
...
...
@@ -149,6 +149,13 @@ public class WrongAngleFixer {
}
replacement
.
setOnBoundary
(
true
);
}
if
(
toRepl
.
getOnCountryBorder
()){
if
(
replacement
.
equals
(
toRepl
)
==
false
){
log
.
error
(
"country boundary node is replaced by node with non-equal coordinates at"
,
toRepl
.
toOSMURL
());
assert
false
:
"country boundary node is replaced"
;
}
replacement
.
setOnCountryBorder
(
true
);
}
toRepl
.
setReplaced
(
true
);
if
(
toRepl
instanceof
CoordPOI
)
{
CoordPOI
cp
=
(
CoordPOI
)
toRepl
;
...
...
@@ -834,7 +841,7 @@ public class WrongAngleFixer {
* @return true if remove is okay
*/
private
boolean
allowedToRemove
(
Coord
p
){
if
(
p
.
getOnBoundary
())
if
(
p
.
getOnBoundary
()
||
p
.
getOnCountryBorder
()
)
return
false
;
if
(
mode
==
MODE_LINES
&&
p
.
isEndOfWay
())
return
false
;
...
...
@@ -1100,26 +1107,27 @@ public class WrongAngleFixer {
}
Coord
c
=
getCurrentLocation
(
replacements
);
Coord
n
=
neighbour
.
getCurrentLocation
(
replacements
);
// check special cases: don't merge if
// 1) both points are via nodes
// 2) both nodes are boundary nodes with non-equal coords
// 3) one point is via node and the other is a boundary node, the result could be that the restriction is ignored.
if
(
c
.
getOnBoundary
()){
if
(
n
.
isViaNodeOfRestriction
()
||
n
.
getOnBoundary
()
&&
c
.
equals
(
n
)
==
false
)
boolean
cOnBoundary
=
c
.
getOnBoundary
()
||
c
.
getOnCountryBorder
();
boolean
nOnBoundary
=
n
.
getOnBoundary
()
||
n
.
getOnCountryBorder
();
if
(
cOnBoundary
){
if
(
n
.
isViaNodeOfRestriction
()
||
nOnBoundary
&&
c
.
equals
(
n
)
==
false
)
return
false
;
}
if
(
c
.
isViaNodeOfRestriction
()
&&
(
n
.
isViaNodeOfRestriction
()
||
n
.
get
OnBoundary
()
))
if
(
c
.
isViaNodeOfRestriction
()
&&
(
n
.
isViaNodeOfRestriction
()
||
nOnBoundary
))
return
false
;
if
(
c
instanceof
CoordPOI
&&
(
n
instanceof
CoordPOI
||
n
.
get
OnBoundary
()
))
if
(
c
instanceof
CoordPOI
&&
(
n
instanceof
CoordPOI
||
nOnBoundary
))
return
false
;
if
(
n
instanceof
CoordPOI
&&
(
c
instanceof
CoordPOI
||
c
.
get
OnBoundary
()
))
if
(
n
instanceof
CoordPOI
&&
(
c
instanceof
CoordPOI
||
cOnBoundary
))
return
false
;
//TODO: nodes on country borders?
Coord
mergePoint
;
if
(
c
.
get
OnBoundary
()
||
c
instanceof
CoordPOI
)
if
(
cOnBoundary
||
c
instanceof
CoordPOI
)
mergePoint
=
c
;
else
if
(
n
.
get
OnBoundary
()
||
n
instanceof
CoordPOI
)
else
if
(
nOnBoundary
||
n
instanceof
CoordPOI
)
mergePoint
=
n
;
else
if
(
c
.
equals
(
n
))
mergePoint
=
c
;
...
...
src/uk/me/parabola/mkgmap/osmstyle/housenumber/ExtNumbers.java
View file @
4697e867
...
...
@@ -944,6 +944,7 @@ public class ExtNumbers {
Coord
closePoint
=
getRoad
().
getPoints
().
get
(
index
);
Coord
toAdd
=
new
Coord
(
closePoint
);
toAdd
.
setOnBoundary
(
closePoint
.
getOnBoundary
());
toAdd
.
setOnCountryBorder
(
closePoint
.
getOnCountryBorder
());
toAdd
.
incHighwayCount
();
// we have to make sure that the road starts and ends with a CoordNode!
this
.
endInRoad
=
addAsNumberNode
(
splitSegment
,
toAdd
);
...
...
src/uk/me/parabola/mkgmap/reader/osm/CoastlineFileLoader.java
View file @
4697e867
...
...
@@ -152,26 +152,7 @@ public final class CoastlineFileLoader {
if
(
log
.
isDebugEnabled
())
log
.
debug
(
"Create coastline way"
,
id
,
"with"
,
points
.
size
(),
"points"
);
Coord
firstPoint
=
getPoints
().
get
(
0
);
int
minLat
=
firstPoint
.
getLatitude
();
int
maxLat
=
firstPoint
.
getLatitude
();
int
minLong
=
firstPoint
.
getLongitude
();
int
maxLong
=
firstPoint
.
getLongitude
();
for
(
Coord
c
:
getPoints
())
{
if
(
c
.
getLatitude
()
<
minLat
)
{
minLat
=
c
.
getLatitude
();
}
else
if
(
c
.
getLatitude
()
>
maxLat
)
{
maxLat
=
c
.
getLatitude
();
}
if
(
c
.
getLongitude
()
<
minLong
)
{
minLong
=
c
.
getLongitude
();
}
else
if
(
c
.
getLongitude
()
>
maxLong
)
{
maxLong
=
c
.
getLongitude
();
}
}
bbox
=
new
Area
(
minLat
,
minLong
,
maxLat
,
maxLong
);
bbox
=
Area
.
getBBox
(
points
);
}
@Override
...
...
src/uk/me/parabola/mkgmap/reader/osm/ElementSaver.java
View file @
4697e867
...
...
@@ -67,9 +67,7 @@ public class ElementSaver {
private
final
boolean
ignoreTurnRestrictions
;
/** name of the tag that contains a ;-separated list of tag names that should be removed after all elements have been processed */
public
static
final
String
MKGMAP_REMOVE_TAG
=
"mkgmap:removetags"
;
/** tagvalue of the {@link ElementSaver#MKGMAP_REMOVE_TAG} if all tags should be removed */
public
static
final
String
MKGMAP_REMOVE_TAG_ALL_KEY
=
"mkgmap:ALL"
;
public
static
final
short
MKGMAP_REMOVE_TAG_KEY
=
TagDict
.
getInstance
().
xlate
(
"mkgmap:removetags"
);
public
ElementSaver
(
EnhancedProperties
args
)
{
if
(
args
.
getProperty
(
"preserve-element-order"
,
false
))
{
...
...
src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonFinishHook.java
View file @
4697e867
...
...
@@ -34,23 +34,18 @@ public class MultiPolygonFinishHook extends OsmReadingHooksAdaptor {
long
t1
=
System
.
currentTimeMillis
();
log
.
info
(
"Finishing multipolygons"
);
for
(
Way
way
:
saver
.
getWays
().
values
())
{
String
removeTag
=
way
.
getTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
);
String
removeTag
=
way
.
getTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
_KEY
);
if
(
removeTag
==
null
)
{
continue
;
}
if
(
ElementSaver
.
MKGMAP_REMOVE_TAG_ALL_KEY
.
equals
(
removeTag
))
{
if
(
log
.
isDebugEnabled
())
log
.
debug
(
"Remove all tags from way"
,
way
.
getId
(),
way
.
toTagString
());
way
.
removeAllTags
();
}
else
{
String
[]
tagsToRemove
=
removeTag
.
split
(
";"
);
if
(
log
.
isDebugEnabled
())
if
(
log
.
isDebugEnabled
())
{
log
.
debug
(
"Remove tags"
,
Arrays
.
toString
(
tagsToRemove
),
"from way"
,
way
.
getId
(),
way
.
toTagString
());
}
way
.
deleteTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG_KEY
);
for
(
String
rTag
:
tagsToRemove
)
{
way
.
deleteTag
(
rTag
);
}
way
.
deleteTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
);
}
}
log
.
info
(
"Multipolygon hook finished in "
+(
System
.
currentTimeMillis
()-
t1
)+
" ms"
);
...
...
src/uk/me/parabola/mkgmap/reader/osm/MultiPolygonRelation.java
View file @
4697e867
...
...
@@ -18,6 +18,7 @@ import java.awt.Rectangle;
import
java.awt.geom.Area
;
import
java.awt.geom.Line2D
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.BitSet
;
import
java.util.Collection
;
import
java.util.Collections
;
...
...
@@ -37,7 +38,6 @@ import java.util.Set;
import
java.util.concurrent.LinkedBlockingQueue
;
import
java.util.logging.Level
;
import
uk.me.parabola.imgfmt.Utils
;
import
uk.me.parabola.imgfmt.app.Coord
;
import
uk.me.parabola.log.Logger
;
import
uk.me.parabola.util.Java2DConverter
;
...
...
@@ -1632,7 +1632,7 @@ public class MultiPolygonRelation extends Relation {
||
(
prevLatField
==
0
&&
prevLonField
==
0
);
boolean
intersects
=
intersectionPossible
&&
Utils
.
linesCutEachOther
(
p1_1
,
p1_2
,
p2_1
,
p2_2
);
&&
linesCutEachOther
(
p1_1
,
p1_2
,
p2_1
,
p2_2
);
if
(
intersects
)
{
if
((
polygon1
.
getWay
().
isClosedArtificially
()
&&
!
it1
.
hasNext
())
...
...
@@ -1725,12 +1725,54 @@ public class MultiPolygonRelation extends Relation {
Coord
sw
=
new
Coord
(
tileBounds
.
getMinLat
(),
tileBounds
.
getMinLong
());
Coord
se
=
new
Coord
(
tileBounds
.
getMinLat
(),
tileBounds
.
getMaxLong
());
Coord
ne
=
new
Coord
(
tileBounds
.
getMaxLat
(),
tileBounds
.
getMaxLong
());
return
Utils
.
linesCutEachOther
(
nw
,
sw
,
p1_1
,
p1_2
)
||
Utils
.
linesCutEachOther
(
sw
,
se
,
p1_1
,
p1_2
)
||
Utils
.
linesCutEachOther
(
se
,
ne
,
p1_1
,
p1_2
)
||
Utils
.
linesCutEachOther
(
ne
,
nw
,
p1_1
,
p1_2
);
return
linesCutEachOther
(
nw
,
sw
,
p1_1
,
p1_2
)
||
linesCutEachOther
(
sw
,
se
,
p1_1
,
p1_2
)
||
linesCutEachOther
(
se
,
ne
,
p1_1
,
p1_2
)
||
linesCutEachOther
(
ne
,
nw
,
p1_1
,
p1_2
);
}
/**
* XXX: This code presumes that certain tests were already done!
* Check if the line p1_1 to p1_2 cuts line p2_1 to p2_2 in two pieces and vice versa.
* This is a form of intersection check where it is allowed that one line ends on the
* other line or that the two lines overlap.
* @param p1_1 first point of line 1
* @param p1_2 second point of line 1
* @param p2_1 first point of line 2
* @param p2_2 second point of line 2
* @return true if both lines intersect somewhere in the middle of each other
*/
private
static
boolean
linesCutEachOther
(
Coord
p1_1
,
Coord
p1_2
,
Coord
p2_1
,
Coord
p2_2
)
{
long
width1
=
p1_2
.
getHighPrecLon
()
-
p1_1
.
getHighPrecLon
();
long
width2
=
p2_2
.
getHighPrecLon
()
-
p2_1
.
getHighPrecLon
();
long
height1
=
p1_2
.
getHighPrecLat
()
-
p1_1
.
getHighPrecLat
();
long
height2
=
p2_2
.
getHighPrecLat
()
-
p2_1
.
getHighPrecLat
();
long
denominator
=
((
height2
*
width1
)
-
(
width2
*
height1
));
if
(
denominator
==
0
)
{
// the lines are parallel
// they might overlap but this is ok for this test
return
false
;
}
long
x1Mx3
=
p1_1
.
getHighPrecLon
()
-
p2_1
.
getHighPrecLon
();
long
y1My3
=
p1_1
.
getHighPrecLat
()
-
p2_1
.
getHighPrecLat
();
double
isx
=
(
double
)((
width2
*
y1My3
)
-
(
height2
*
x1Mx3
))
/
denominator
;
if
(
isx
<=
0
||
isx
>=
1
)
{
return
false
;
}
double
isy
=
(
double
)((
width1
*
y1My3
)
-
(
height1
*
x1Mx3
))
/
denominator
;
if
(
isy
<=
0
||
isy
>=
1
)
{
return
false
;
}
return
true
;
}
private
List
<
JoinedWay
>
getWaysFromPolygonList
(
BitSet
selection
)
{
if
(
selection
.
isEmpty
())
{
...
...
@@ -1873,11 +1915,9 @@ public class MultiPolygonRelation extends Relation {
* @param way
* a joined way
* @param tagname
* the tag to be removed (<code>null</code> means remove all
* tags)
* the tag to be removed
* @param tagvalue
* the value of the tag to be removed (<code>null</code> means
* don't check the value)
* the value of the tag to be removed
*/
private
void
removeTagInOrgWays
(
JoinedWay
way
,
String
tagname
,
String
tagvalue
)
{
...
...
@@ -1888,54 +1928,33 @@ public class MultiPolygonRelation extends Relation {
continue
;
}
boolean
remove
=
false
;
if
(
tagname
==
null
)
{
// remove all tags
remove
=
true
;
}
else
if
(
tagvalue
==
null
)
{
// remove the tag without comparing the value
remove
=
w
.
getTag
(
tagname
)
!=
null
;
}
else
if
(
tagvalue
.
equals
(
w
.
getTag
(
tagname
)))
{
remove
=
true
;
if
(
tagvalue
.
equals
(
w
.
getTag
(
tagname
)))
{
if
(
log
.
isDebugEnabled
())
{
log
.
debug
(
"Will remove"
,
tagname
+
"="
+
w
.
getTag
(
tagname
),
"from way"
,
w
.
getId
(),
w
.
toTagString
());
}
if
(
remove
)
{
if
(
tagname
==
null
)
{
// remove all tags
if
(
log
.
isDebugEnabled
())
log
.
debug
(
"Will remove all tags from"
,
w
.
getId
(),
w
.
toTagString
());
removeTagsInOrgWays
(
w
,
tagname
);
}
else
{
if
(
log
.
isDebugEnabled
())
log
.
debug
(
"Will remove"
,
tagname
+
"="
+
w
.
getTag
(
tagname
),
"from way"
,
w
.
getId
(),
w
.
toTagString
());
removeTagsInOrgWays
(
w
,
tagname
);
}
}
}
}
protected
void
removeTagsInOrgWays
(
Way
way
,
String
tag
)
{
if
(
tag
==
null
)
{
way
.
addTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
,
ElementSaver
.
MKGMAP_REMOVE_TAG_ALL_KEY
);
if
(
tag
==
null
||
tag
.
isEmpty
())
{
return
;
}
if
(
tag
.
isEmpty
())
{
String
tagsToRemove
=
way
.
getTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG_KEY
);
if
(
tagsToRemove
==
null
)
{
tagsToRemove
=
tag
;
}
else
if
(
tag
.
equals
(
tagsToRemove
))
{
return
;
}
String
removedTagsTag
=
way
.
getTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
);
if
(
ElementSaver
.
MKGMAP_REMOVE_TAG_ALL_KEY
.
equals
(
removedTagsTag
))
{
// cannot add more tags to remove
}
else
{
String
[]
keys
=
tagsToRemove
.
split
(
";"
);
if
(
Arrays
.
asList
(
keys
).
contains
(
tag
))
{
return
;
}
if
(
removedTagsTag
==
null
)
{
way
.
addTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
,
tag
);
}
else
if
(
removedTagsTag
.
equals
(
tag
)
==
false
)
{
way
.
addTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG
,
removedTagsTag
+
";"
+
tag
);
tagsToRemove
+=
";"
+
tag
;
}
way
.
addTag
(
ElementSaver
.
MKGMAP_REMOVE_TAG_KEY
,
tagsToRemove
);
}
/**
...
...
Prev
1
2
Next