Package org.opentripplanner.graph_builder.impl.osm

Source Code of org.opentripplanner.graph_builder.impl.osm.Area$AreaConstructionException

/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.opentripplanner.graph_builder.impl.osm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.opentripplanner.common.geometry.GeometryUtils;
import org.opentripplanner.openstreetmap.model.OSMNode;
import org.opentripplanner.openstreetmap.model.OSMWay;
import org.opentripplanner.openstreetmap.model.OSMWithTags;

import com.google.common.collect.ArrayListMultimap;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

/**
* Stores information about an OSM area needed for visibility graph construction. Algorithm based on
* http://wiki.openstreetmap.org/wiki/Relation:multipolygon/Algorithm but generally done in a
* quick/dirty way.
*/
class Area {

    public class AreaConstructionException extends RuntimeException {
        private static final long serialVersionUID = 1L;
    }

    // This is the way or relation that has the relevant tags for the area
    OSMWithTags parent;

    List<Ring> outermostRings = new ArrayList<Ring>();

    private MultiPolygon jtsMultiPolygon;

    Area(OSMWithTags parent, List<OSMWay> outerRingWays, List<OSMWay> innerRingWays,
            Map<Long, OSMNode> _nodes) {
        this.parent = parent;
        // ring assignment
        List<List<Long>> innerRingNodes = constructRings(innerRingWays);
        List<List<Long>> outerRingNodes = constructRings(outerRingWays);
        if (innerRingNodes == null || outerRingNodes == null) {
            throw new AreaConstructionException();
        }
        ArrayList<List<Long>> allRings = new ArrayList<List<Long>>(innerRingNodes);
        allRings.addAll(outerRingNodes);

        List<Ring> innerRings = new ArrayList<Ring>();
        List<Ring> outerRings = new ArrayList<Ring>();
        for (List<Long> ring : innerRingNodes) {
            innerRings.add(new Ring(ring, _nodes));
        }
        for (List<Long> ring : outerRingNodes) {
            outerRings.add(new Ring(ring, _nodes));
        }

        // now, ring grouping
        // first, find outermost rings
        OUTER: for (Ring outer : outerRings) {
            for (Ring possibleContainer : outerRings) {
                if (outer != possibleContainer
                        && outer.geometry.hasPointInside(possibleContainer.geometry)) {
                    continue OUTER;
                }
            }
            outermostRings.add(outer);

            // find holes in this ring
            for (Ring possibleHole : innerRings) {
                if (possibleHole.geometry.hasPointInside(outer.geometry)) {
                    outer.holes.add(possibleHole);
                }
            }
        }
        // run this at end of ctor so that exception
        // can be caught in the right place
        toJTSMultiPolygon();
    }

    public MultiPolygon toJTSMultiPolygon() {
        if (jtsMultiPolygon == null) {
            List<Polygon> polygons = new ArrayList<Polygon>();
            for (Ring ring : outermostRings) {
                polygons.add(ring.toJtsPolygon());
            }
            jtsMultiPolygon = GeometryUtils.getGeometryFactory().createMultiPolygon(
                    polygons.toArray(new Polygon[0]));
            if (!jtsMultiPolygon.isValid()) {
                throw new AreaConstructionException();
            }
        }

        return jtsMultiPolygon;
    }

    public List<List<Long>> constructRings(List<OSMWay> ways) {
        if (ways.size() == 0) {
            // no rings is no rings
            return Collections.emptyList();
        }

        List<List<Long>> closedRings = new ArrayList<List<Long>>();

        ArrayListMultimap<Long, OSMWay> waysByEndpoint = ArrayListMultimap.create();
        for (OSMWay way : ways) {
            List<Long> refs = way.getNodeRefs();

            long start = refs.get(0);
            long end = refs.get(refs.size() - 1);
            if (start == end) {
                ArrayList<Long> ring = new ArrayList<Long>(refs);
                closedRings.add(ring);
            } else {
                waysByEndpoint.put(start, way);
                waysByEndpoint.put(end, way);
            }
        }

        // precheck for impossible situations
        List<Long> toRemove = new ArrayList<Long>();
        for (Long endpoint : waysByEndpoint.keySet()) {
            Collection<OSMWay> list = waysByEndpoint.get(endpoint);
            if (list.size() % 2 == 1) {
                return null;
            }
        }
        for (Long key : toRemove) {
            waysByEndpoint.removeAll(key);
        }

        List<Long> partialRing = new ArrayList<Long>();
        if (waysByEndpoint.size() == 0) {
            return closedRings;
        }

        long firstEndpoint = 0, otherEndpoint = 0;
        OSMWay firstWay = null;
        for (Long endpoint : waysByEndpoint.keySet()) {
            List<OSMWay> list = waysByEndpoint.get(endpoint);
            firstWay = list.get(0);
            List<Long> nodeRefs = firstWay.getNodeRefs();
            partialRing.addAll(nodeRefs);
            firstEndpoint = nodeRefs.get(0);
            otherEndpoint = nodeRefs.get(nodeRefs.size() - 1);
            break;
        }
        waysByEndpoint.get(firstEndpoint).remove(firstWay);
        waysByEndpoint.get(otherEndpoint).remove(firstWay);
        if (constructRingsRecursive(waysByEndpoint, partialRing, closedRings, firstEndpoint)) {
            return closedRings;
        } else {
            return null;
        }
    }

    private boolean constructRingsRecursive(ArrayListMultimap<Long, OSMWay> waysByEndpoint,
            List<Long> ring, List<List<Long>> closedRings, long endpoint) {

        List<OSMWay> ways = new ArrayList<OSMWay>(waysByEndpoint.get(endpoint));

        for (OSMWay way : ways) {
            // remove this way from the map
            List<Long> nodeRefs = way.getNodeRefs();
            long firstEndpoint = nodeRefs.get(0);
            long otherEndpoint = nodeRefs.get(nodeRefs.size() - 1);

            waysByEndpoint.remove(firstEndpoint, way);
            waysByEndpoint.remove(otherEndpoint, way);

            ArrayList<Long> newRing = new ArrayList<Long>(ring.size() + nodeRefs.size());
            long newFirstEndpoint;
            if (firstEndpoint == endpoint) {
                for (int j = nodeRefs.size() - 1; j >= 1; --j) {
                    newRing.add(nodeRefs.get(j));
                }
                newRing.addAll(ring);
                newFirstEndpoint = otherEndpoint;
            } else {
                newRing.addAll(nodeRefs.subList(0, nodeRefs.size() - 1));
                newRing.addAll(ring);
                newFirstEndpoint = firstEndpoint;
            }
            if (newRing.get(newRing.size() - 1).equals(newRing.get(0))) {
                // ring closure
                closedRings.add(newRing);
                // if we're out of endpoints, then we have succeeded
                if (waysByEndpoint.size() == 0) {
                    return true; // success
                }

                // otherwise, we need to start a new partial ring
                newRing = new ArrayList<Long>();
                OSMWay firstWay = null;
                for (Long entry : waysByEndpoint.keySet()) {
                    List<OSMWay> list = waysByEndpoint.get(entry);
                    firstWay = list.get(0);
                    nodeRefs = firstWay.getNodeRefs();
                    newRing.addAll(nodeRefs);
                    firstEndpoint = nodeRefs.get(0);
                    otherEndpoint = nodeRefs.get(nodeRefs.size() - 1);
                    break;
                }

                waysByEndpoint.remove(firstEndpoint, firstWay);
                waysByEndpoint.remove(otherEndpoint, firstWay);

                if (constructRingsRecursive(waysByEndpoint, newRing, closedRings, firstEndpoint)) {
                    return true;
                }

                waysByEndpoint.remove(firstEndpoint, firstWay);
                waysByEndpoint.remove(otherEndpoint, firstWay);

            } else {
                // continue with this ring
                if (waysByEndpoint.get(newFirstEndpoint) != null) {
                    if (constructRingsRecursive(waysByEndpoint, newRing, closedRings,
                            newFirstEndpoint)) {
                        return true;
                    }
                }
            }
            if (firstEndpoint == endpoint) {
                waysByEndpoint.put(otherEndpoint, way);
            } else {
                waysByEndpoint.put(firstEndpoint, way);
            }
        }
        return false;
    }
}
TOP

Related Classes of org.opentripplanner.graph_builder.impl.osm.Area$AreaConstructionException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.