/** * Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onebusaway.transit_data_federation.bundle.utilities; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.Parser; import org.onebusaway.csv_entities.EntityHandler; import org.onebusaway.geospatial.model.CoordinatePoint; import org.onebusaway.geospatial.model.XYPoint; import org.onebusaway.geospatial.services.UTMLibrary; import org.onebusaway.geospatial.services.UTMProjection; import org.onebusaway.gtfs.model.Stop; import org.onebusaway.gtfs.serialization.GtfsReader; import org.onebusaway.transit_data_federation.bundle.model.GtfsBundle; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Utility script to compute a polygon boundary for the set of stops in a GTFS, * useful for passing to an OpenStreetMap script for extracting an appropriate * subset of a larger OSM document. * * @author bdferris * */ public class GtfsComputePolylineBoundaryForStopsMain { private static final String ARG_FORMAT = "format"; private enum EFormat { OSM, XML, ENCODED, TEXT }; public static void main(String[] args) throws IOException, ClassNotFoundException { GtfsComputePolylineBoundaryForStopsMain main = new GtfsComputePolylineBoundaryForStopsMain(); main.run(args); } public void run(String[] args) throws IOException { try { Parser parser = new GnuParser(); Options options = new Options(); buildOptions(options); CommandLine commandLine = parser.parse(options, args); String[] remainingArgs = commandLine.getArgs(); if (remainingArgs.length < 2) { printUsage(); System.exit(-1); } List<GtfsBundle> bundles = getGtfsBundlesFromCommandLine(remainingArgs); EFormat format = getFormat(commandLine); StopToPolygonEntityHandler handler = new StopToPolygonEntityHandler(2500); for (GtfsBundle bundle : bundles) { System.err.println(bundle.getPath()); GtfsReader reader = new GtfsReader(); reader.addEntityHandler(handler); reader.setInputLocation(bundle.getPath()); if (bundle.getDefaultAgencyId() != null) reader.setDefaultAgencyId(bundle.getDefaultAgencyId()); reader.readEntities(Stop.class); } PrintWriter out = getOutputAsPrinter(remainingArgs[remainingArgs.length - 1]); switch (format) { case OSM: handleOutputAsOSMPolygon(out, handler); break; case TEXT: handleOutputAsText(out, handler); break; } out.close(); } catch (ParseException ex) { System.err.println(ex.getLocalizedMessage()); printUsage(); System.exit(-1); } System.exit(0); } private PrintWriter getOutputAsPrinter(String path) throws IOException { if (path.equals("-")) return new PrintWriter(new OutputStreamWriter(System.out)); return new PrintWriter(new FileWriter(path)); } private void handleOutputAsOSMPolygon(PrintWriter out, StopToPolygonEntityHandler handler) throws IOException { Geometry geometry = handler.getGeometry(); UTMProjection proj = handler.getProjection(); out.println("polygon"); AtomicInteger index = new AtomicInteger(); printGeometry(out, geometry, proj, index, false); out.println("END"); } private void handleOutputAsText(PrintWriter out, StopToPolygonEntityHandler handler) { Geometry geometry = handler.getGeometry(); UTMProjection proj = handler.getProjection(); out.println("polygon"); AtomicInteger index = new AtomicInteger(); printGeometry(out, geometry, proj, index, true); out.println("END"); } private List<GtfsBundle> getGtfsBundlesFromCommandLine(String[] args) { List<String> subList = new ArrayList<String>(); for (int i = 0; i < args.length - 1; i++) { subList.add(args[i]); } return UtilityLibrary.getGtfsBundlesForArguments(subList); } private void printGeometry(PrintWriter out, Geometry geometry, UTMProjection proj, AtomicInteger index, boolean latFirst) { if (geometry instanceof Polygon) printPolygon(out, (Polygon) geometry, proj, index, latFirst); else if (geometry instanceof MultiPolygon) printMultiPolygon(out, (MultiPolygon) geometry, proj, index, latFirst); else System.err.println("unknown geometry: " + geometry); } private void printMultiPolygon(PrintWriter out, MultiPolygon multi, UTMProjection proj, AtomicInteger index, boolean latFirst) { for (int i = 0; i < multi.getNumGeometries(); i++) printGeometry(out, multi.getGeometryN(i), proj, index, latFirst); } private void printPolygon(PrintWriter out, Polygon poly, UTMProjection proj, AtomicInteger index, boolean latFirst) { out.println(index.incrementAndGet()); printLineString(out, proj, poly.getExteriorRing(), latFirst); out.println("END"); for (int i = 0; i < poly.getNumInteriorRing(); i++) { out.println("!" + index.incrementAndGet()); printLineString(out, proj, poly.getInteriorRingN(i), latFirst); out.println("END"); } } private void printLineString(PrintWriter out, UTMProjection proj, LineString line, boolean latFirst) { for (int i = 0; i < line.getNumPoints(); i++) { Point point = line.getPointN(i); XYPoint p = new XYPoint(point.getX(), point.getY()); CoordinatePoint c = proj.reverse(p); if (latFirst) out.println(c.getLat() + " " + c.getLon()); else out.println(c.getLon() + " " + c.getLat()); } } protected void buildOptions(Options options) { options.addOption(ARG_FORMAT, true, ""); } protected void printUsage() { System.err.println("usage: [-format osm|xml|encoded] data-sources.xml [data-sources.xml ...] output_path"); } protected EFormat getFormat(CommandLine cli) { if (!cli.hasOption(ARG_FORMAT)) return EFormat.XML; String format = cli.getOptionValue(ARG_FORMAT); if (format.equals("osm")) return EFormat.OSM; else if (format.equals("xml")) return EFormat.XML; else if (format.equals("text")) return EFormat.TEXT; else if (format.equals("encoded")) return EFormat.ENCODED; throw new IllegalStateException("unknown format: " + format); } private static class StopToPolygonEntityHandler implements EntityHandler { private GeometryFactory _factory = new GeometryFactory(); private UTMProjection _projection; private double _bufferRadiusInMeters; private Geometry _geometry; public StopToPolygonEntityHandler(double bufferRadiusInMeters) { _bufferRadiusInMeters = bufferRadiusInMeters; } public Geometry getGeometry() { return _geometry; } public UTMProjection getProjection() { return _projection; } @Override public void handleEntity(Object bean) { Stop stop = (Stop) bean; if (_projection == null) { int zone = UTMLibrary.getUTMZoneForLongitude(stop.getLon()); _projection = new UTMProjection(zone); } XYPoint point = _projection.forward(new CoordinatePoint(stop.getLat(), stop.getLon())); Point p = _factory.createPoint(new Coordinate(point.getX(), point.getY())); Geometry geometry = p.buffer(_bufferRadiusInMeters).getEnvelope(); if (_geometry == null) _geometry = geometry; else _geometry = _geometry.union(geometry); } } }