Package org.osm2world.core.map_data.creation

Source Code of org.osm2world.core.map_data.creation.OSMToMapDataConverter

package org.osm2world.core.map_data.creation;

import static java.util.Collections.emptyList;
import static org.osm2world.core.math.VectorXZ.distance;
import static org.osm2world.core.util.FaultTolerantIterationUtil.iterate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
import org.openstreetmap.josm.plugins.graphview.core.data.osmosis.OSMFileDataSource;
import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
import org.osm2world.core.map_data.creation.index.MapDataIndex;
import org.osm2world.core.map_data.creation.index.MapIntersectionGrid;
import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.MapAreaSegment;
import org.osm2world.core.map_data.data.MapData;
import org.osm2world.core.map_data.data.MapElement;
import org.osm2world.core.map_data.data.MapNode;
import org.osm2world.core.map_data.data.MapWaySegment;
import org.osm2world.core.map_data.data.overlaps.MapIntersectionWW;
import org.osm2world.core.map_data.data.overlaps.MapOverlapAA;
import org.osm2world.core.map_data.data.overlaps.MapOverlapNA;
import org.osm2world.core.map_data.data.overlaps.MapOverlapType;
import org.osm2world.core.map_data.data.overlaps.MapOverlapWA;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.GeometryUtil;
import org.osm2world.core.math.InvalidGeometryException;
import org.osm2world.core.math.LineSegmentXZ;
import org.osm2world.core.math.PolygonWithHolesXZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMData;
import org.osm2world.core.osm.data.OSMElement;
import org.osm2world.core.osm.data.OSMNode;
import org.osm2world.core.osm.data.OSMRelation;
import org.osm2world.core.osm.data.OSMWay;
import org.osm2world.core.osm.ruleset.HardcodedRuleset;
import org.osm2world.core.osm.ruleset.Ruleset;
import org.osm2world.core.util.FaultTolerantIterationUtil.Operation;

/**
* converts {@link OSMData} into the internal map data representation
*/
public class OSMToMapDataConverter {

  private final Ruleset ruleset = new HardcodedRuleset();
 
  private final MapProjection mapProjection;
 
  private static final Tag MULTIPOLYON_TAG = new Tag("type", "multipolygon");
 
   
  public OSMToMapDataConverter(MapProjection mapProjection) {
    this.mapProjection = mapProjection;
  }

  public MapData createMapData(OSMData osmData) throws IOException {
   
    final List<MapNode> mapNodes = new ArrayList<MapNode>();
    final List<MapWaySegment> mapWaySegs = new ArrayList<MapWaySegment>();
    final List<MapArea> mapAreas = new ArrayList<MapArea>();
   
    createMapElements(osmData, mapNodes, mapWaySegs, mapAreas);
   
    MapData mapData = new MapData(mapNodes, mapWaySegs, mapAreas,
        calculateFileBoundary(osmData.getBounds()));
   
    calculateIntersectionsInMapData(mapData);

    return mapData;

  }

  /**
   * creates {@link MapElement}s
   * based on OSM data from an {@link OSMFileDataSource}
   * and adds them to collections
   */
  private void createMapElements(OSMData osmData,
      final List<MapNode> mapNodes, final List<MapWaySegment> mapWaySegs,
      final List<MapArea> mapAreas) {
   
    /* create MapNode for each OSM node */

    final Map<OSMNode, MapNode> nodeMap = new HashMap<OSMNode, MapNode>();

    for (OSMNode node : osmData.getNodes()) {
      VectorXZ nodePos = mapProjection.calcPos(node.lat, node.lon);
      MapNode mapNode = new MapNode(nodePos, node);
      mapNodes.add(mapNode);
      nodeMap.put(node, mapNode);
    }
   
    /* create areas ... */
   
    final Map<OSMWay, MapArea> areaMap = new HashMap<OSMWay, MapArea>();
       
    /* ... based on multipolygons */
   
    iterate(osmData.getRelations(), new Operation<OSMRelation>() {
      @Override public void perform(OSMRelation relation) {
       
        if (relation.tags.contains(MULTIPOLYON_TAG)) {
         
          for (MapArea area : MultipolygonAreaBuilder.
              createAreasForMultipolygon(relation, nodeMap)) {
           
            mapAreas.add(area);
           
            for (MapNode boundaryMapNode : area.getBoundaryNodes()) {
              boundaryMapNode.addAdjacentArea(area);
            }
           
            if (area.getOsmObject() instanceof OSMWay) {
              areaMap.put((OSMWay)area.getOsmObject(), area);
            }
           
          }
         
        }
       
      }
    });
   
    /* ... based on coastline ways */
   
    for (MapArea area : MultipolygonAreaBuilder.createAreasForCoastlines(
        osmData, nodeMap, mapNodes,
        calculateFileBoundary(osmData.getBounds()))) {
     
      mapAreas.add(area);
     
      for (MapNode boundaryMapNode : area.getBoundaryNodes()) {
        boundaryMapNode.addAdjacentArea(area);
      }
     
    }
   
    /* ... based on closed ways */
   
    for (OSMWay way : osmData.getWays()) {
      if (way.isClosed() && !areaMap.containsKey(way)) {
        //create MapArea only if at least one tag is an area tag
        for (Tag tag : way.tags) {
          if (ruleset.isAreaTag(tag)) {
            //TODO: check whether this is old-style MP outer
           
            List<MapNode> nodes = new ArrayList<MapNode>(way.nodes.size());
            for (OSMNode boundaryOSMNode : way.nodes) {
              nodes.add(nodeMap.get(boundaryOSMNode));
            }
           
            try {
             
              MapArea mapArea = new MapArea((OSMElement)way, nodes);
             
              mapAreas.add(mapArea);
              areaMap.put(way, mapArea);
             
            } catch (InvalidGeometryException e) {
              System.err.println(e);
            }
           
            break;
          }
        }
      }
    }
   
    /* ... for empty terrain */
   
    AxisAlignedBoundingBoxXZ terrainBoundary =
        calculateFileBoundary(osmData.getBounds());
   
    if (terrainBoundary != null) {
     
      EmptyTerrainBuilder.createAreasForEmptyTerrain(
          mapNodes, mapAreas, terrainBoundary);
     
    } else {
     
      //TODO fall back on data boundary if file does not contain bounds
     
    }
       
    /* finish calculations */
   
    for (MapNode node : nodeMap.values()) {
      node.calculateAdjacentAreaSegments();
    }
   
    /* create way segments from remaining ways */
   
    for (OSMWay way : osmData.getWays()) {
      if (!way.tags.isEmpty() && !areaMap.containsKey(way)) {
       
        OSMNode previousNode = null;
        for (OSMNode node : way.nodes) {
          if (previousNode != null) {
           
            MapWaySegment mapWaySeg = new MapWaySegment(
                way, nodeMap.get(previousNode), nodeMap.get(node));
           
            mapWaySegs.add(mapWaySeg);
            nodeMap.get(previousNode).addOutboundLine(mapWaySeg);
            nodeMap.get(node).addInboundLine(mapWaySeg);
           
          }
          previousNode = node;
        }
       
      }
    }
   
  }
 
  /**
   * calculates intersections and adds the information to the
   * {@link MapElement}s
   */
  private static void calculateIntersectionsInMapData(MapData mapData) {
   
    MapDataIndex index = new MapIntersectionGrid(mapData.getDataBoundary());
   
    for (MapElement e1 : mapData.getMapElements()) {
     
      /* collect all nearby elements */
     
      Collection<? extends Iterable<MapElement>> leaves
          = index.insertAndProbe(e1);
     
      Iterable<MapElement> nearbyElements;
     
      if (leaves.size() == 1) {
        nearbyElements = leaves.iterator().next();
      } else {
        // collect and de-duplicate elements from all the leaves
        Set<MapElement> elementSet = new HashSet<MapElement>();
        for (Iterable<MapElement> leaf : leaves) {
          for (MapElement e : leaf) {
            elementSet.add(e);
          }
        }
        nearbyElements = elementSet;
      }
     
      for (MapElement e2 : nearbyElements) {
     
        if (e1 == e2) { continue; }
       
        addOverlapBetween(e1, e2);
       
      }
     
    }
   
  }

  /**
   * adds the overlap between two {@link MapElement}s
   * to both, if it exists. It calls the appropriate
   * subtype-specific addOverlapBetween method
   */
  private static void addOverlapBetween(MapElement e1, MapElement e2) {
   
    if (e1 instanceof MapWaySegment
        && e2 instanceof MapWaySegment) {
     
      addOverlapBetween((MapWaySegment) e1, (MapWaySegment) e2);
     
    } else if (e1 instanceof MapWaySegment
        && e2 instanceof MapArea) {
     
      addOverlapBetween((MapWaySegment) e1, (MapArea) e2);
     
    } else if (e1 instanceof MapArea
        && e2 instanceof MapWaySegment) {
     
      addOverlapBetween((MapWaySegment) e2, (MapArea) e1);
     
    } else if (e1 instanceof MapArea
        && e2 instanceof MapArea) {
     
      addOverlapBetween((MapArea) e1, (MapArea) e2);
     
    } else if (e1 instanceof MapNode
        && e2 instanceof MapArea) {
     
      addOverlapBetween((MapNode) e1, (MapArea) e2);
     
    } else if (e1 instanceof MapArea
        && e2 instanceof MapNode) {
     
      addOverlapBetween((MapNode) e2, (MapArea) e1);
     
    }
   
  }

  /**
   * adds the overlap between two {@link MapWaySegment}s
   * to both, if it exists
   */
  private static void addOverlapBetween(
      MapWaySegment line1, MapWaySegment line2) {
   
    if (line1.isConnectedTo(line2)) { return; }
   
    VectorXZ intersection = GeometryUtil.getLineSegmentIntersection(
        line1.getStartNode().getPos(),
        line1.getEndNode().getPos(),
        line2.getStartNode().getPos(),
        line2.getEndNode().getPos());
   
    if (intersection != null) {
     
      /* add the intersection */
     
      MapIntersectionWW newIntersection =
        new MapIntersectionWW(line1, line2, intersection);
     
      line1.addOverlap(newIntersection);
      line2.addOverlap(newIntersection);
                     
    }

  }
 
  /**
   * adds the overlap between a {@link MapWaySegment}
   * and a {@link MapArea} to both, if it exists
   */
  private static void addOverlapBetween(
      MapWaySegment line, MapArea area) {
   
    final LineSegmentXZ segmentXZ = line.getLineSegment();
   
    /* check whether the line corresponds to one of the area segments */
       
    for (MapAreaSegment areaSegment : area.getAreaSegments()) {
      if (areaSegment.sharesBothNodes(line)) {
       
        MapOverlapWA newOverlap =
          new MapOverlapWA(line, area, MapOverlapType.SHARE_SEGMENT,
              Collections.<VectorXZ>emptyList(),
              Collections.<MapAreaSegment>emptyList());
       
        line.addOverlap(newOverlap);
        area.addOverlap(newOverlap);
       
        return;
       
      }
    }
   
    /* calculate whether the line contains or intersects the area (or neither) */
   
    boolean contains;
    boolean intersects;
   
    {
      final PolygonWithHolesXZ polygon = area.getPolygon();
     
      if (!line.isConnectedTo(area)) {
 
        intersects = polygon.intersects(segmentXZ);
        contains = !intersects && polygon.contains(segmentXZ);
       
      } else {
     
        /* check whether the line intersects the area somewhere
         * else than just at the common node(s).
         */
       
        intersects = false;
     
        double segmentLength = distance(segmentXZ.p1, segmentXZ.p2);
       
        for (VectorXZ pos : polygon.intersectionPositions(segmentXZ)) {
          if (distance(pos, segmentXZ.p1) > segmentLength / 100
              && distance(pos, segmentXZ.p2) > segmentLength / 100) {
            intersects = true;
            break;
          }
        }
 
        /* check whether the area contains the line's center.
         * Unless the line intersects the area outline,
         * this means that the area contains the line itself.
         */
             
        contains = !intersects && polygon.contains(segmentXZ.getCenter());
       
      }
     
    }
   
    /* add an overlap if detected */
         
    if (contains || intersects) {
     
      /* find out which area segments intersect the way segment */

      List<VectorXZ> intersectionPositions = emptyList();
      List<MapAreaSegment> intersectingSegments = emptyList();
     
      if (intersects) {

        intersectionPositions = new ArrayList<VectorXZ>();
        intersectingSegments = new ArrayList<MapAreaSegment>();
       
        for (MapAreaSegment areaSegment : area.getAreaSegments()) {
         
          VectorXZ intersection = segmentXZ.getIntersection(
              areaSegment.getStartNode().getPos(),
              areaSegment.getEndNode().getPos());
         
          if (intersection != null) {
            intersectionPositions.add(intersection);
            intersectingSegments.add(areaSegment);
          }
         
        }
       
      }
     
      /* add the overlap */
     
      MapOverlapWA newOverlap = new MapOverlapWA(line, area,
            intersects ? MapOverlapType.INTERSECT : MapOverlapType.CONTAIN,
            intersectionPositions, intersectingSegments);
     
      line.addOverlap(newOverlap);
      area.addOverlap(newOverlap);
     
    }
   
  }

  /**
   * adds the overlap between two {@link MapArea}s
   * to both, if it exists
   */
  private static void addOverlapBetween(
      MapArea area1, MapArea area2) {
   
    /* check whether the areas have a shared segment */
       
    Collection<MapAreaSegment> area1Segments = area1.getAreaSegments();
    Collection<MapAreaSegment> area2Segments = area2.getAreaSegments();
   
    for (MapAreaSegment area1Segment : area1Segments) {
      for (MapAreaSegment area2Segment : area2Segments) {
        if (area1Segment.sharesBothNodes(area2Segment)) {
         
          MapOverlapAA newOverlap =
            new MapOverlapAA(area1, area2, MapOverlapType.SHARE_SEGMENT);
          area1.addOverlap(newOverlap);
          area2.addOverlap(newOverlap);
         
          return;
         
        }
      }
    }
   
    /* calculate whether one area contains the other
     * or whether their outlines intersect (or neither) */
   
    boolean contains1 = false;
    boolean contains2 = false;
    boolean intersects = false;
   
    {
      final PolygonWithHolesXZ polygon1 = area1.getPolygon();
      final PolygonWithHolesXZ polygon2 = area2.getPolygon();
     
      /* determine common nodes */
     
      Set<VectorXZ> commonNodes = new HashSet<VectorXZ>();
      for (SimplePolygonXZ p : polygon1.getPolygons()) {
        commonNodes.addAll(p.getVertices());
      }
     
      Set<VectorXZ> nodes2 = new HashSet<VectorXZ>();
      for (SimplePolygonXZ p : polygon2.getPolygons()) {
        nodes2.addAll(p.getVertices());
      }
     
      commonNodes.retainAll(nodes2);
     
      /* check whether the areas' outlines intersects somewhere
       * else than just at the common node(s).
       */
     
      intersectionPosCheck:
      for (VectorXZ pos : polygon1.intersectionPositions(polygon2)) {
        boolean trueIntersection = true;
        for (VectorXZ commonNode : commonNodes) {
          if (distance(pos, commonNode) < 0.01) {
            trueIntersection = false;
          }
        }
        if (trueIntersection) {
          intersects = true;
          break intersectionPosCheck;
        }
      }

      /* check whether one area contains the other */
     
      if (polygon1.contains(polygon2.getOuter())) {
        contains1 = true;
      } else if (polygon2.contains(polygon1.getOuter())) {
        contains2 = true;
      }
                 
    }
   
    /* add an overlap if detected */
         
    if (contains1 || contains2 || intersects) {
     
      /* add the overlap */
     
      MapOverlapAA newOverlap = null;
     
      if (contains1) {
        newOverlap = new MapOverlapAA(area2, area1, MapOverlapType.CONTAIN);
      } else if (contains2) {
        newOverlap = new MapOverlapAA(area1, area2, MapOverlapType.CONTAIN);
      } else {
        newOverlap = new MapOverlapAA(area1, area2, MapOverlapType.INTERSECT);
      }
     
      area1.addOverlap(newOverlap);
      area2.addOverlap(newOverlap);
     
    }
   
  }
 
  private static void addOverlapBetween(MapNode node, MapArea area) {
   
    if (area.getPolygon().contains(node.getPos())) {
     
      /* add the overlap */
     
      MapOverlapNA newOverlap =
          new MapOverlapNA(node, area, MapOverlapType.CONTAIN);
     
      area.addOverlap(newOverlap);
     
    }
   
  }
 
  private AxisAlignedBoundingBoxXZ calculateFileBoundary(
      Collection<Bound> bounds) {
   
    Collection<VectorXZ> boundedPoints = new ArrayList<VectorXZ>();
   
    for (Bound bound : bounds) {
     
      boundedPoints.add(mapProjection.calcPos(bound.getBottom(), bound.getLeft()));
      boundedPoints.add(mapProjection.calcPos(bound.getBottom(), bound.getRight()));
      boundedPoints.add(mapProjection.calcPos(bound.getTop(), bound.getLeft()));
      boundedPoints.add(mapProjection.calcPos(bound.getTop(), bound.getRight()));
     
    }
   
    if (boundedPoints.isEmpty()) {
      return null;
    } else {
      return new AxisAlignedBoundingBoxXZ(boundedPoints);
    }
   
  }

}
TOP

Related Classes of org.osm2world.core.map_data.creation.OSMToMapDataConverter

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.