private void treatAndAddUshapeWithinTimeLimits(
      long maxTime,
      double userSpeed,
      ArrayList<LineString> walkShedEdges,
      Edge edge,
      long fromTime,
      long toTime,
      LineString ls,
      boolean hasCar) {

    // check if the u-shape can be traveled within the remaining time
    long dt = Math.abs(toTime - fromTime);
    double distanceToMoveInTimeMissing =
        distanceToMoveInRemainingTime(maxTime, fromTime, dt, userSpeed, edge, hasCar, true);
    double lineDist = edge.getDistance();
    double fraction = (double) distanceToMoveInTimeMissing / (double) lineDist;
    // get the sub-edge geom
    LineString subLine = null;
    if (fraction < 1.0) {
      // the u-shape is not fully walkable in maxTime
      subLine = this.getSubLineString(ls, fraction);
      walkShedEdges.add(subLine);
      // if it is smaller we need also to calculate the LS from the other side
      LineString reversedLine = (LineString) ls.reverse();
      double distanceToMoveInTimeMissing2 =
          distanceToMoveInRemainingTime(maxTime, toTime, dt, userSpeed, edge, hasCar, true);
      double fraction2 = (double) distanceToMoveInTimeMissing2 / (double) lineDist;
      LineString secondsubLine = this.getSubLineString(reversedLine, fraction2);
      ;
      walkShedEdges.add(secondsubLine);
    } else { // the whole u-shape is within the time
      // add only once
      walkShedEdges.add(ls);
    }
  }
 private void setGeometry(LineString geometry) {
   this.compactGeometry =
       CompactLineString.compactLineString(
           fromv.getLon(),
           fromv.getLat(),
           tov.getLon(),
           tov.getLat(),
           isBack() ? (LineString) geometry.reverse() : geometry,
           isBack());
 }
    /**
     * Handle oneway streets, cycleways, and whatnot. See http://wiki.openstreetmap.org/wiki/Bicycle
     * for various scenarios, along with
     * http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing#Oneway.
     *
     * @param end
     * @param start
     */
    private P2<PlainStreetEdge> getEdgesForStreet(
        IntersectionVertex start,
        IntersectionVertex end,
        OSMWay way,
        long startNode,
        StreetTraversalPermission permissions,
        LineString geometry) {
      // get geometry length in meters, irritatingly.
      Coordinate[] coordinates = geometry.getCoordinates();
      double d = 0;
      for (int i = 1; i < coordinates.length; ++i) {
        d += DistanceLibrary.distance(coordinates[i - 1], coordinates[i]);
      }

      LineString backGeometry = (LineString) geometry.reverse();

      Map<String, String> tags = way.getTags();

      if (permissions == StreetTraversalPermission.NONE) return new P2<PlainStreetEdge>(null, null);

      PlainStreetEdge street = null, backStreet = null;

      /*
       * pedestrian rules: everything is two-way (assuming pedestrians are allowed at all)
       * bicycle rules: default: permissions;
       *
       * cycleway=dismount means walk your bike -- the engine will automatically try walking
       * bikes any time it is forbidden to ride them, so the only thing to do here is to
       * remove bike permissions
       *
       * oneway=... sets permissions for cars and bikes oneway:bicycle overwrites these
       * permissions for bikes only
       *
       * now, cycleway=opposite_lane, opposite, opposite_track can allow once oneway has been
       * set by oneway:bicycle, but should give a warning if it conflicts with oneway:bicycle
       *
       * bicycle:backward=yes works like oneway:bicycle=no bicycle:backwards=no works like
       * oneway:bicycle=yes
       */

      String foot = way.getTag("foot");
      if ("yes".equals(foot) || "designated".equals(foot)) {
        permissions = permissions.add(StreetTraversalPermission.PEDESTRIAN);
      }

      if (OSMWithTags.isFalse(foot)) {
        permissions = permissions.remove(StreetTraversalPermission.PEDESTRIAN);
      }

      boolean forceBikes = false;
      String bicycle = way.getTag("bicycle");
      if ("yes".equals(bicycle) || "designated".equals(bicycle)) {
        permissions = permissions.add(StreetTraversalPermission.BICYCLE);
        forceBikes = true;
      }

      if (way.isTag("cycleway", "dismount") || "dismount".equals(bicycle)) {
        permissions = permissions.remove(StreetTraversalPermission.BICYCLE);
        if (forceBikes) {
          _log.warn(
              GraphBuilderAnnotation.register(graph, Variety.CONFLICTING_BIKE_TAGS, way.getId()));
        }
      }

      StreetTraversalPermission permissionsFront = permissions;
      StreetTraversalPermission permissionsBack = permissions;

      if (way.isTagTrue("oneway") || "roundabout".equals(tags.get("junction"))) {
        permissionsBack = permissionsBack.remove(StreetTraversalPermission.BICYCLE_AND_CAR);
      }
      if (way.isTag("oneway", "-1")) {
        permissionsFront = permissionsFront.remove(StreetTraversalPermission.BICYCLE_AND_CAR);
      }
      String oneWayBicycle = way.getTag("oneway:bicycle");
      if (OSMWithTags.isTrue(oneWayBicycle) || way.isTagFalse("bicycle:backwards")) {
        permissionsBack = permissionsBack.remove(StreetTraversalPermission.BICYCLE);
      }
      if ("-1".equals(oneWayBicycle)) {
        permissionsFront = permissionsFront.remove(StreetTraversalPermission.BICYCLE);
      }
      if (OSMWithTags.isFalse(oneWayBicycle) || way.isTagTrue("bicycle:backwards")) {
        if (permissions.allows(StreetTraversalPermission.BICYCLE)) {
          permissionsFront = permissionsFront.add(StreetTraversalPermission.BICYCLE);
          permissionsBack = permissionsBack.add(StreetTraversalPermission.BICYCLE);
        }
      }

      // any cycleway which is opposite* allows contraflow biking
      String cycleway = way.getTag("cycleway");
      String cyclewayLeft = way.getTag("cycleway:left");
      String cyclewayRight = way.getTag("cycleway:right");
      if ((cycleway != null && cycleway.startsWith("opposite"))
          || (cyclewayLeft != null && cyclewayLeft.startsWith("opposite"))
          || (cyclewayRight != null && cyclewayRight.startsWith("opposite"))) {

        permissionsBack = permissionsBack.add(StreetTraversalPermission.BICYCLE);
      }

      String access = way.getTag("access");
      boolean noThruTraffic =
          "destination".equals(access)
              || "private".equals(access)
              || "customers".equals(access)
              || "delivery".equals(access)
              || "forestry".equals(access)
              || "agricultural".equals(access);

      if (permissionsFront != StreetTraversalPermission.NONE) {
        street = getEdgeForStreet(start, end, way, startNode, d, permissionsFront, geometry, false);
        street.setNoThruTraffic(noThruTraffic);
      }
      if (permissionsBack != StreetTraversalPermission.NONE) {
        backStreet =
            getEdgeForStreet(end, start, way, startNode, d, permissionsBack, backGeometry, true);
        backStreet.setNoThruTraffic(noThruTraffic);
      }

      /* mark edges that are on roundabouts */
      if ("roundabout".equals(tags.get("junction"))) {
        if (street != null) street.setRoundabout(true);
        if (backStreet != null) backStreet.setRoundabout(true);
      }

      return new P2<PlainStreetEdge>(street, backStreet);
    }
  /**
   * Filters all input edges and returns all those as LineString geometries, that have at least one
   * end point within the time limits. If they have only one end point inside, then the sub-edge is
   * returned.
   *
   * @param maxTime the time limit in seconds that defines the size of the walkshed
   * @param allConnectingStateEdges all Edges that have been found to connect all states < maxTime
   * @param spt the ShortestPathTree generated for the pushpin drop point as origin
   * @param angleLimit the angle tolerance to detect roads with u-shapes, i.e. Pi/2 angles, in
   *     Radiant.
   * @param distanceTolerance in percent (e.g. 1.1 = 110%) for u-shape detection based on distance
   *     criteria
   * @param hasCar is travel mode by CAR?
   * @param performSpeedTest if true applies a test to each edge to check if the edge can be
   *     traversed in time. The test can detect u-shaped roads.
   * @return
   */
  ArrayList<LineString> getLinesAndSubEdgesWithinMaxTime(
      long maxTime,
      ArrayList<Edge> allConnectingStateEdges,
      ShortestPathTree spt,
      double angleLimit,
      double distanceTolerance,
      double userSpeed,
      boolean hasCar,
      boolean performSpeedTest) {

    LOG.debug("maximal userSpeed set to: " + userSpeed + " m/sec ");
    if (hasCar) {
      LOG.debug("travel mode is set to CAR, hence the given speed may be adjusted for each edge");
    }

    ArrayList<LineString> walkShedEdges = new ArrayList<LineString>();
    ArrayList<LineString> otherEdges = new ArrayList<LineString>();
    ArrayList<LineString> borderEdges = new ArrayList<LineString>();
    ArrayList<LineString> uShapes = new ArrayList<LineString>();
    int countEdgesOutside = 0;
    // -- determination of walkshed edges via edge states
    for (Iterator iterator = allConnectingStateEdges.iterator(); iterator.hasNext(); ) {
      Edge edge = (Edge) iterator.next();
      State sFrom = spt.getState(edge.getFromVertex());
      State sTo = spt.getState(edge.getToVertex());
      if ((sFrom != null) && (sTo != null)) {
        long fromTime = sFrom.getElapsedTimeSeconds();
        long toTime = sTo.getElapsedTimeSeconds();
        long dt = Math.abs(toTime - fromTime);
        Geometry edgeGeom = edge.getGeometry();
        if ((edgeGeom != null) && (edgeGeom instanceof LineString)) {
          LineString ls = (LineString) edgeGeom;
          // detect u-shape roads/crescents - they need to be treated separately
          boolean uShapeOrLonger =
              testForUshape(
                  edge,
                  maxTime,
                  fromTime,
                  toTime,
                  angleLimit,
                  distanceTolerance,
                  userSpeed,
                  hasCar,
                  performSpeedTest);
          if (uShapeOrLonger) {
            uShapes.add(ls);
          }

          // evaluate if an edge is completely within the time or only with one end
          if ((fromTime < maxTime) && (toTime < maxTime)) {
            // this one is within the time limit on both ends, however we need to do
            // a second test if we have a u-shaped road.
            if (uShapeOrLonger) {
              treatAndAddUshapeWithinTimeLimits(
                  maxTime, userSpeed, walkShedEdges, edge, fromTime, toTime, ls, hasCar);
            } else {
              walkShedEdges.add(ls);
            }
          } // end if:fromTime & toTime < maxTime
          else {
            // check if at least one end is inside, because then we need to
            // create the sub edge
            if ((fromTime < maxTime) || (toTime < maxTime)) {
              double lineDist = edge.getDistance();
              LineString inputLS = ls;
              double fraction = 1.0;
              if (fromTime < toTime) {
                double distanceToWalkInTimeMissing =
                    distanceToMoveInRemainingTime(
                        maxTime, fromTime, dt, userSpeed, edge, hasCar, uShapeOrLonger);
                fraction = (double) distanceToWalkInTimeMissing / (double) lineDist;
              } else {
                // toTime < fromTime : invert the edge direction
                inputLS = (LineString) ls.reverse();
                double distanceToWalkInTimeMissing =
                    distanceToMoveInRemainingTime(
                        maxTime, toTime, dt, userSpeed, edge, hasCar, uShapeOrLonger);
                fraction = (double) distanceToWalkInTimeMissing / (double) lineDist;
              }
              // get the subedge
              LineString subLine = this.getSubLineString(inputLS, fraction);
              borderEdges.add(subLine);
            } else {
              // this edge is completely outside - this should actually not happen
              // we will not do anything, just count
              countEdgesOutside++;
            }
          } // end else: fromTime & toTime < maxTime
        } // end if: edge instance of LineString
        else {
          // edge is not instance of LineString
          LOG.debug("edge not instance of LineString");
        }
      } // end if(sFrom && sTo != null) start Else
      else {
        // LOG.debug("could not retrieve state for edge-endpoint"); //for a 6min car ride, there can
        // be (too) many of such messages
        Geometry edgeGeom = edge.getGeometry();
        if ((edgeGeom != null) && (edgeGeom instanceof LineString)) {
          otherEdges.add((LineString) edgeGeom);
        }
      } // end else: sFrom && sTo != null
    } // end for loop over edges
    walkShedEdges.addAll(borderEdges);
    this.debugGeoms.addAll(uShapes);
    LOG.debug("number of detected u-shapes/crescents: " + uShapes.size());
    return walkShedEdges;
  }
Esempio n. 5
0
  boolean layout(LineLabel label, LabelIndex labels) {
    String txt = label.getText();
    Rule rule = label.getRule();
    Feature f = label.getFeature();
    Geometry g = label.getGeometry();

    LineString line = null;
    if (g instanceof MultiLineString) {
      // TODO: handle multiple lines
      if (g.getNumGeometries() == 1) {
        line = (LineString) g.getGeometryN(0);
      }
    } else {
      line = (LineString) g;
    }
    if (line == null) {
      return false;
    }

    Paint p = label.get(Paint.class, Paint.class);

    // compute the bounds of the label with no rotation
    Rect r = new Rect();
    p.getTextBounds(txt, 0, txt.length(), r);

    // map it to world coordinates
    RectF bounds = new RectF(r);
    tx.getCanvasToWorld().mapRect(bounds);

    // ignore label if its too long for the line

    if (line.getLength() < bounds.width()) {
      // ignore this label
      return false;
    }

    // reverse
    if (line.getPointN(0).getX() > line.getPointN(line.getNumPoints() - 1).getX()) {
      line = (LineString) line.reverse();
    }

    // compute width of individual letters
    float[] widths = new float[txt.length()];
    p.getTextWidths(txt, widths);

    // map the widths to world space
    float sum = 0;
    for (int i = 0; i < widths.length; i++) {
      RectF s = new RectF(0, 0, widths[i], 1);
      tx.getCanvasToWorld().mapRect(s);

      widths[i] = s.width();
      sum += s.width();
    }

    // TODO: properly figure out spacing between letters
    float space = tx.getCanvasToWorld().mapRadius(1);

    // allowable angle change in consecutive characters
    double maxAngleDelta = rule.number(f, TEXT_MAX_CHAR_ANGLE_DELTA, DEFAULT_MAX_ANGLE_CHAR_DELTA);

    //
    // sample points along the line for letters
    //
    List<LineSegment> path = new ArrayList<LineSegment>();

    LineSampler sampler = new LineSampler(line.getCoordinates());

    for (int i = 0; i < txt.length(); i++) {
      // get next point
      Coordinate c1 = sampler.sample();

      // advance by width of letter
      sampler.advance(widths[i]);

      // get point for end of letter
      Coordinate c2 = sampler.sample();

      if (c1 == null || c2 == null) {
        // ran out of room
        return false;
      }

      LineSegment seg = new LineSegment(c1, c2);

      // check angle made with previous segment
      if (i > 0) {
        LineSegment prev = path.get(i - 1);
        if (Math.abs(angle(seg) - angle(prev)) > maxAngleDelta) {
          return false;
        }
      }

      path.add(seg);
      sampler.advance(space);
    }

    label.setPath(path);
    label.setShape(toShape(path, bounds.height()));
    return labels.insert(label);
  }