Package org.osm2world.core.world.modules

Source Code of org.osm2world.core.world.modules.SurfaceAreaModule

package org.osm2world.core.world.modules;

import static java.util.Collections.*;
import static org.osm2world.core.map_data.creation.EmptyTerrainBuilder.EMPTY_SURFACE_TAG;
import static org.osm2world.core.map_elevation.creation.EleConstraintEnforcer.ConstraintType.MIN;
import static org.osm2world.core.map_elevation.data.GroundState.*;
import static org.osm2world.core.target.common.material.NamedTexCoordFunction.GLOBAL_X_Z;
import static org.osm2world.core.target.common.material.TexCoordUtil.triangleTexCoordLists;

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

import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
import org.osm2world.core.map_data.creation.EmptyTerrainBuilder;
import org.osm2world.core.map_data.data.MapArea;
import org.osm2world.core.map_data.data.overlaps.MapOverlap;
import org.osm2world.core.map_data.data.overlaps.MapOverlapType;
import org.osm2world.core.map_elevation.creation.EleConstraintEnforcer;
import org.osm2world.core.map_elevation.data.EleConnector;
import org.osm2world.core.map_elevation.data.GroundState;
import org.osm2world.core.math.LineSegmentXZ;
import org.osm2world.core.math.PolygonWithHolesXZ;
import org.osm2world.core.math.PolygonXYZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.TriangleXYZ;
import org.osm2world.core.math.TriangleXZ;
import org.osm2world.core.math.VectorGridXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.math.algorithms.CAGUtil;
import org.osm2world.core.math.algorithms.JTSTriangulationUtil;
import org.osm2world.core.math.algorithms.Poly2TriTriangulationUtil;
import org.osm2world.core.target.RenderableToAllTargets;
import org.osm2world.core.target.Target;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.material.Materials;
import org.osm2world.core.util.exception.TriangulationException;
import org.osm2world.core.world.data.AbstractAreaWorldObject;
import org.osm2world.core.world.data.TerrainBoundaryWorldObject;
import org.osm2world.core.world.data.WorldObject;
import org.osm2world.core.world.data.WorldObjectWithOutline;
import org.osm2world.core.world.modules.common.AbstractModule;

/**
* adds generic areas with surface information to the world.
* Is based on surface information on otherwise unknown/unspecified areas.
*/
public class SurfaceAreaModule extends AbstractModule {
 
  /** assumptions about default surfaces for certain tags */
  private static final Map<Tag, String> defaultSurfaceMap
    = new HashMap<Tag, String>();
 
  static {
    defaultSurfaceMap.put(new Tag("leisure", "pitch"), "ground");
    defaultSurfaceMap.put(new Tag("landuse", "construction"), "ground");
    defaultSurfaceMap.put(new Tag("golf", "bunker"), "sand");
    defaultSurfaceMap.put(new Tag("golf", "green"), "grass");
    defaultSurfaceMap.put(new Tag("natural", "sand"), "sand");
    defaultSurfaceMap.put(new Tag("natural", "beach"), "sand");
    defaultSurfaceMap.put(new Tag("landuse", "meadow"), "grass");
    defaultSurfaceMap.put(new Tag("landuse", "grass"), "grass");
  }
 
  @Override
  protected void applyToArea(MapArea area) {
   
    if (!area.getRepresentations().isEmpty()) return;
   
    TagGroup tags = area.getTags();
   
    if (tags.containsKey("surface")) {
      area.addRepresentation(new SurfaceArea(area, tags.getValue("surface")));
    } else {
   
      for (Tag tagWithDefault : defaultSurfaceMap.keySet()) {
        if (tags.contains(tagWithDefault)) {
          area.addRepresentation(new SurfaceArea(
              area, defaultSurfaceMap.get(tagWithDefault)));
        }
      }
     
    }
   
  }
 
  public static class SurfaceArea extends AbstractAreaWorldObject
    implements RenderableToAllTargets, TerrainBoundaryWorldObject {
   
    private final String surface;
   
    private Collection<TriangleXZ> triangulationXZ;
   
    public SurfaceArea(MapArea area, String surface) {
      super(area);
      this.surface = surface;
    }

    @Override
    public void renderTo(Target<?> target) {
     
      Material material = null;
     
      if (surface.equals(EMPTY_SURFACE_TAG.value)) {
        material = Materials.TERRAIN_DEFAULT;
      } else {
        material = Materials.getSurfaceMaterial(surface);
      }
     
      if (material != null) {
       
        Collection<TriangleXYZ> triangles = getTriangulation();
        target.drawTriangles(material, triangles,
            triangleTexCoordLists(triangles, material, GLOBAL_X_Z));
       
      }
     
    }
   
    /**
     * calculates the true ground footprint of this area by removing
     * area covered by other overlapping features, then triangulates it
     * into counterclockwise triangles.
     */
    @Override
    protected Collection<TriangleXZ> getTriangulationXZ() {
     
      if (triangulationXZ != null) {
        return triangulationXZ;
      }
     
      boolean isEmptyTerrain = surface.equals(EMPTY_SURFACE_TAG.value);
     
      /* collect the outlines of overlapping ground polygons and other polygons,
       * and EleConnectors within the area */
     
      List<SimplePolygonXZ> subtractPolys = new ArrayList<SimplePolygonXZ>();
      List<SimplePolygonXZ> allPolys = new ArrayList<SimplePolygonXZ>();
     
      List<VectorXZ> eleConnectorPoints = new ArrayList<VectorXZ>();
     
      for (MapOverlap<?, ?> overlap : area.getOverlaps()) {
      for (WorldObject otherWO : overlap.getOther(area).getRepresentations()) {

        if (otherWO instanceof TerrainBoundaryWorldObject
            && otherWO.getGroundState() == GroundState.ON) {
         
          if (otherWO instanceof SurfaceArea && !isEmptyTerrain) {
            // empty terrain has lowest priority
            continue;
          }
         
          if (overlap.type == MapOverlapType.CONTAIN
              && overlap.e1 == this.area) {
            // completely within other element, no ground area left
            return emptyList();
          }
         
          TerrainBoundaryWorldObject terrainBoundary =
            (TerrainBoundaryWorldObject)otherWO;
         
          SimplePolygonXZ outlinePolygon = terrainBoundary.getOutlinePolygonXZ();
         
          if (outlinePolygon != null) {
           
            subtractPolys.add(outlinePolygon);
            allPolys.add(outlinePolygon);
           
            for (EleConnector eleConnector : otherWO.getEleConnectors()) {
             
              if (!outlinePolygon.getVertexCollection().contains(eleConnector.pos)) {
                eleConnectorPoints.add(eleConnector.pos);
              }
             
            }
           
          }
         
        } else {
         
          for (EleConnector eleConnector : otherWO.getEleConnectors()) {
           
            if (eleConnector.reference == null) {
              /* workaround to avoid using connectors at intersections,
               * which might fall on area segments
               * //TODO cleaner solution
               */
              continue;
            }
           
            eleConnectorPoints.add(eleConnector.pos);
          }
         
          if (otherWO instanceof WorldObjectWithOutline) {
           
            SimplePolygonXZ outlinePolygon =
                ((WorldObjectWithOutline)otherWO).getOutlinePolygonXZ();
           
            if (outlinePolygon != null) {
             
              allPolys.add(outlinePolygon);
             
            }
           
          }
         
        }
       
      }
      }
     
      /* add a grid of points within the area for smoother surface shapes */
     
      VectorGridXZ pointGrid = new VectorGridXZ(
          area.getAxisAlignedBoundingBoxXZ(),
          EmptyTerrainBuilder.POINT_GRID_DIST);
     
      for (VectorXZ point : pointGrid) {
       
        //don't insert if it is e.g. on top of a tunnel;
        //otherwise there would be no minimum vertical distance
       
        boolean safe = true;
       
        for (SimplePolygonXZ polygon : allPolys) {
          if (polygon.contains(point)) {
            safe = false;
            break;
          }
        }
       
        if (safe) {
          eleConnectorPoints.add(point);
        }
       
      }
     
      /* create "leftover" polygons by subtracting the existing ones */
     
      Collection<PolygonWithHolesXZ> polygons;
     
      if (subtractPolys.isEmpty()) {
       
        /* SUGGEST (performance) handle the common "empty terrain cell"
         * special case more efficiently, also regarding point raster? */
       
        polygons = singleton(area.getPolygon());
       
      } else {
       
        polygons = CAGUtil.subtractPolygons(
            area.getOuterPolygon(), subtractPolys);
       
      }
           
      /* triangulate, using elevation information from all participants */
     
      triangulationXZ = new ArrayList<TriangleXZ>();
     
      for (PolygonWithHolesXZ polygon : polygons) {
       
        List<VectorXZ> points = new ArrayList<VectorXZ>();
       
        for (VectorXZ point : eleConnectorPoints) {
          if (polygon.contains(point)) {
            points.add(point);
          }
        }
       
        try {
         
          triangulationXZ.addAll(Poly2TriTriangulationUtil.triangulate(
              polygon.getOuter(),
              polygon.getHoles(),
              Collections.<LineSegmentXZ>emptyList(),
              points));
         
        } catch (TriangulationException e) {
         
          System.err.println("Poly2Tri exception for " + this + ":");
          e.printStackTrace();
          System.err.println("... falling back to JTS triangulation.");
         
          triangulationXZ.addAll(JTSTriangulationUtil.triangulate(
              polygon.getOuter(),
              polygon.getHoles(),
              Collections.<LineSegmentXZ>emptyList(),
              points));
         
        }
       
      }
     
      return triangulationXZ;
     
    }
   
    @Override
    public void defineEleConstraints(EleConstraintEnforcer enforcer) {
     
      super.defineEleConstraints(enforcer);
     
      /** add vertical distance to connectors above and below */
     
      for (MapOverlap<?, ?> overlap : area.getOverlaps()) {
      for (WorldObject otherWO : overlap.getOther(area).getRepresentations()) {
       
        for (EleConnector eleConnector : otherWO.getEleConnectors()) {
         
          EleConnector ownConnector = getEleConnectors().getConnector(eleConnector.pos);
         
          if (ownConnector == null) continue;
                   
          if (eleConnector.groundState == ABOVE) {
           
            enforcer.requireVerticalDistance(
                MIN, 1,
                eleConnector, ownConnector); //TODO actual clearing
           
          } else if (eleConnector.groundState == BELOW) {
           
            enforcer.requireVerticalDistance(
                MIN, 10,
                ownConnector, eleConnector); //TODO actual clearing
           
          }
       
        }
       
      }
      }
     
    }
   
    @Override
    public PolygonXYZ getOutlinePolygon() {
      if (surface.equals(EMPTY_SURFACE_TAG.value)) {
        // avoid interfering with e.g. tree placement
        return null;
      } else {
        return super.getOutlinePolygon();
      }
    }
   
    @Override
    public SimplePolygonXZ getOutlinePolygonXZ() {
      if (surface.equals(EMPTY_SURFACE_TAG.value)) {
        // avoid interfering with e.g. tree placement
        return null;
      } else {
        return super.getOutlinePolygonXZ();
      }
    }
   
    @Override
    public GroundState getGroundState() {
      if (BridgeModule.isBridge(area.getTags())) {
        return GroundState.ABOVE;
      } else if (TunnelModule.isTunnel(area.getTags())) {
        return GroundState.BELOW;
      } else {
        return GroundState.ON;
      }
    }
   
  }

}
TOP

Related Classes of org.osm2world.core.world.modules.SurfaceAreaModule

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.
m/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');