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; }
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); }