001package org.matsim.contribs.discrete_mode_choice.components.constraints; 002 003import java.io.IOException; 004import java.net.URL; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.HashSet; 008import java.util.List; 009import java.util.Set; 010 011import org.geotools.data.DataStore; 012import org.geotools.data.DataStoreFinder; 013import org.geotools.data.simple.SimpleFeatureCollection; 014import org.geotools.data.simple.SimpleFeatureIterator; 015import org.geotools.data.simple.SimpleFeatureSource; 016import org.locationtech.jts.geom.Coordinate; 017import org.locationtech.jts.geom.Geometry; 018import org.locationtech.jts.geom.GeometryFactory; 019import org.locationtech.jts.geom.Point; 020import org.matsim.api.core.v01.Coord; 021import org.matsim.api.core.v01.Id; 022import org.matsim.api.core.v01.network.Link; 023import org.matsim.api.core.v01.network.Network; 024import org.matsim.api.core.v01.population.Person; 025import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip; 026import org.matsim.contribs.discrete_mode_choice.model.trip_based.TripConstraint; 027import org.matsim.contribs.discrete_mode_choice.model.trip_based.TripConstraintFactory; 028import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate; 029import org.opengis.feature.simple.SimpleFeature; 030 031/** 032 * This constraint decides whether a mode is allowed for a certain trip by 033 * checking whether the origin and/or destination location are within a feature 034 * of a given shape file. 035 * 036 * @author sebhoerl 037 */ 038public class ShapeFileConstraint implements TripConstraint { 039 private final static GeometryFactory geometryFactory = new GeometryFactory(); 040 041 private final Network network; 042 private final Collection<String> restrictedModes; 043 private final Set<Geometry> shapes; 044 045 public enum Requirement { 046 ORIGIN, DESTINATION, BOTH, ANY, NONE; 047 } 048 049 private final Requirement requirement; 050 051 public ShapeFileConstraint(Network network, Collection<String> restrictedModes, Requirement requirement, 052 Set<Geometry> shapes) { 053 this.network = network; 054 this.restrictedModes = restrictedModes; 055 this.shapes = shapes; 056 this.requirement = requirement; 057 } 058 059 private boolean checkLinkId(Id<Link> linkId) { 060 Link link = network.getLinks().get(linkId); 061 Coord coord = link.getCoord(); 062 Coordinate coordinate = new Coordinate(coord.getX(), coord.getY()); 063 Point point = geometryFactory.createPoint(coordinate); 064 065 for (Geometry shape : shapes) { 066 if (shape.contains(point)) { 067 return true; 068 } 069 } 070 071 return false; 072 } 073 074 @Override 075 public boolean validateBeforeEstimation(DiscreteModeChoiceTrip trip, String mode, List<String> previousModes) { 076 if (restrictedModes.contains(mode)) { 077 boolean originValid = checkLinkId(trip.getOriginActivity().getLinkId()); 078 boolean destinationValid = checkLinkId(trip.getDestinationActivity().getLinkId()); 079 080 switch (requirement) { 081 case ANY: 082 return originValid || destinationValid; 083 case BOTH: 084 return originValid && destinationValid; 085 case DESTINATION: 086 return destinationValid; 087 case ORIGIN: 088 return originValid; 089 case NONE: 090 return !(originValid || destinationValid); 091 } 092 } 093 094 return true; 095 } 096 097 @Override 098 public boolean validateAfterEstimation(DiscreteModeChoiceTrip trip, TripCandidate candidate, 099 List<TripCandidate> previousCandidates) { 100 return true; 101 } 102 103 static public class Factory implements TripConstraintFactory { 104 private final Network network; 105 private final Collection<String> restrictedModes; 106 private final Set<Geometry> shapes = new HashSet<>(); 107 private final Requirement requirement; 108 109 public Factory(Network network, Collection<String> restrictedModes, Requirement requirement, URL url) { 110 this.network = network; 111 this.restrictedModes = restrictedModes; 112 this.requirement = requirement; 113 114 try { 115 DataStore dataStore = DataStoreFinder.getDataStore(Collections.singletonMap("url", url)); 116 117 SimpleFeatureSource featureSource = dataStore.getFeatureSource(dataStore.getTypeNames()[0]); 118 SimpleFeatureCollection featureCollection = featureSource.getFeatures(); 119 SimpleFeatureIterator featureIterator = featureCollection.features(); 120 121 while (featureIterator.hasNext()) { 122 SimpleFeature feature = featureIterator.next(); 123 shapes.add((Geometry) feature.getDefaultGeometry()); 124 } 125 126 featureIterator.close(); 127 dataStore.dispose(); 128 } catch (IOException e) { 129 throw new RuntimeException(e); 130 } 131 } 132 133 @Override 134 public TripConstraint createConstraint(Person person, List<DiscreteModeChoiceTrip> trips, 135 Collection<String> availableModes) { 136 return new ShapeFileConstraint(network, restrictedModes, requirement, shapes); 137 } 138 } 139}