use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class MultiPolygonRelation method runIntersectionCheck.
private void runIntersectionCheck(BitSet unfinishedPolys) {
if (intersectingPolygons.isEmpty()) {
// nothing to do
return;
}
log.warn("Some polygons are intersecting. This is not allowed in multipolygons.");
boolean oneOufOfBbox = false;
for (JoinedWay polygon : intersectingPolygons) {
int pi = polygons.indexOf(polygon);
unfinishedPolys.clear(pi);
boolean outOfBbox = false;
for (Coord c : polygon.getPoints()) {
if (!tileBounds.contains(c)) {
outOfBbox = true;
oneOufOfBbox = true;
break;
}
}
logWayURLs(Level.WARNING, (outOfBbox ? "*" : "-"), polygon);
}
for (JoinedWay polygon : intersectingPolygons) {
// print out the details of the original ways
logFakeWayDetails(Level.WARNING, polygon);
}
if (oneOufOfBbox) {
log.warn("Some of these intersections/overlaps may be caused by incomplete data on bounding box edges (*).");
}
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class RestrictionRelation method eval.
/**
* The evaluation should happen after style processing.
* Normally this is called from the {@link RelationStyleHook}
* Performs also diverse plausibility checks.
* @param bbox tile boundary
*/
public void eval(Area bbox) {
if (evalWasCalled) {
log.error(messagePrefix, "internal error: eval() was already called");
fromWayIds.clear();
toWayIds.clear();
viaWayIds.clear();
}
evalWasCalled = true;
if (getTag("type") == null) {
// style removed the tag
log.info(messagePrefix, "type tag was removed, relation is ignored");
valid = false;
return;
}
List<Way> fromWays = new ArrayList<>();
List<Way> toWays = new ArrayList<>();
List<Way> viaWays = new ArrayList<>();
final String browseURL = toBrowseURL();
valid = true;
// find out what kind of restriction we have and to which vehicles it applies
exceptMask = DEFAULT_EXCEPT_MASK;
String specifc_type = getTag("restriction");
int count_unknown = 0;
Map<String, String> vehicles = getTagsWithPrefix("restriction:", true);
if (vehicles.isEmpty() == false) {
exceptMask = (byte) 0xff;
Iterator<Entry<String, String>> iter = vehicles.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, String> entry = iter.next();
String vehicle = entry.getKey();
if (setExceptMask(vehicle, false) == false)
count_unknown++;
if (specifc_type == null)
specifc_type = entry.getValue();
else if (specifc_type.equals(entry.getValue()) == false) {
log.warn(messagePrefix, "is invalid, it specifies different kinds of turns");
valid = false;
break;
}
}
if (valid && vehicles.size() == count_unknown) {
log.warn(messagePrefix, "no supported vehicle in turn restriction");
valid = false;
return;
}
}
if (specifc_type == null) {
// style removed the tag
log.info(messagePrefix, "no valid restriction tag found");
valid = false;
return;
}
restriction = specifc_type.trim();
messagePrefix = "Turn restriction (" + restriction + ") " + browseURL;
if (supportedRestrictions.contains(restriction) == false) {
log.warn(messagePrefix, "ignoring unsupported restriction type '" + restriction + "'");
valid = false;
return;
}
String dirInfo = "";
if (restriction.contains("left"))
dirInfo += "l";
if (restriction.contains("right"))
dirInfo += "r";
if (restriction.contains("straight"))
dirInfo += "s";
if (restriction.endsWith("u_turn"))
dirInfo += "u";
if (dirInfo.length() > 1) {
log.warn(messagePrefix, "ignoring unsupported restriction type '" + restriction + "'");
valid = false;
return;
} else if (dirInfo.length() == 1) {
dirIndicator = dirInfo.charAt(0);
} else
dirIndicator = '?';
String type = getTag("type");
if (type.startsWith("restriction:")) {
exceptMask = (byte) 0xff;
String vehicle = type.substring("restriction:".length());
if (setExceptMask(vehicle, false) == false) {
log.warn(messagePrefix, "ignoring unsupported '" + vehicle + "' in turn restriction");
valid = false;
return;
}
}
String except = getTag("except");
if (except != null) {
for (String vehicle : except.split("[,;]")) {
// be nice
vehicle = vehicle.trim();
setExceptMask(vehicle, true);
}
}
for (String unsupportedTag : unsupportedTags) {
if (getTag(unsupportedTag) != null) {
log.warn(messagePrefix, "ignoring unsupported '" + unsupportedTag + "' tag");
}
}
// evaluate members
for (Map.Entry<String, Element> pair : getElements()) {
String role = pair.getKey();
Element el = pair.getValue();
Coord location = null;
if (viaCoord != null)
location = viaCoord;
else if (!fromWays.isEmpty() && !fromWays.get(0).getPoints().isEmpty())
location = fromWays.get(0).getPoints().get(0);
else if (!toWays.isEmpty() && !toWays.get(0).getPoints().isEmpty())
location = toWays.get(0).getPoints().get(0);
else if (!viaWays.isEmpty() && !viaWays.get(0).getPoints().isEmpty())
location = viaWays.get(0).getPoints().get(0);
if (location != null)
messagePrefix = "Turn restriction (" + restriction + ") " + browseURL + " (at " + location.toOSMURL() + ")";
if ("to".equals(role)) {
if (!(el instanceof Way)) {
log.warn(messagePrefix, "'to' member", el.toBrowseURL(), "is not a way but it should be");
} else if (((Way) el).getPoints().isEmpty()) {
log.warn(messagePrefix, "ignoring empty 'to' way", el.toBrowseURL());
} else
toWays.add((Way) el);
} else if ("from".equals(role)) {
if (!(el instanceof Way)) {
log.warn(messagePrefix, "'from' member", el.toBrowseURL(), "is not a way but it should be");
} else if (((Way) el).getPoints().isEmpty()) {
log.warn(messagePrefix, "ignoring empty 'from' way", el.toBrowseURL());
} else
fromWays.add((Way) el);
} else if ("via".equals(role)) {
if (el instanceof Node) {
if (viaCoord != null) {
log.warn(messagePrefix, "has extra 'via' node", el.toBrowseURL());
valid = false;
} else
viaCoord = ((Node) el).getLocation();
} else if (el instanceof Way) {
if (viaCoord != null) {
log.warn(messagePrefix, "has extra 'via' way", el.toBrowseURL());
valid = false;
} else
viaWays.add((Way) el);
} else {
log.warn(messagePrefix, "'via' member", el.toBrowseURL(), "is not a node or way");
}
} else if ("location_hint".equals(role)) {
// relax - we don't care about this
} else {
log.warn(messagePrefix, "unknown member role '" + role + "'");
}
}
if (!valid)
return;
if ("no_entry".equals(restriction) == false) {
if (fromWays.size() > 1) {
log.warn(messagePrefix, "multiple 'from' members are only accepted for no_entry restrictions");
valid = false;
return;
}
}
if ("no_exit".equals(restriction) == false) {
if (toWays.size() > 1) {
log.warn(messagePrefix, "multiple 'to' members are only accepted for no_exit restrictions");
valid = false;
return;
}
}
if (viaWays.isEmpty() && viaCoord == null && fromWays.size() == 1 && toWays.size() == 1) {
Way fromWay = fromWays.get(0);
Way toWay = toWays.get(0);
List<Coord> fromPoints = fromWay.getPoints();
List<Coord> toPoints = toWay.getPoints();
int countSame = 0;
for (Coord fp : fromPoints) {
for (Coord tp : toPoints) {
if (fp == tp) {
countSame++;
viaCoord = fp;
}
}
}
if (countSame > 1) {
log.warn(messagePrefix, "lacks 'via' node and way and the 'from' (", fromWay.toBrowseURL(), ") and 'to' (", toWay.toBrowseURL(), ") ways connect in more than one place");
valid = false;
} else if (viaCoord == null) {
log.warn(messagePrefix, "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + toWay.toBrowseURL() + ") ways don't connect");
valid = false;
} else {
if (fromPoints.get(0) != viaCoord && fromPoints.get(fromPoints.size() - 1) != viaCoord || toPoints.get(0) != viaCoord && toPoints.get(toPoints.size() - 1) != viaCoord) {
log.warn(messagePrefix, "lacks 'via' node and the 'from' (" + fromWay.toBrowseURL() + ") and 'to' (" + toWay.toBrowseURL() + ") ways don't connect at an end point");
valid = false;
} else
log.warn(messagePrefix, "lacks 'via' node (guessing it should be at", viaCoord.toOSMURL() + ", why don't you add it to the OSM data?)");
}
}
if (fromWays.isEmpty()) {
log.warn(messagePrefix, "lacks 'from' way");
valid = false;
}
if (toWays.isEmpty()) {
log.warn(messagePrefix, "lacks 'to' way");
valid = false;
}
if ((fromWays.size() > 1 || toWays.size() > 1) && viaWays.isEmpty() == false) {
log.warn(messagePrefix, "'via' way(s) are not supported with multiple 'from' or 'to' ways");
valid = false;
}
if (!valid)
return;
for (List<Way> ways : Arrays.asList(fromWays, viaWays, toWays)) {
for (Way way : ways) {
if (way.getPoints().size() < 2) {
log.warn(messagePrefix, "way", way.toBrowseURL(), "has less than 2 points, restriction is ignored");
valid = false;
} else {
if (way.getPoints().get(0) == way.getPoints().get(way.getPoints().size() - 1)) {
if (ways == toWays && dirIndicator != '?')
// we try to determine the correct part in RoadNetwork
continue;
log.warn(messagePrefix, "way", way.toBrowseURL(), "starts and ends at same node, don't know which one to use");
valid = false;
}
}
}
}
if (!valid)
return;
if (viaPoints.isEmpty() == false)
viaCoord = viaPoints.get(0);
if (viaCoord == null && viaWays.isEmpty()) {
valid = false;
return;
}
viaPoints.clear();
Coord v1 = viaCoord;
Coord v2 = viaCoord;
if (viaWays.isEmpty() == false) {
v1 = viaWays.get(0).getPoints().get(0);
v2 = viaWays.get(0).getPoints().get(viaWays.get(0).getPoints().size() - 1);
}
// check if all from ways are connected at the given via point or with the given via ways
for (Way fromWay : fromWays) {
Coord e1 = fromWay.getPoints().get(0);
Coord e2 = fromWay.getPoints().get(fromWay.getPoints().size() - 1);
if (e1 == v1 || e2 == v1)
viaCoord = v1;
else if (e1 == v2 || e2 == v2)
viaCoord = v2;
else {
log.warn(messagePrefix, "'from' way", fromWay.toBrowseURL(), "doesn't start or end at 'via' node or way");
valid = false;
}
}
if (!valid)
return;
viaPoints.add(viaCoord);
// check if via ways are connected in the given order
for (int i = 0; i < viaWays.size(); i++) {
Way way = viaWays.get(i);
Coord v = viaPoints.get(viaPoints.size() - 1);
if (way.getPoints().get(0) == v)
v2 = way.getPoints().get(way.getPoints().size() - 1);
else if (way.getPoints().get(way.getPoints().size() - 1) == v)
v2 = way.getPoints().get(0);
else {
log.warn(messagePrefix, "'via' way", way.toBrowseURL(), "doesn't start or end at", v.toDegreeString());
valid = false;
}
viaPoints.add(v2);
}
// check if all via points are inside the bounding box
int countInside = 0;
for (Coord via : viaPoints) {
if (bbox.contains(via))
++countInside;
}
if (countInside == 0)
valid = false;
else if (countInside > 0 && countInside < viaPoints.size()) {
log.warn(messagePrefix, "via way crosses tile boundary. Don't know how to save that, ignoring it");
valid = false;
}
if (!valid)
return;
// check if all to ways are connected to via point or last via way
Coord lastVia = viaPoints.get(viaPoints.size() - 1);
for (Way toWay : toWays) {
Coord e1 = toWay.getPoints().get(0);
Coord e2 = toWay.getPoints().get(toWay.getPoints().size() - 1);
if (e1 != lastVia && e2 != lastVia) {
log.warn(messagePrefix, "'to' way", toWay.toBrowseURL(), "doesn't start or end at 'via' node or way");
valid = false;
}
}
if (valid && !viaWays.isEmpty() && restriction.startsWith("only")) {
log.warn(messagePrefix, "check: 'via' way(s) are used in", restriction, "restriction");
}
if (valid) {
// make sure that via way(s) don't appear in the from or to lists
for (Way w : viaWays) {
if (fromWays.contains(w)) {
log.warn(messagePrefix, "'via' way", w.toBrowseURL(), "appears also as 'from' way");
valid = false;
}
if (toWays.contains(w)) {
log.warn(messagePrefix, "'via' way", w.toBrowseURL(), "appears also as 'to' way");
valid = false;
}
}
}
if (valid) {
for (Way w : fromWays) fromWayIds.add(w.getId());
for (Way w : toWays) toWayIds.add(w.getId());
for (Way w : viaWays) {
w.setViaWay(true);
viaWayIds.add(w.getId());
}
for (Coord v : viaPoints) v.setViaNodeOfRestriction(true);
}
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class SeaGenerator method findIntesectionPoints.
/**
* Find the points where the remaining shore line segments intersect with the
* map boundary.
*
* @param shoreline The remaining shore line segments.
* @param seaBounds The map boundary.
* @param seaRelation If we are using a multi-polygon, this is it. Otherwise it will be null.
* @return A map of the 'hits' where the shore line intersects the boundary.
*/
private NavigableMap<EdgeHit, Way> findIntesectionPoints(List<Way> shoreline, Area seaBounds, Relation seaRelation) {
assert !generateSeaUsingMP || seaRelation != null;
NavigableMap<EdgeHit, Way> hitMap = new TreeMap<EdgeHit, Way>();
for (Way w : shoreline) {
List<Coord> points = w.getPoints();
Coord pStart = points.get(0);
Coord pEnd = points.get(points.size() - 1);
EdgeHit hStart = getEdgeHit(seaBounds, pStart);
EdgeHit hEnd = getEdgeHit(seaBounds, pEnd);
if (hStart == null || hEnd == null) {
/*
* This problem occurs usually when the shoreline is cut by osmosis (e.g. country-extracts from geofabrik)
* There are two possibilities to solve this problem:
* 1. Close the way and treat it as an island. This is sometimes the best solution (Germany: Usedom at the
* border to Poland)
* 2. Create a "sea sector" only for this shoreline segment. This may also be the best solution
* (see German border to the Netherlands where the shoreline continues in the Netherlands)
* The first choice may lead to "flooded" areas, the second may lead to "triangles".
*
* Usually, the first choice is appropriate if the segment is "nearly" closed.
*/
double length = 0;
Coord p0 = pStart;
for (Coord p1 : points.subList(1, points.size() - 1)) {
length += p0.distance(p1);
p0 = p1;
}
boolean nearlyClosed = pStart.distance(pEnd) < 0.1 * length;
if (nearlyClosed) {
// close the way
points.add(pStart);
if (!FakeIdGenerator.isFakeId(w.getId())) {
Way w1 = new Way(w.getOriginalId());
w1.setFakeId();
w1.getPoints().addAll(w.getPoints());
// only copy the name tags
for (Entry<String, String> tagEntry : w.getTagEntryIterator()) {
if (tagEntry.getKey().equals("name") || tagEntry.getKey().contains("name"))
w1.addTag(tagEntry.getKey(), tagEntry.getValue());
}
w = w1;
}
w.addTag(landTag[0], landTag[1]);
saver.addWay(w);
if (generateSeaUsingMP) {
seaRelation.addElement("inner", w);
}
} else if (allowSeaSectors) {
Way sea;
if (seaRelation != null) {
sea = new Way(seaRelation.getOriginalId());
sea.setFakeId();
} else
sea = new Way(FakeIdGenerator.makeFakeId());
sea.getPoints().addAll(points);
sea.addPoint(new Coord(pEnd.getLatitude(), pStart.getLongitude()));
sea.addPoint(pStart);
sea.addTag("natural", "sea");
log.info("sea: ", sea);
saver.addWay(sea);
if (generateSeaUsingMP)
seaRelation.addElement("outer", sea);
generateSeaBackground = false;
} else if (extendSeaSectors) {
// create additional points at next border to prevent triangles from point 2
if (null == hStart) {
hStart = getNextEdgeHit(seaBounds, pStart);
w.getPoints().add(0, hStart.getPoint(seaBounds));
}
if (null == hEnd) {
hEnd = getNextEdgeHit(seaBounds, pEnd);
w.getPoints().add(hEnd.getPoint(seaBounds));
}
log.debug("hits (second try): ", hStart, hEnd);
hitMap.put(hStart, w);
hitMap.put(hEnd, null);
} else {
// show the coastline even though we can't produce
// a polygon for the land
w.addTag("natural", "coastline");
if (w.hasIdenticalEndPoints() == false) {
log.error("adding sea shape that is not really closed");
}
saver.addWay(w);
}
} else {
log.debug("hits: ", hStart, hEnd);
hitMap.put(hStart, w);
hitMap.put(hEnd, null);
}
}
return hitMap;
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class SeaGenerator method closeGaps.
private void closeGaps(List<Way> ways, Area bounds) {
// maxCoastlineGap metres apart
if (maxCoastlineGap > 0) {
boolean changed = true;
while (changed) {
changed = false;
for (Way w1 : ways) {
if (w1.hasIdenticalEndPoints())
continue;
List<Coord> points1 = w1.getPoints();
Coord w1e = points1.get(points1.size() - 1);
if (bounds.onBoundary(w1e))
continue;
Way nearest = null;
double smallestGap = Double.MAX_VALUE;
for (Way w2 : ways) {
if (w1 == w2 || w2.hasIdenticalEndPoints())
continue;
List<Coord> points2 = w2.getPoints();
Coord w2s = points2.get(0);
if (bounds.onBoundary(w2s))
continue;
double gap = w1e.distance(w2s);
if (gap < smallestGap) {
nearest = w2;
smallestGap = gap;
}
}
if (nearest != null && smallestGap < maxCoastlineGap) {
Coord w2s = nearest.getPoints().get(0);
log.warn("Bridging " + (int) smallestGap + "m gap in coastline from " + w1e.toOSMURL() + " to " + w2s.toOSMURL());
Way wm;
if (FakeIdGenerator.isFakeId(w1.getId())) {
wm = w1;
} else {
wm = new Way(w1.getOriginalId());
wm.setFakeId();
ways.remove(w1);
ways.add(wm);
wm.getPoints().addAll(points1);
wm.copyTags(w1);
}
wm.getPoints().addAll(nearest.getPoints());
ways.remove(nearest);
// make a line that shows the filled gap
Way w = new Way(FakeIdGenerator.makeFakeId());
w.addTag("natural", "mkgmap:coastline-gap");
w.addPoint(w1e);
w.addPoint(w2s);
saver.addWay(w);
changed = true;
break;
}
}
}
}
}
use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.
the class SeaGenerator method createInnerWays.
private boolean createInnerWays(Area seaBounds, List<Way> islands, NavigableMap<EdgeHit, Way> hitMap) {
NavigableSet<EdgeHit> hits = hitMap.navigableKeySet();
boolean shorelineReachesBoundary = false;
while (!hits.isEmpty()) {
long id = FakeIdGenerator.makeFakeId();
Way w = new Way(id);
saver.addWay(w);
EdgeHit hit = hits.first();
EdgeHit hFirst = hit;
do {
Way segment = hitMap.get(hit);
log.info("current hit:", hit);
EdgeHit hNext;
if (segment != null) {
// add the segment and get the "ending hit"
log.info("adding:", segment);
for (Coord p : segment.getPoints()) w.addPointIfNotEqualToLastPoint(p);
hNext = getEdgeHit(seaBounds, segment.getPoints().get(segment.getPoints().size() - 1));
} else {
w.addPointIfNotEqualToLastPoint(hit.getPoint(seaBounds));
hNext = hits.higher(hit);
if (hNext == null)
hNext = hFirst;
Coord p;
if (hit.compareTo(hNext) < 0) {
log.info("joining: ", hit, hNext);
for (int i = hit.edge; i < hNext.edge; i++) {
EdgeHit corner = new EdgeHit(i, 1.0);
p = corner.getPoint(seaBounds);
log.debug("way: ", corner, p);
w.addPointIfNotEqualToLastPoint(p);
}
} else if (hit.compareTo(hNext) > 0) {
log.info("joining: ", hit, hNext);
for (int i = hit.edge; i < 4; i++) {
EdgeHit corner = new EdgeHit(i, 1.0);
p = corner.getPoint(seaBounds);
log.debug("way: ", corner, p);
w.addPointIfNotEqualToLastPoint(p);
}
for (int i = 0; i < hNext.edge; i++) {
EdgeHit corner = new EdgeHit(i, 1.0);
p = corner.getPoint(seaBounds);
log.debug("way: ", corner, p);
w.addPointIfNotEqualToLastPoint(p);
}
}
w.addPointIfNotEqualToLastPoint(hNext.getPoint(seaBounds));
}
hits.remove(hit);
hit = hNext;
} while (!hits.isEmpty() && !hit.equals(hFirst));
if (!w.hasIdenticalEndPoints())
// close shape
w.addPoint(w.getPoints().get(0));
log.info("adding non-island landmass, hits.size()=" + hits.size());
islands.add(w);
shorelineReachesBoundary = true;
}
return shorelineReachesBoundary;
}
Aggregations