Java tutorial
/***************************************************************************** * * Copyright (C) 2011 Andrew Harvey <andrew.harvey4@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ package osmTileListFromGeometry; import java.io.*; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.io.WKTReader; import java.sql.*; import java.util.ArrayList; import org.apache.commons.cli.*; /** * @author Andrew Harvey * * Generates a list of OSM tiles which fall within OSM polygons. * * We retieve the OSM polygons from an osm2pgsql database. * */ public class Main { // the polygons to use as our source polygons for which we find all the OSM tiles within static ArrayList<Geometry> imageryBoundaries; // JTS geometry factory static GeometryFactory gf; // count of how many tiles we have found so far static int tileCount; // writter for the file storing the tile list static BufferedWriter tileListWriter; /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { try { /* parse the command line arguments */ // create the command line parser CommandLineParser parser = new PosixParser(); // create the Options Options options = new Options(); options.addOption("h", "help", false, ""); options.addOption("o", "output", true, "File to write list of tiles to (required)"); options.addOption("H", "host", true, "osm2pgsql db host"); options.addOption("P", "port", true, "osm2pgsql db port"); options.addOption("D", "db", true, "osm2pgsql db name"); options.addOption("U", "user", true, "osm2pgsql db user"); options.addOption("W", "password", true, "osm2pgsql db password"); // parse the command line arguments CommandLine line = parser.parse(options, args); if (line.hasOption("help")) printUsage(options); String outputFileName = null; if (line.hasOption("output")) outputFileName = line.getOptionValue("output"); String dbHost; if (!line.hasOption("host")) dbHost = "localhost"; else dbHost = line.getOptionValue("host"); String dbPort; if (!line.hasOption("port")) dbPort = "5432"; else dbPort = line.getOptionValue("port"); String dbName; if (!line.hasOption("db")) dbName = "osm"; else dbName = line.getOptionValue("db"); String dbUser; if (!line.hasOption("user")) dbUser = "osm"; else dbUser = line.getOptionValue("user"); String dbPassword; if (!line.hasOption("password")) dbPassword = "osm"; else dbPassword = line.getOptionValue("password"); // setup the FileWriter // use STDOUT if no filename was given FileWriter tileListFileWriter; if (outputFileName == null) tileListFileWriter = new FileWriter(FileDescriptor.out); else tileListFileWriter = new FileWriter(outputFileName); tileListWriter = new BufferedWriter(tileListFileWriter); gf = new GeometryFactory(); // define an arbitary area to generate tiles within (this one is NSW, Australia) ArrayList<Geometry> nsw = new ArrayList<Geometry>(); nsw.add(gf.toGeometry( new Envelope(15676317.2673864, 17701592.7270857, -4444354.61727114, -3338769.41530374))); // everything worldwide -- bluemarble imageryBoundaries = nsw; renderAllTiles(0, 0, 0, 0, 8); // landsat imageryBoundaries = nsw; renderAllTiles(0, 0, 0, 9, 12); // nearmap // connect to a local postgresql Class.forName("org.postgresql.Driver"); java.sql.Connection conn = DriverManager .getConnection("jdbc:postgresql://" + dbHost + ":" + dbPort + "/" + dbName, dbUser, dbPassword); imageryBoundaries = grabBoundaryPolygonsFromPostgres(conn, "nearmap"); renderAllTiles(0, 0, 0, 13, 17); // start at 0,0,0 to get up to z13 quickly, but just start printing from z13 up // finish up tileListWriter.close(); // print tile summary System.out.println(""); System.out.println("###"); int totalTiles = totalTiles(0, 15); System.out.println(tileCount + " of " + totalTiles + " tiles (" + String.format("%.4f", (float) (tileCount * 100) / totalTiles) + "%)"); } catch (Exception e) { e.printStackTrace(); } } /** * Finds the number of tiles worldwide within a zoom range * * TODO: accept bounds in projected coordinates to limit tile count * * @param zoomFrom * @param zoomTo * @return Total number of tiles for all zooms within range. */ public static int totalTiles(int zoomFrom, int zoomTo) { if (zoomFrom > zoomTo) return 0; int totalTilesAtThisZoom = (int) Math.pow(2, zoomFrom) * (int) Math.pow(2, zoomFrom); return totalTilesAtThisZoom + totalTiles(zoomFrom + 1, zoomTo); } /** * For a given tile find if we need to render this tile. If yes, recursivly check all below * @param zoom * @param z_start only start printing tiles when we reach this level. * @throws IOException */ public static void renderAllTiles(int z, int x, int y, int z_start, int z_stop) throws IOException { // find the projected bounds of this tile in osm projection double[] tile = TileToMercBounds.tileToMercBounds(z, x, y); // convert the bounds to a jts geometry Envelope envolope = new Envelope(tile[0], tile[2], tile[1], tile[3]); Geometry bbox = gf.toGeometry(envolope); // see if our tile intersects with any of the polygons for the regions to render boolean intersects = false; if (imageryBoundaries == null) { intersects = true; } else { for (Geometry i : imageryBoundaries) { intersects = intersects || i.intersects(bbox); if (intersects) break; // no need to check the rest of the candidates if we already have a match } } if (intersects) { if (z >= z_start) { tileListWriter.write(z + "/" + x + "/" + y + "\n"); tileCount++; } // move on to a higher zoom level if (z < z_stop) { renderAllTiles(z + 1, x * 2, y * 2, z_start, z_stop); renderAllTiles(z + 1, x * 2 + 1, y * 2, z_start, z_stop); renderAllTiles(z + 1, x * 2, y * 2 + 1, z_start, z_stop); renderAllTiles(z + 1, x * 2 + 1, y * 2 + 1, z_start, z_stop); } } return; } /** * Retrieve OSM polygons from an osm2pgsql database, and add them to a JTS geometry list. * @param area * @return */ public static ArrayList<Geometry> grabBoundaryPolygonsFromPostgres(java.sql.Connection conn, String area) { // FIXME: use a WKB reader instead, should be faster WKTReader wktReader = new WKTReader(); ArrayList<Geometry> geom = new ArrayList<Geometry>(); try { Statement s = conn.createStatement(); ResultSet r; if (area.equals("nearmap")) r = s.executeQuery("select ST_AsText(way) from planet_osm_polygon where boundary in ('nearmap');"); else if (area.equals("australia")) r = s.executeQuery( "select ST_AsText(way) from planet_osm_line where (admin_level in ('2') and name in ('Australia'));"); else return null; while (r.next()) { geom.add(wktReader.read(r.getString(1))); } s.close(); conn.close(); System.out.println("Found " + geom.size() + " imagery boundaries."); } catch (Exception e) { e.printStackTrace(); } return geom; } public static void printUsage(Options options) { System.out.println("java -jar osmTileListFromGeometry.jar [options]"); System.out.println(); System.out.println("Options:"); System.out.println(" -h, --help " + options.getOption("help").getDescription()); System.out.println(" -o, --output " + options.getOption("output").getDescription()); System.out.println(" -H, --host " + options.getOption("host").getDescription()); System.out.println(" -P, --port " + options.getOption("port").getDescription()); System.out.println(" -D, --db " + options.getOption("db").getDescription()); System.out.println(" -U, --user " + options.getOption("user").getDescription()); System.out.println(" -W, --password " + options.getOption("password").getDescription()); System.exit(1); } }