org.opentripplanner.graph_builder.module.osm.WayPropertySet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.graph_builder.module.osm;
import org.opentripplanner.common.model.P2;
import org.opentripplanner.common.model.T2;
import org.opentripplanner.model.StreetNote;
import org.opentripplanner.openstreetmap.model.OSMWithTags;
import org.opentripplanner.routing.edgetype.StreetTraversalPermission;
import org.opentripplanner.routing.services.notes.NoteMatcher;
import org.opentripplanner.util.I18NString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Information given to the GraphBuilder about how to assign permissions, safety values, names, etc. to edges based on OSM tags.
* TODO rename so that the connection with OSM tags is obvious
*
* WayPropertyPickers, CreativeNamePickers, SlopeOverridePickers, and SpeedPickers are applied to ways based on how well
* their OSMSpecifiers match a given OSM way. Generally one OSMSpecifier will win out over all the others based on the
* number of exact, partial, and wildcard tag matches. See OSMSpecifier for more details on the matching process.
*/
public class WayPropertySet {
private static Logger LOG = LoggerFactory.getLogger(WayPropertySet.class);
private List wayProperties;
/** Assign names to ways that do not have them based on OSM tags. */
private List creativeNamers;
private List slopeOverrides;
/** Assign automobile speeds based on OSM tags. */
private List speedPickers;
/** The automobile speed for street segments that do not match any SpeedPicker. */
public Float defaultSpeed;
private List notes;
private Pattern maxSpeedPattern;
/** The WayProperties applied to all ways that do not match any WayPropertyPicker. */
public WayProperties defaultProperties;
public WayPropertySetSource base;
public WayPropertySet() {
/* sensible defaults */
defaultProperties = new WayProperties();
defaultProperties.setSafetyFeatures(new P2(1.0, 1.0));
defaultProperties.setPermission(StreetTraversalPermission.ALL);
defaultSpeed = 11.2f; // 11.2 m/s ~= 25 mph ~= 40 kph, standard speed limit in the US
wayProperties = new ArrayList();
creativeNamers = new ArrayList();
slopeOverrides = new ArrayList();
speedPickers = new ArrayList();
notes = new ArrayList();
// regex courtesy http://wiki.openstreetmap.org/wiki/Key:maxspeed
// and edited
maxSpeedPattern = Pattern.compile("^([0-9][\\.0-9]+?)(?:[ ]?(kmh|km/h|kmph|kph|mph|knots))?$");
}
/**
* Applies the WayProperties whose OSMPicker best matches this way. In addition, WayProperties that are mixins
* will have their safety values applied if they match at all.
*/
public WayProperties getDataForWay(OSMWithTags way) {
WayProperties leftResult = defaultProperties;
WayProperties rightResult = defaultProperties;
int bestLeftScore = 0;
int bestRightScore = 0;
List leftMixins = new ArrayList();
List rightMixins = new ArrayList();
for (WayPropertyPicker picker : wayProperties) {
OSMSpecifier specifier = picker.getSpecifier();
WayProperties wayProperties = picker.getProperties();
P2 score = specifier.matchScores(way);
int leftScore = score.first;
int rightScore = score.second;
if (picker.isSafetyMixin()) {
if (leftScore > 0) {
leftMixins.add(wayProperties);
}
if (rightScore > 0) {
rightMixins.add(wayProperties);
}
} else {
if (leftScore > bestLeftScore) {
leftResult = wayProperties;
bestLeftScore = leftScore;
}
if (rightScore > bestRightScore) {
rightResult = wayProperties;
bestRightScore = rightScore;
}
}
}
WayProperties result = rightResult.clone();
result.setSafetyFeatures(new P2(rightResult.getSafetyFeatures().first,
leftResult.getSafetyFeatures().second));
/* apply mixins */
if (leftMixins.size() > 0) {
applyMixins(result, leftMixins, false);
}
if (rightMixins.size() > 0) {
applyMixins(result, rightMixins, true);
}
if ((bestLeftScore == 0 || bestRightScore == 0)
&& (leftMixins.size() == 0 || rightMixins.size() == 0)) {
String all_tags = dumpTags(way);
LOG.debug("Used default permissions: " + all_tags);
}
return result;
}
private String dumpTags(OSMWithTags way) {
/* generate warning message */
String all_tags = null;
Map tags = way.getTags();
for (Entry entry : tags.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String tag = key + "=" + value;
if (all_tags == null) {
all_tags = tag;
} else {
all_tags += "; " + tag;
}
}
return all_tags;
}
private void applyMixins(WayProperties result, List mixins, boolean right) {
P2 safetyFeatures = result.getSafetyFeatures();
double first = safetyFeatures.first;
double second = safetyFeatures.second;
for (WayProperties properties : mixins) {
if (right) {
second *= properties.getSafetyFeatures().second;
} else {
first *= properties.getSafetyFeatures().first;
}
}
result.setSafetyFeatures(new P2(first, second));
}
public I18NString getCreativeNameForWay(OSMWithTags way) {
CreativeNamer bestNamer = null;
int bestScore = 0;
for (CreativeNamerPicker picker : creativeNamers) {
OSMSpecifier specifier = picker.specifier;
CreativeNamer namer = picker.namer;
int score = specifier.matchScore(way);
if (score > bestScore) {
bestNamer = namer;
bestScore = score;
}
}
if (bestNamer == null) {
return null;
}
return bestNamer.generateCreativeName(way);
}
/**
* Calculate the automobile speed, in meters per second, for this way.
*/
public float getCarSpeedForWay(OSMWithTags way, boolean back) {
// first, check for maxspeed tags
Float speed = null;
Float currentSpeed;
if (way.hasTag("maxspeed:motorcar"))
speed = getMetersSecondFromSpeed(way.getTag("maxspeed:motorcar"));
if (speed == null && !back && way.hasTag("maxspeed:forward"))
speed = getMetersSecondFromSpeed(way.getTag("maxspeed:forward"));
if (speed == null && back && way.hasTag("maxspeed:reverse"))
speed = getMetersSecondFromSpeed(way.getTag("maxspeed:reverse"));
if (speed == null && way.hasTag("maxspeed:lanes")) {
for (String lane : way.getTag("maxspeed:lanes").split("\\|")) {
currentSpeed = getMetersSecondFromSpeed(lane);
// Pick the largest speed from the tag
// currentSpeed might be null if it was invalid, for instance 10|fast|20
if (currentSpeed != null && (speed == null || currentSpeed > speed))
speed = currentSpeed;
}
}
if (way.hasTag("maxspeed") && speed == null)
speed = getMetersSecondFromSpeed(way.getTag("maxspeed"));
// this would be bad, as the segment could never be traversed by an automobile
// The small epsilon is to account for possible rounding errors
if (speed != null && speed < 0.0001)
LOG.warn("Zero or negative automobile speed detected at {} based on OSM " +
"maxspeed tags; ignoring these tags", this);
// if there was a defined speed and it's not 0, we're done
if (speed != null)
return speed;
// otherwise, we use the speedPickers
int bestScore = 0;
Float bestSpeed = null;
int score;
// SpeedPickers are constructed in DefaultWayPropertySetSource with an OSM specifier
// (e.g. highway=motorway) and a default speed for that segment.
for (SpeedPicker picker : speedPickers) {
OSMSpecifier specifier = picker.specifier;
score = specifier.matchScore(way);
if (score > bestScore) {
bestScore = score;
bestSpeed = picker.speed;
}
}
if (bestSpeed != null)
return bestSpeed;
else
return this.defaultSpeed;
}
public Set> getNoteForWay(OSMWithTags way) {
HashSet> out = new HashSet<>();
for (NotePicker picker : notes) {
OSMSpecifier specifier = picker.specifier;
NoteProperties noteProperties = picker.noteProperties;
if (specifier.matchScore(way) > 0) {
out.add(noteProperties.generateNote(way));
}
}
if (out.size() == 0) {
return null;
}
return out;
}
public boolean getSlopeOverride(OSMWithTags way) {
boolean result = false;
int bestScore = 0;
for (SlopeOverridePicker picker : slopeOverrides) {
OSMSpecifier specifier = picker.getSpecifier();
int score = specifier.matchScore(way);
if (score > bestScore) {
result = picker.getOverride();
bestScore = score;
}
}
return result;
}
public void addProperties(OSMSpecifier spec, WayProperties properties, boolean mixin) {
wayProperties.add(new WayPropertyPicker(spec, properties, mixin));
}
public void addProperties(OSMSpecifier spec, WayProperties properties) {
wayProperties.add(new WayPropertyPicker(spec, properties, false));
}
public void addCreativeNamer(OSMSpecifier spec, CreativeNamer namer) {
creativeNamers.add(new CreativeNamerPicker(spec, namer));
}
public void addNote(OSMSpecifier osmSpecifier, NoteProperties properties) {
notes.add(new NotePicker(osmSpecifier, properties));
}
public void setSlopeOverride(OSMSpecifier spec, boolean override) {
slopeOverrides.add(new SlopeOverridePicker(spec, override));
}
public boolean equals(Object o) {
if (o instanceof WayPropertySet) {
WayPropertySet other = (WayPropertySet) o;
return (defaultProperties.equals(other.defaultProperties)
&& wayProperties.equals(other.wayProperties)
&& creativeNamers.equals(other.creativeNamers)
&& slopeOverrides.equals(other.slopeOverrides) && notes.equals(other.notes));
}
return false;
}
public int hashCode() {
return defaultProperties.hashCode() + wayProperties.hashCode() + creativeNamers.hashCode()
+ slopeOverrides.hashCode();
}
public void addSpeedPicker(SpeedPicker picker) {
this.speedPickers.add(picker);
}
public Float getMetersSecondFromSpeed(String speed) {
Matcher m = maxSpeedPattern.matcher(speed);
if (!m.matches())
return null;
float originalUnits;
try {
originalUnits = (float) Double.parseDouble(m.group(1));
} catch (NumberFormatException e) {
LOG.warn("Could not parse max speed {}", m.group(1));
return null;
}
String units = m.group(2);
if (units == null || units.equals(""))
units = "kmh";
// we'll be doing quite a few string comparisons here
units = units.intern();
float metersSecond;
if (units == "kmh" || units == "km/h" || units == "kmph" || units == "kph")
metersSecond = 0.277778f * originalUnits;
else if (units == "mph")
metersSecond = 0.446944f * originalUnits;
else if (units == "knots")
metersSecond = 0.514444f * originalUnits;
else
return null;
return metersSecond;
}
public void createNames(String spec, String patternKey) {
String pattern = patternKey;
CreativeNamer namer = new CreativeNamer(pattern);
addCreativeNamer(new OSMSpecifier(spec), namer);
}
public void createNotes(String spec, String patternKey, NoteMatcher matcher) {
String pattern = patternKey;
// TODO: notes aren't localized
NoteProperties properties = new NoteProperties(pattern, matcher);
addNote(new OSMSpecifier(spec), properties);
}
public void setProperties(String spec,
StreetTraversalPermission permission) {
setProperties( spec, permission, 1.0, 1.0);
}
/**
* Note that the safeties here will be adjusted such that the safest street
* has a safety value of 1, with all others scaled proportionately.
*/
public void setProperties(String spec,
StreetTraversalPermission permission, double safety, double safetyBack) {
setProperties(spec, permission, safety, safetyBack, false);
}
public void setProperties(String spec,
StreetTraversalPermission permission, double safety, double safetyBack, boolean mixin) {
WayProperties properties = new WayProperties();
properties.setPermission(permission);
properties.setSafetyFeatures(new P2(safety, safetyBack));
addProperties(new OSMSpecifier(spec), properties, mixin);
}
public void setCarSpeed(String spec, float speed) {
SpeedPicker picker = new SpeedPicker();
picker.specifier = new OSMSpecifier(spec);
picker.speed = speed;
addSpeedPicker(picker);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy