Search in sources :

Example 41 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class LinkDestinationHook method cutoffWay.

/**
 * Cuts off at least minLength meter of the given way and returns the cut off way tagged
 * identical to the given way.
 * @param w the way to be cut
 * @param maxLength the cut off way is no longer than this value
 * @return the cut off way or <code>null</code> if cutting not possible
 */
private Way cutoffWay(Way w, double cutLength, double maxLength, Coord c1, Coord c2) {
    if (w.getPoints().size() < 2) {
        return null;
    }
    if (w.getPoints().size() >= 3) {
        // try to use existing points - that does not deform the way
        Coord firstPoint = w.getPoints().get(0);
        Coord cutPoint = w.getPoints().get(1);
        // check if the maxLength is not exceeded
        double dist = firstPoint.distance(cutPoint);
        if (dist <= maxLength) {
            // create a new way with the first two points and identical tags
            Way precedingWay = new Way(w.getOriginalId(), w.getPoints().subList(0, 1 + 1));
            precedingWay.setFakeId();
            precedingWay.copyTags(w);
            saver.addWay(precedingWay);
            // remove the points of the new way from the original way
            removePointsFromWay(w, 0, 1);
            registerPointsOfWay(precedingWay);
            // check and update relations so that they use the new way if appropriate
            changeWayIdInRelations(w, precedingWay);
            log.debug("Cut way", w, "at existing point 1. New way:", precedingWay);
            // return the new way
            return precedingWay;
        } else {
            log.debug("Cannot cut way", w, "on existing nodes because the first distance is too big:", dist);
        }
    }
    double startSegmentLength = 0;
    Coord lastC = w.getPoints().get(0);
    for (int i = 1; i < w.getPoints().size(); i++) {
        Coord c = w.getPoints().get(i);
        double segmentLength = lastC.distance(c);
        if (startSegmentLength + segmentLength >= cutLength) {
            double frac = (cutLength - startSegmentLength) / segmentLength;
            // insert a new point at the minimum distance
            Coord cConnection = lastC.makeBetweenPoint(c, frac);
            if (c1 != null && c2 != null && cConnection != null) {
                // test if the way using the new point still uses the same
                // orientation to the main motorway
                double oldAngle = getAngle(c1, c2, c);
                double newAngle = getAngle(c1, c2, cConnection);
                if (Math.signum(oldAngle) != Math.signum(newAngle)) {
                    double bestAngleDiff = 180.0d;
                    Coord bestCoord = cConnection;
                    for (Coord cNeighbour : getDirectNeighbours(cConnection)) {
                        double neighbourAngle = getAngle(c1, c2, cNeighbour);
                        if (Math.signum(oldAngle) == Math.signum(neighbourAngle) && Math.abs(oldAngle - neighbourAngle) < bestAngleDiff) {
                            bestAngleDiff = Math.abs(oldAngle - neighbourAngle);
                            bestCoord = cNeighbour;
                        }
                    }
                    if (log.isDebugEnabled()) {
                        log.debug("Changed orientation:", oldAngle, "to", newAngle);
                        log.debug("on Link", w);
                        log.debug("Corrected coord ", cConnection, "to", bestCoord);
                    }
                    cConnection = bestCoord;
                }
            }
            // create the new way with identical tags
            w.getPoints().add(i, cConnection);
            Way precedingWay = new Way(w.getOriginalId(), new ArrayList<Coord>(w.getPoints().subList(0, i + 1)));
            precedingWay.setFakeId();
            precedingWay.copyTags(w);
            saver.addWay(precedingWay);
            // remove the points of the new way from the old way
            removePointsFromWay(w, 0, i);
            registerPointsOfWay(precedingWay);
            // check and update relations so that they use the new way if appropriate
            changeWayIdInRelations(w, precedingWay);
            // return the split way
            return precedingWay;
        }
        lastC = c;
    }
    // way too short
    return null;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord)

Example 42 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class MultiPolygonRelation method calcAreaSize.

/**
 * Calculates a unitless number that gives a value for the size
 * of the area. The calculation does not correct to any earth
 * coordinate system. It uses the simple rectangular coordinate
 * system of garmin coordinates.
 *
 * @param polygon the points of the area
 * @return the size of the area (unitless)
 */
public static double calcAreaSize(List<Coord> polygon) {
    if (polygon.size() < 4 || polygon.get(0) != polygon.get(polygon.size() - 1)) {
        // line or not closed
        return 0;
    }
    long area = 0;
    Iterator<Coord> polyIter = polygon.iterator();
    Coord c2 = polyIter.next();
    while (polyIter.hasNext()) {
        Coord c1 = c2;
        c2 = polyIter.next();
        area += (long) (c2.getHighPrecLon() + c1.getHighPrecLon()) * (c1.getHighPrecLat() - c2.getHighPrecLat());
    }
    // convert from high prec to value in map units
    double areaSize = (double) area / (2 * (1 << Coord.DELTA_SHIFT) * (1 << Coord.DELTA_SHIFT));
    return Math.abs(areaSize);
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord)

Example 43 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class MultiPolygonRelation method connectUnclosedWays.

protected boolean connectUnclosedWays(List<JoinedWay> allWays) {
    List<JoinedWay> unclosed = new ArrayList<>();
    for (JoinedWay w : allWays) {
        if (w.hasIdenticalEndPoints() == false) {
            unclosed.add(w);
        }
    }
    // try to connect ways lying outside or on the bbox
    if (unclosed.size() >= 2) {
        log.debug("Checking", unclosed.size(), "unclosed ways for connections outside the bbox");
        Map<Coord, JoinedWay> outOfBboxPoints = new IdentityHashMap<>();
        // check all ways for endpoints outside or on the bbox
        for (JoinedWay w : unclosed) {
            Coord c1 = w.getPoints().get(0);
            Coord c2 = w.getPoints().get(w.getPoints().size() - 1);
            if (tileBounds.insideBoundary(c1) == false) {
                log.debug("Point", c1, "of way", w.getId(), "outside bbox");
                outOfBboxPoints.put(c1, w);
            }
            if (tileBounds.insideBoundary(c2) == false) {
                log.debug("Point", c2, "of way", w.getId(), "outside bbox");
                outOfBboxPoints.put(c2, w);
            }
        }
        if (outOfBboxPoints.size() < 2) {
            log.debug(outOfBboxPoints.size(), "point outside the bbox. No connection possible.");
            return false;
        }
        List<ConnectionData> coordPairs = new ArrayList<>();
        ArrayList<Coord> coords = new ArrayList<>(outOfBboxPoints.keySet());
        for (int i = 0; i < coords.size(); i++) {
            for (int j = i + 1; j < coords.size(); j++) {
                ConnectionData cd = new ConnectionData();
                cd.c1 = coords.get(i);
                cd.c2 = coords.get(j);
                cd.w1 = outOfBboxPoints.get(cd.c1);
                cd.w2 = outOfBboxPoints.get(cd.c2);
                if (lineCutsBbox(cd.c1, cd.c2)) {
                    // Check if the way can be closed with one additional point
                    // outside the bounding box.
                    // The additional point is combination of the coords of both endpoints.
                    // It works if the lines from the endpoints to the additional point does
                    // not cut the bounding box.
                    // This can be removed when the splitter guarantees to provide logical complete
                    // multi-polygons.
                    Coord edgePoint1 = new Coord(cd.c1.getLatitude(), cd.c2.getLongitude());
                    Coord edgePoint2 = new Coord(cd.c2.getLatitude(), cd.c1.getLongitude());
                    if (lineCutsBbox(cd.c1, edgePoint1) == false && lineCutsBbox(edgePoint1, cd.c2) == false) {
                        cd.imC = edgePoint1;
                    } else if (lineCutsBbox(cd.c1, edgePoint2) == false && lineCutsBbox(edgePoint2, cd.c2) == false) {
                        cd.imC = edgePoint1;
                    } else {
                        // automatically closing such points would create wrong polygons in most cases
                        continue;
                    }
                    cd.distance = cd.c1.distance(cd.imC) + cd.imC.distance(cd.c2);
                } else {
                    cd.distance = cd.c1.distance(cd.c2);
                }
                coordPairs.add(cd);
            }
        }
        if (coordPairs.isEmpty()) {
            log.debug("All potential connections cross the bbox. No connection possible.");
            return false;
        } else {
            // retrieve the connection with the minimum distance
            ConnectionData minCon = Collections.min(coordPairs, new Comparator<ConnectionData>() {

                public int compare(ConnectionData o1, ConnectionData o2) {
                    return Double.compare(o1.distance, o2.distance);
                }
            });
            if (minCon.w1 == minCon.w2) {
                log.debug("Close a gap in way", minCon.w1);
                if (minCon.imC != null)
                    minCon.w1.getPoints().add(minCon.imC);
                minCon.w1.closeWayArtificially();
            } else {
                log.debug("Connect", minCon.w1, "with", minCon.w2);
                if (minCon.w1.getPoints().get(0) == minCon.c1) {
                    Collections.reverse(minCon.w1.getPoints());
                }
                if (minCon.w2.getPoints().get(0) != minCon.c2) {
                    Collections.reverse(minCon.w2.getPoints());
                }
                minCon.w1.getPoints().addAll(minCon.w2.getPoints());
                minCon.w1.addWay(minCon.w2);
                allWays.remove(minCon.w2);
                return true;
            }
        }
    }
    return false;
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) IdentityHashMap(java.util.IdentityHashMap) ArrayList(java.util.ArrayList)

Example 44 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class MultiPolygonRelation method removeWaysOutsideBbox.

/**
 * Removes all ways that are completely outside the bounding box.
 * This reduces error messages from problems on the tile bounds.
 * @param wayList list of ways
 */
protected void removeWaysOutsideBbox(ArrayList<JoinedWay> wayList) {
    ListIterator<JoinedWay> wayIter = wayList.listIterator();
    while (wayIter.hasNext()) {
        JoinedWay w = wayIter.next();
        boolean remove = true;
        // check all points
        for (Coord c : w.getPoints()) {
            if (tileBounds.contains(c)) {
                // if one point is in the bounding box the way should not be removed
                remove = false;
                break;
            }
        }
        if (remove) {
            // check if the polygon contains the complete bounding box
            if (w.getBounds().contains(tileArea.getBounds())) {
                remove = false;
            }
        }
        if (remove) {
            if (log.isDebugEnabled()) {
                log.debug("Remove way", w.getId(), "because it is completely outside the bounding box.");
            }
            wayIter.remove();
        }
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord)

Example 45 with Coord

use of uk.me.parabola.imgfmt.app.Coord in project mkgmap by openstreetmap.

the class MultiPolygonRelation method closeWays.

/**
 * Try to close all unclosed ways in the given list of ways.
 *
 * @param wayList
 *            a list of ways
 */
protected void closeWays(ArrayList<JoinedWay> wayList, double maxCloseDist) {
    for (JoinedWay way : wayList) {
        if (way.hasIdenticalEndPoints() || way.getPoints().size() < 3) {
            continue;
        }
        Coord p1 = way.getPoints().get(0);
        Coord p2 = way.getPoints().get(way.getPoints().size() - 1);
        if (tileBounds.insideBoundary(p1) == false && tileBounds.insideBoundary(p2) == false) {
            // check if both points are on the same side of the bounding box
            if ((p1.getLatitude() <= tileBounds.getMinLat() && p2.getLatitude() <= tileBounds.getMinLat()) || (p1.getLatitude() >= tileBounds.getMaxLat() && p2.getLatitude() >= tileBounds.getMaxLat()) || (p1.getLongitude() <= tileBounds.getMinLong() && p2.getLongitude() <= tileBounds.getMinLong()) || (p1.getLongitude() >= tileBounds.getMaxLong() && p2.getLongitude() >= tileBounds.getMaxLong())) {
                // they are on the same side outside of the bbox
                // so just close them without worrying about if
                // they intersect itself because the intersection also
                // is outside the bbox
                way.closeWayArtificially();
                log.info("Endpoints of way", way, "are both outside the bbox. Closing it directly.");
                continue;
            }
        }
        Line2D closingLine = new Line2D.Float(p1.getLongitude(), p1.getLatitude(), p2.getLongitude(), p2.getLatitude());
        boolean intersects = false;
        Coord lastPoint = null;
        // Both isn't interesting for this check
        for (Coord thisPoint : way.getPoints().subList(1, way.getPoints().size() - 1)) {
            if (lastPoint != null) {
                if (closingLine.intersectsLine(lastPoint.getLongitude(), lastPoint.getLatitude(), thisPoint.getLongitude(), thisPoint.getLatitude())) {
                    intersects = true;
                    break;
                }
            }
            lastPoint = thisPoint;
        }
        if (!intersects) {
            // close the polygon
            // the new way segment does not intersect the rest of the polygon
            boolean doClose = true;
            if (maxCloseDist > 0) {
                // calc the distance to close
                double closeDist = way.getPoints().get(0).distance(way.getPoints().get(way.getPoints().size() - 1));
                doClose = closeDist < maxCloseDist;
            }
            if (doClose) {
                log.info("Closing way", way);
                log.info("from", way.getPoints().get(0).toOSMURL());
                log.info("to", way.getPoints().get(way.getPoints().size() - 1).toOSMURL());
                // mark this ways as artificially closed
                way.closeWayArtificially();
            }
        }
    }
}
Also used : Coord(uk.me.parabola.imgfmt.app.Coord) Line2D(java.awt.geom.Line2D)

Aggregations

Coord (uk.me.parabola.imgfmt.app.Coord)178 ArrayList (java.util.ArrayList)71 Way (uk.me.parabola.mkgmap.reader.osm.Way)31 MapPoint (uk.me.parabola.mkgmap.general.MapPoint)27 List (java.util.List)23 MapLine (uk.me.parabola.mkgmap.general.MapLine)16 Area (uk.me.parabola.imgfmt.app.Area)15 MapShape (uk.me.parabola.mkgmap.general.MapShape)15 CoordNode (uk.me.parabola.imgfmt.app.CoordNode)13 MapExitPoint (uk.me.parabola.mkgmap.general.MapExitPoint)13 Node (uk.me.parabola.mkgmap.reader.osm.Node)13 HashMap (java.util.HashMap)12 IdentityHashMap (java.util.IdentityHashMap)11 Test (org.junit.Test)11 MapRoad (uk.me.parabola.mkgmap.general.MapRoad)9 Long2ObjectOpenHashMap (it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap)8 Area (java.awt.geom.Area)8 HashSet (java.util.HashSet)8 IntArrayList (it.unimi.dsi.fastutil.ints.IntArrayList)5 LinkedHashMap (java.util.LinkedHashMap)5