/******************************************************************************* * Copyright (c) MOBAC developers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package mobac.program.model; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.geom.Area; import java.awt.geom.Line2D; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import mobac.program.Logging; import mobac.program.interfaces.LayerInterface; import mobac.program.interfaces.MapInterface; import mobac.program.interfaces.MapSource; import mobac.program.interfaces.MapSpace; import mobac.program.interfaces.TileFilter; import mobac.program.tilefilter.PolygonTileFilter; import mobac.utilities.I18nUtils; import mobac.utilities.MyMath; @XmlRootElement public class MapPolygon extends Map { @XmlElement protected Polygon polygon; protected long calculatedTileCount = -1; protected MapPolygon() { } public static MapPolygon createTrackEnclosure(Layer layer, String name, MapSource mapSource, int zoom, EastNorthCoordinate[] trackPoints, int pixelDistance, TileImageParameters parameters) { MapSpace mapSpace = mapSource.getMapSpace(); Area area = new Area(); for (int i = 1; i < trackPoints.length; i++) { EastNorthCoordinate point1 = trackPoints[i - 1]; EastNorthCoordinate point2 = trackPoints[i]; //int y1 = mapSpace.cLatToY(point1.lat, zoom); //int y2 = mapSpace.cLatToY(point2.lat, zoom); //int x1 = mapSpace.cLonToX(point1.lon, zoom); //int x2 = mapSpace.cLonToX(point2.lon, zoom); Point p1 = mapSpace.cLonLatToXY(point1.lon, point1.lat, zoom); Point p2 = mapSpace.cLonLatToXY(point2.lon, point2.lat, zoom); int y1 = p1.y; int y2 = p2.y; int x1 = p1.x; int x2 = p2.x; Line2D.Double ln = new Line2D.Double(x1, y1, x2, y2); double indent = pixelDistance; // distance from central line double length = ln.getP1().distance(ln.getP2()); double dx_li = (ln.getX2() - ln.getX1()) / length * indent; double dy_li = (ln.getY2() - ln.getY1()) / length * indent; // moved p1 point double p1X = ln.getX1() - dx_li; double p1Y = ln.getY1() - dy_li; // line moved to the left double lX1 = ln.getX1() - dy_li; double lY1 = ln.getY1() + dx_li; double lX2 = ln.getX2() - dy_li; double lY2 = ln.getY2() + dx_li; // moved p2 point double p2X = ln.getX2() + dx_li; double p2Y = ln.getY2() + dy_li; // line moved to the right double rX1_ = ln.getX1() + dy_li; double rY1 = ln.getY1() - dx_li; double rX2 = ln.getX2() + dy_li; double rY2 = ln.getY2() - dx_li; Path2D p = new Path2D.Double(); p.moveTo(lX1, lY1); p.lineTo(lX2, lY2); p.lineTo(p2X, p2Y); p.lineTo(rX2, rY2); p.lineTo(rX1_, rY1); p.lineTo(p1X, p1Y); p.lineTo(lX1, lY1); area.add(new Area(p)); } PathIterator pi = area.getPathIterator(null); ArrayList<Integer> xPoints = new ArrayList<Integer>(100); ArrayList<Integer> yPoints = new ArrayList<Integer>(100); double coords[] = new double[6]; while (!pi.isDone()) { int type = pi.currentSegment(coords); switch (type) { case PathIterator.SEG_MOVETO: case PathIterator.SEG_LINETO: case PathIterator.SEG_CLOSE: xPoints.add((int) coords[0]); yPoints.add((int) coords[1]); break; default: Logging.LOG.warn("Area to polygon conversion: unexpected segment type found: " + type + " " + Arrays.toString(coords)); } pi.next(); } int[] xp = new int[xPoints.size()]; int[] yp = new int[yPoints.size()]; for (int i = 0; i < xp.length; i++) { xp[i] = xPoints.get(i); yp[i] = yPoints.get(i); } Polygon polygon = new Polygon(xp, yp, xp.length); return new MapPolygon(layer, name, mapSource, zoom, polygon, parameters); } public static MapPolygon createFromMapPolygon(Layer layer, String name, int newZoom, MapPolygon map) { Polygon oldPolygon = map.getPolygon(); int oldZoom = map.getZoom(); MapSpace mapSpace = map.getMapSource().getMapSpace(); int xPoints[] = new int[oldPolygon.npoints]; int yPoints[] = new int[oldPolygon.npoints]; Point p = new Point(); for (int i = 0; i < xPoints.length; i++) { p.x = oldPolygon.xpoints[i]; p.y = oldPolygon.ypoints[i]; Point nP = mapSpace.changeZoom(p, oldZoom, newZoom); xPoints[i] = nP.x; yPoints[i] = nP.y; } Polygon newPolygon = new Polygon(xPoints, yPoints, xPoints.length); return new MapPolygon(layer, name, map.getMapSource(), newZoom, newPolygon, map.getParameters()); } public MapPolygon(Layer layer, String name, MapSource mapSource, int zoom, Polygon polygon, TileImageParameters parameters) { super(layer, name, mapSource, zoom, null, null, parameters); this.polygon = polygon; Rectangle bounds = polygon.getBounds(); int mapSourceTileSize = mapSource.getMapSpace().getTileSize(); // Make sure the minimum tile coordinate starts/ends on the edge of a tile from the map source int minx = MyMath.roundDownToNearest(bounds.x, mapSourceTileSize); int miny = MyMath.roundDownToNearest(bounds.y, mapSourceTileSize); int maxx = MyMath.roundUpToNearest(bounds.x + bounds.width, mapSourceTileSize) - 1; int maxy = MyMath.roundUpToNearest(bounds.y + bounds.height, mapSourceTileSize) - 1; minTileCoordinate = new Point(minx, miny); maxTileCoordinate = new Point(maxx, maxy); internalCalculateTilesToDownload(); } @Override public long calculateTilesToDownload() { if (calculatedTileCount < 0) internalCalculateTilesToDownload(); return calculatedTileCount; } protected void internalCalculateTilesToDownload() { int tileSize = mapSource.getMapSpace().getTileSize(); double tileSizeD = tileSize; int xMin = minTileCoordinate.x; int xMax = maxTileCoordinate.x; int yMin = minTileCoordinate.y; int yMax = maxTileCoordinate.y; int count = 0; for (int x = xMin; x <= xMax; x += tileSize) { for (int y = yMin; y <= yMax; y += tileSize) { if (polygon.intersects(x, y, tileSizeD, tileSizeD)) count++; } } calculatedTileCount = count; } @Override public String getToolTip() { MapSpace mapSpace = mapSource.getMapSpace(); EastNorthCoordinate tl = new EastNorthCoordinate(mapSpace, zoom, minTileCoordinate.x, minTileCoordinate.y); EastNorthCoordinate br = new EastNorthCoordinate(mapSpace, zoom, maxTileCoordinate.x, maxTileCoordinate.y); StringWriter sw = new StringWriter(1024); sw.write("<html>"); sw.write(I18nUtils.localizedStringForKey("lp_atlas_info_polygon_map_title")); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_map_source_short"), mapSource.getName())); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_map_zoom_lv"), zoom)); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_polygon_map_point"), polygon.npoints)); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_map_area_start"), tl, minTileCoordinate.x, minTileCoordinate.y)); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_map_area_end"), br, maxTileCoordinate.x, maxTileCoordinate.y)); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_map_size"), (maxTileCoordinate.x - minTileCoordinate.x + 1), (maxTileCoordinate.y - minTileCoordinate.y + 1))); if (parameters != null) { sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_tile_size"), parameters.getWidth(), parameters.getHeight())); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_tile_format"), parameters.getFormat())); } else sw.write(I18nUtils.localizedStringForKey("lp_atlas_info_tile_format_origin")); sw.write(String.format(I18nUtils.localizedStringForKey("lp_atlas_info_max_tile"),calculateTilesToDownload())); sw.write("</html>"); return sw.toString(); } public Polygon getPolygon() { return polygon; } public TileFilter getTileFilter() { return new PolygonTileFilter(this); } @Override public MapInterface deepClone(LayerInterface newLayer) { MapPolygon map = (MapPolygon) super.deepClone(newLayer); map.polygon = new Polygon(polygon.xpoints, polygon.ypoints, polygon.npoints); return map; } }