// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.canvec_helper; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.Bounds; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.gui.MapView; import org.openstreetmap.josm.io.CachedFile; import org.openstreetmap.josm.io.IllegalDataException; import org.openstreetmap.josm.io.OsmImporter; import org.openstreetmap.josm.io.OsmImporter.OsmImporterData; class CanVecTile { private CanvecLayer layer; boolean canDownload = false; private List<String> subTileIds = new ArrayList<>(); private boolean zipScanned = false; private List<CanVecTile> subTiles = new ArrayList<>(); private boolean subTilesMade = false; private List<String> index; private int depth; private int corda; private int cordc; private boolean valid = false; private String cordb; private String cordd; private Bounds bounds; String tileid; CanVecTile(String tileid, CanvecLayer layer) { String parta, partb, partc, partd; parta = tileid.substring(0, 3); partb = tileid.substring(3, 4); partc = tileid.substring(4, 6); partd = tileid.substring(6); int a, c; a = Integer.parseInt(parta); c = Integer.parseInt(partc); realInit(a, partb, c, partd, layer, new ArrayList<String>()); } CanVecTile(int a, String b, int c, String d, CanvecLayer layer, List<String> index) { realInit(a, b, c, d, layer, index); } private void realInit(int a, String b, int c, String d, CanvecLayer layer, List<String> index) { this.index = index; this.layer = layer; corda = a; cordb = b; cordc = c; cordd = d; double zeroPointLat, zeroPointLon; double latSpan, lonSpan; double lat2, lon2; if ((a >= 0) && (a <= 119)) { // main block of tiles int column = a / 10; int row = a % 10; if (row > 6) { // cant handle x7 x8 and x9 yet return; } zeroPointLat = 40 + 4 * row; zeroPointLon = -56 - 8 * column; // size of each grid if (row <= 6) { // each is 4x8 degrees, broken into a 4x4 grid latSpan = 4; lonSpan = 8; depth = 1; } else { return; } } else { // last few tiles, very far north return; } // a 4x4 grid of A thru P // map A-P to 1-16 int grid2; if (b.isEmpty()) grid2 = 0; else grid2 = b.charAt(0) - 64; int[] rows1 = {0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3}; int[] cols1 = {0, 3, 2, 1, 0, 0, 1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 3}; lat2 = zeroPointLat + (latSpan/4)*rows1[grid2]; lon2 = zeroPointLon + (lonSpan/4)*cols1[grid2]; if (grid2 != 0) { latSpan = latSpan / 4; lonSpan = lonSpan / 4; depth = 2; } int[] rows3 = {0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3}; lat2 = lat2 + (latSpan/4)*rows3[c]; int[] cols3 = {0, 3, 2, 1, 0, 0, 1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 3}; lon2 = lon2 + (lonSpan/4)*cols3[c]; if (c != 0) { latSpan = latSpan / 4; lonSpan = lonSpan / 4; depth = 3; } if (!cordd.isEmpty()) { depth = 4; String[] foo = cordd.split("\\."); for (int i = 0; i < foo.length; i++) { int cell; if ("osm".equals(foo[i])) break; if (foo[i].isEmpty()) continue; try { cell = Integer.parseInt(foo[i]); } catch (NumberFormatException e) { continue; } switch (cell) { case 0: break; case 1: lat2 = lat2 + latSpan/2; break; case 2: lat2 = lat2 + latSpan/2; lon2 = lon2 + lonSpan/2; break; case 3: lon2 = lon2 + lonSpan/2; break; } latSpan = latSpan/2; lonSpan = lonSpan/2; } } bounds = new Bounds(lat2, lon2, lat2+latSpan, lon2+lonSpan); if (cordb.isEmpty()) this.tileid = String.format("%03d", corda); else if (cordc == 0) this.tileid = String.format("%03d%s", corda, cordb); else if (cordd.isEmpty()) this.tileid = String.format("%03d%s%02d", corda, cordb, cordc); else this.tileid = String.format("%03d%s%02d%s", corda, cordb, cordc, cordd); valid = true; } boolean isValid() { return valid; } String getTileId() { return this.tileid; } boolean isVisible(Bounds view) { return view.intersects(bounds); } Point[] getCorners(MapView mv) { LatLon min = bounds.getMin(); LatLon max = bounds.getMax(); LatLon x1 = new LatLon(min.lat(), max.lon()); LatLon x2 = new LatLon(max.lat(), min.lon()); return new Point[] { mv.getPoint(min), // south west mv.getPoint(x1), mv.getPoint(max), mv.getPoint(x2) // north west }; } public String getDownloadUrl() { return String.format("http://ftp2.cits.rncan.gc.ca/OSM/pub/%1$03d/%2$s/%1$03d%2$s%3$02d.zip", corda, cordb, cordc); } private ZipFile openZip() throws IOException { File downloadPath = new File(layer.plugin.getPluginDir() + File.separator); if (!downloadPath.mkdir() && Main.isDebugEnabled()) { Main.debug("Unable to create directory: "+downloadPath); } CachedFile tileZip = new CachedFile(getDownloadUrl()).setDestDir(downloadPath.toString()); return new ZipFile(tileZip.getFile()); } void downloadSelf() { if (zipScanned) return; ZipFile zipFile; try { zipFile = openZip(); } catch (IOException e) { Main.error(e); return; } Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if ("Metadata.txt".equals(entry.getName())) continue; subTileIds.add(entry.getName()); zipScanned = true; CanVecTile finalTile = new CanVecTile(entry.getName(), layer); if (finalTile.isValid()) { subTiles.add(finalTile); } } } void loadRawOsm() { ZipFile zipFile; try { zipFile = openZip(); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (tileid.equals(entry.getName())) { InputStream rawtile = zipFile.getInputStream(entry); OsmImporter importer = new OsmImporter(); OsmImporterData temp = importer.loadLayer(rawtile, null, entry.getName(), null); Main.worker.submit(temp.getPostLayerTask()); Main.getLayerManager().addLayer(temp.getLayer()); temp.getLayer().data.setUploadDiscouraged(false); } } } catch (IOException | IllegalDataException e) { Main.error(e); return; } } private void makeSubTiles(int layer) { List<String> buffer = new ArrayList<>(); Pattern p; if (subTilesMade) return; switch (layer) { case 1: p = Pattern.compile("\\d\\d\\d([A-Z]).*"); String lastCell = ""; for (int i = 0; i < index.size(); i++) { Matcher m = p.matcher(index.get(i)); m.matches(); String cell = m.group(1); if (cell.equals(lastCell)) { buffer.add(m.group(0)); } else if (lastCell.isEmpty()) { buffer.add(m.group(0)); } else { subTiles.add(new CanVecTile(corda, lastCell, 0, "", this.layer, buffer)); buffer = new ArrayList<>(); buffer.add(m.group(0)); } lastCell = cell; } subTiles.add(new CanVecTile(corda, lastCell, 0, "", this.layer, buffer)); break; case 2: p = Pattern.compile("\\d\\d\\d[A-Z](\\d\\d).*"); int lastCell2 = -1; for (int i = 0; i < index.size(); i++) { Matcher m = p.matcher(index.get(i)); m.matches(); int cell = Integer.parseInt(m.group(1)); if (cell == lastCell2) { buffer.add(m.group(0)); } else if (lastCell2 == -1) { buffer.add(m.group(0)); } else { subTiles.add(new CanVecTile(corda, cordb, lastCell2, "", this.layer, buffer)); buffer = new ArrayList<>(); buffer.add(m.group(0)); } lastCell2 = cell; } if (lastCell2 != -1) subTiles.add(new CanVecTile(corda, cordb, lastCell2, "", this.layer, buffer)); break; } subTilesMade = true; } void paint(Graphics2D g, MapView mv, Bounds bounds, int maxZoom) { boolean showSubTiles = false; if (!isVisible(bounds)) return; if (depth == 4) { layer.openable.add(this); } if ((depth == 3) && (bounds.getArea() < 0.5)) { // 022B01 if (zipScanned) { showSubTiles = true; } else if (canDownload) { downloadSelf(); showSubTiles = true; } else { layer.downloadable.add(this); } } else if ((depth == 2) && (bounds.getArea() < 20)) { // its a layer2 tile makeSubTiles(2); showSubTiles = true; } else if ((depth == 1) && (bounds.getArea() < 40)) { // its a layer1 tile and zoom too small // draw layer2 tiles for self makeSubTiles(1); showSubTiles = true; } if (showSubTiles && (depth < maxZoom)) { for (int i = 0; i < subTiles.size(); i++) { CanVecTile tile = subTiles.get(i); tile.paint(g, mv, bounds, maxZoom); } } else { Point[] corners = getCorners(mv); int[] xs = {corners[0].x, corners[1].x, corners[2].x, corners[3].x }; int[] ys = {corners[0].y, corners[1].y, corners[2].y, corners[3].y }; Polygon shape = new Polygon(xs, ys, 4); g.draw(shape); g.drawString(getTileId(), corners[0].x, corners[0].y); } } }