/******************************************************************************* * 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.Dimension; import java.awt.Point; import java.io.StringWriter; import java.util.Enumeration; import javax.swing.tree.TreeNode; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlTransient; import mobac.exceptions.InvalidNameException; import mobac.program.JobDispatcher.Job; import mobac.program.download.jobenumerators.DownloadJobEnumerator; import mobac.program.interfaces.CapabilityDeletable; import mobac.program.interfaces.DownloadJobListener; import mobac.program.interfaces.DownloadableElement; 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.interfaces.ToolTipProvider; import mobac.program.tilefilter.DummyTileFilter; import mobac.utilities.I18nUtils; import mobac.utilities.tar.TarIndexedArchive; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.log4j.Logger; public class Map implements MapInterface, ToolTipProvider, CapabilityDeletable, TreeNode, DownloadableElement { protected String name; protected Layer layer; protected TileImageParameters parameters = null; @XmlAttribute protected Point maxTileCoordinate = null; @XmlAttribute protected Point minTileCoordinate = null; @XmlAttribute protected MapSource mapSource = null; protected Dimension tileDimension = null; @XmlAttribute protected int zoom; private static Logger log = Logger.getLogger(Map.class); protected Map() { } protected Map(Layer layer, String name, MapSource mapSource, int zoom, Point minTileCoordinate, Point maxTileCoordinate, TileImageParameters parameters) { this.layer = layer; this.maxTileCoordinate = maxTileCoordinate; this.minTileCoordinate = minTileCoordinate; this.name = name; this.mapSource = mapSource; this.zoom = zoom; this.parameters = parameters; calculateRuntimeValues(); } protected void calculateRuntimeValues() { if (mapSource == null) throw new RuntimeException("The map source of map " + name + " is unknown to MOBAC"); if (parameters == null) { int tileSize = mapSource.getMapSpace().getTileSize(); tileDimension = new Dimension(tileSize, tileSize); } else tileDimension = parameters.getDimension(); } public LayerInterface getLayer() { return layer; } @XmlTransient public void setLayer(LayerInterface layer) { this.layer = (Layer) layer; } public MapSource getMapSource() { return mapSource; } public Point getMaxTileCoordinate() { return maxTileCoordinate; } public Point getMinTileCoordinate() { return minTileCoordinate; } @XmlAttribute public String getName() { return name; } public int getZoom() { return zoom; } @Override public String toString() { return getName(); } public TileImageParameters getParameters() { return parameters; } public void setParameters(TileImageParameters parameters) { this.parameters = parameters; } public String getInfoText() { return "Map\n name=" + name + "\n mapSource=" + mapSource + "\n zoom=" + zoom + "\n maxTileCoordinate=" + maxTileCoordinate.x + "/" + maxTileCoordinate.y + "\n minTileCoordinate=" + minTileCoordinate.x + "/" + minTileCoordinate.y + "\n parameters=" + parameters; } 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_map_title")); sw.write(I18nUtils.localizedStringForKey("lp_atlas_info_map_source", StringEscapeUtils.escapeHtml4(mapSource.toString()), StringEscapeUtils.escapeHtml4(mapSource.getName()))); sw.write(I18nUtils.localizedStringForKey("lp_atlas_info_map_zoom_lv", zoom)); sw.write(I18nUtils.localizedStringForKey("lp_atlas_info_map_area_start", tl.toString(), minTileCoordinate.x, minTileCoordinate.y)); sw.write(I18nUtils.localizedStringForKey("lp_atlas_info_map_area_end", br.toString(), maxTileCoordinate.x, maxTileCoordinate.y)); sw.write(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() .toString())); } 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 Dimension getTileSize() { return tileDimension; } public double getMinLat() { //return mapSource.getMapSpace().cYToLat(maxTileCoordinate.y, zoom); return mapSource.getMapSpace().cXYToLonLat(maxTileCoordinate.x, maxTileCoordinate.y, zoom).y; } public double getMaxLat() { //return mapSource.getMapSpace().cYToLat(minTileCoordinate.y, zoom); return mapSource.getMapSpace().cXYToLonLat(minTileCoordinate.x, minTileCoordinate.y, zoom).y; } public double getMinLon() { //return mapSource.getMapSpace().cXToLon(minTileCoordinate.x, zoom); return mapSource.getMapSpace().cXYToLonLat(minTileCoordinate.x, minTileCoordinate.y, zoom).x; } public double getMaxLon() { //return mapSource.getMapSpace().cXToLon(maxTileCoordinate.x, zoom); return mapSource.getMapSpace().cXYToLonLat(maxTileCoordinate.x, maxTileCoordinate.y, zoom).x; } public void delete() { layer.deleteMap(this); } public void setName(String newName) throws InvalidNameException { if (layer != null) { for (MapInterface map : layer) { if ((map != this) && (newName.equals(map.getName()))) throw new InvalidNameException("There is already a map named \"" + newName + "\" in this layer.\nMap names have to unique within an layer."); } } this.name = newName; } public Enumeration<?> children() { return null; } public boolean getAllowsChildren() { return false; } public TreeNode getChildAt(int childIndex) { return null; } public int getChildCount() { return 0; } public int getIndex(TreeNode node) { return 0; } public TreeNode getParent() { return (TreeNode) layer; } public boolean isLeaf() { return true; } public long calculateTilesToDownload() { int tileSize = mapSource.getMapSpace().getTileSize(); // This algorithm has to be identically to those used in // @DownloadJobEnumerator int xMin = minTileCoordinate.x / tileSize; int xMax = maxTileCoordinate.x / tileSize; int yMin = minTileCoordinate.y / tileSize; int yMax = maxTileCoordinate.y / tileSize; int width = xMax - xMin + 1; int height = yMax - yMin + 1; int tileCount = width * height; // TODO correct tile count in case of multi-layer maps // if (mapSource instanceof MultiLayerMapSource) { // // We have a map with two layers and for each layer we have to // // download the tiles - therefore double the tileCount // tileCount *= 2; // } return tileCount; } public boolean checkData() { boolean result = false; boolean[] checks = { name == null, // 0 layer == null, // 1 maxTileCoordinate == null, // 2 minTileCoordinate == null, // 3 mapSource == null, // 4 zoom < 0 // 5 }; for (int i = 0; i < checks.length; i++) if (checks[i]) { log.error("Problem detectected with map \"" + name + "\" check: " + i); result = true; } // Automatically correct bad ordered min/max coordinates try { if (minTileCoordinate.x > maxTileCoordinate.x) { int tmp = maxTileCoordinate.x; maxTileCoordinate.x = minTileCoordinate.x; minTileCoordinate.x = tmp; } if (minTileCoordinate.y > maxTileCoordinate.y) { int tmp = maxTileCoordinate.y; maxTileCoordinate.y = minTileCoordinate.y; minTileCoordinate.y = tmp; } } catch (Exception e) { } return result; } public MapInterface deepClone(LayerInterface newLayer) { try { Map map = this.getClass().newInstance(); map.layer = (Layer) newLayer; map.mapSource = mapSource; map.maxTileCoordinate = (Point) maxTileCoordinate.clone(); map.minTileCoordinate = (Point) minTileCoordinate.clone(); map.name = name; if (parameters != null) map.parameters = (TileImageParameters) parameters.clone(); else map.parameters = null; map.tileDimension = (Dimension) tileDimension.clone(); map.zoom = zoom; return map; } catch (Exception e) { throw new RuntimeException(e); } } /** * Needs to be public - otherwise it will be kicked by ProGuard! */ public void afterUnmarshal(Unmarshaller u, Object parent) { this.layer = (Layer) parent; calculateRuntimeValues(); } public Enumeration<Job> getDownloadJobs(TarIndexedArchive tileArchive, DownloadJobListener listener) { return new DownloadJobEnumerator(this, mapSource, tileArchive, listener); } public TileFilter getTileFilter() { return new DummyTileFilter(); } }