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}