jmapviewer-2.7/0000755000000000000000000000000013255754210012235 5ustar rootrootjmapviewer-2.7/build.xml0000644000000000000000000001157613255753422014074 0ustar rootroot JMapViewer - Javadoc]]> JMapViewer]]> jmapviewer-2.7/Gpl.txt0000644000000000000000000004325413255753414013535 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. jmapviewer-2.7/Readme.txt0000644000000000000000000000253313255753422014202 0ustar rootrootJMapViewer (c) 2007, Tim Haussmann (c) 2008-2012, Jan Peter Stotz (c) 2009-2018, Dirk Stöcker (c) 2009, Stefan Zeller (c) 2009, Karl Guggisberg (c) 2009, Dave Hansen (c) 2010-2011, Ian Dees (c) 2010-2011, Michael Vigovsky (c) 2011-2017, Paul Hartmann (c) 2011-2016, Gleb Smirnoff (c) 2011-2018, Vincent Privat (c) 2011, Jason Huntley (c) 2012-2016, Simon Legner (c) 2012, Teemu Koskinen (c) 2012, Jiri Klement (c) 2013, Matt Hoover (c) 2013, Alexei Kasatkin (c) 2013, Galo Higueras (c) 2015-2016, Wiktor Niesiobędzki (c) 2017, Robert Scott This work bases partly on the JOSM plugin "Slippy Map Chooser" by Tim Haussmann License: GPL FAQ: 1. What is JMapViewer? JMapViewer is a Java Swing component for integrating OSM maps in to your Java application. JMapViewer allows you to set markers on the map or zoom to a specific location on the map. 2. How does JMapViewer work? JMapViewer loads bitmap tiles from the OpenStreetmap tile server (Mapnik renderer). Therefore any application using JMapViewer requires a working Internet connection. 3. How do I use JMapViewer in my application? You can just create an instance of the class org.openstreetmap.gui.jmapviewer.JMapViewer using the default constructor and add it to your panel/frame/windows. For more details please see the Demo class in the same package. jmapviewer-2.7/src/0000755000000000000000000000000013255754210013024 5ustar rootrootjmapviewer-2.7/src/org/0000755000000000000000000000000013255753414013620 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/0000755000000000000000000000000013255753414016506 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/0000755000000000000000000000000013255753414017272 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/0000755000000000000000000000000013255753422021442 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/Tile.java0000644000000000000000000003352713255753416023217 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; import javax.imageio.ImageIO; import org.openstreetmap.gui.jmapviewer.interfaces.TileCache; import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; /** * Holds one map tile. Additionally the code for loading the tile image and * painting it is also included in this class. * * @author Jan Peter Stotz */ public class Tile { /** * Hourglass image that is displayed until a map tile has been loaded, except for overlay sources */ public static final BufferedImage LOADING_IMAGE = loadImage("images/hourglass.png"); /** * Red cross image that is displayed after a loading error, except for overlay sources */ public static final BufferedImage ERROR_IMAGE = loadImage("images/error.png"); protected TileSource source; protected int xtile; protected int ytile; protected int zoom; protected BufferedImage image; protected String key; protected volatile boolean loaded; // field accessed by multiple threads without any monitors, needs to be volatile protected volatile boolean loading; protected volatile boolean error; protected String error_message; /** TileLoader-specific tile metadata */ protected Map metadata; /** * Creates a tile with empty image. * * @param source Tile source * @param xtile X coordinate * @param ytile Y coordinate * @param zoom Zoom level */ public Tile(TileSource source, int xtile, int ytile, int zoom) { this(source, xtile, ytile, zoom, LOADING_IMAGE); } /** * Creates a tile with specified image. * * @param source Tile source * @param xtile X coordinate * @param ytile Y coordinate * @param zoom Zoom level * @param image Image content */ public Tile(TileSource source, int xtile, int ytile, int zoom, BufferedImage image) { this.source = source; this.xtile = xtile; this.ytile = ytile; this.zoom = zoom; this.image = image; this.key = getTileKey(source, xtile, ytile, zoom); } private static BufferedImage loadImage(String path) { try { return ImageIO.read(JMapViewer.class.getResourceAsStream(path)); } catch (IOException | IllegalArgumentException ex) { ex.printStackTrace(); return null; } } private static class CachedCallable implements Callable { private V result; private Callable callable; /** * Wraps callable so it is evaluated only once * @param callable to cache */ CachedCallable(Callable callable) { this.callable = callable; } @Override public synchronized V call() { try { if (result == null) { result = callable.call(); } return result; } catch (Exception e) { // this should not happen here throw new RuntimeException(e); } } } /** * Tries to get tiles of a lower or higher zoom level (one or two level * difference) from cache and use it as a placeholder until the tile has been loaded. * @param cache Tile cache */ public void loadPlaceholderFromCache(TileCache cache) { /* * use LazyTask as creation of BufferedImage is very expensive * this way we can avoid object creation until we're sure it's needed */ final CachedCallable tmpImage = new CachedCallable<>(new Callable() { @Override public BufferedImage call() throws Exception { return new BufferedImage(source.getTileSize(), source.getTileSize(), BufferedImage.TYPE_INT_ARGB); } }); for (int zoomDiff = 1; zoomDiff < 5; zoomDiff++) { // first we check if there are already the 2^x tiles // of a higher detail level int zoomHigh = zoom + zoomDiff; if (zoomDiff < 3 && zoomHigh <= JMapViewer.MAX_ZOOM) { int factor = 1 << zoomDiff; int xtileHigh = xtile << zoomDiff; int ytileHigh = ytile << zoomDiff; final double scale = 1.0 / factor; /* * use LazyTask for graphics to avoid evaluation of tmpImage, until we have * something to draw */ CachedCallable graphics = new CachedCallable<>(new Callable() { @Override public Graphics2D call() throws Exception { Graphics2D g = (Graphics2D) tmpImage.call().getGraphics(); g.setTransform(AffineTransform.getScaleInstance(scale, scale)); return g; } }); int paintedTileCount = 0; for (int x = 0; x < factor; x++) { for (int y = 0; y < factor; y++) { Tile tile = cache.getTile(source, xtileHigh + x, ytileHigh + y, zoomHigh); if (tile != null && tile.isLoaded()) { paintedTileCount++; tile.paint(graphics.call(), x * source.getTileSize(), y * source.getTileSize()); } } } if (paintedTileCount == factor * factor) { image = tmpImage.call(); return; } } int zoomLow = zoom - zoomDiff; if (zoomLow >= JMapViewer.MIN_ZOOM) { int xtileLow = xtile >> zoomDiff; int ytileLow = ytile >> zoomDiff; final int factor = 1 << zoomDiff; final double scale = factor; CachedCallable graphics = new CachedCallable<>(new Callable() { @Override public Graphics2D call() throws Exception { Graphics2D g = (Graphics2D) tmpImage.call().getGraphics(); AffineTransform at = new AffineTransform(); int translateX = (xtile % factor) * source.getTileSize(); int translateY = (ytile % factor) * source.getTileSize(); at.setTransform(scale, 0, 0, scale, -translateX, -translateY); g.setTransform(at); return g; } }); Tile tile = cache.getTile(source, xtileLow, ytileLow, zoomLow); if (tile != null && tile.isLoaded()) { tile.paint(graphics.call(), 0, 0); image = tmpImage.call(); return; } } } } public TileSource getSource() { return source; } /** * Returns the X coordinate. * @return tile number on the x axis of this tile */ public int getXtile() { return xtile; } /** * Returns the Y coordinate. * @return tile number on the y axis of this tile */ public int getYtile() { return ytile; } /** * Returns the zoom level. * @return zoom level of this tile */ public int getZoom() { return zoom; } /** * @return tile indexes of the top left corner as TileXY object */ public TileXY getTileXY() { return new TileXY(xtile, ytile); } public BufferedImage getImage() { return image; } public void setImage(BufferedImage image) { this.image = image; } public void loadImage(InputStream input) throws IOException { setImage(ImageIO.read(input)); } /** * @return key that identifies a tile */ public String getKey() { return key; } public boolean isLoaded() { return loaded; } public boolean isLoading() { return loading; } public void setLoaded(boolean loaded) { this.loaded = loaded; } public String getUrl() throws IOException { return source.getTileUrl(zoom, xtile, ytile); } /** * Paints the tile-image on the {@link Graphics} g at the * position x/y. * * @param g the Graphics object * @param x x-coordinate in g * @param y y-coordinate in g */ public void paint(Graphics g, int x, int y) { if (image == null) return; g.drawImage(image, x, y, null); } /** * Paints the tile-image on the {@link Graphics} g at the * position x/y. * * @param g the Graphics object * @param x x-coordinate in g * @param y y-coordinate in g * @param width width that tile should have * @param height height that tile should have */ public void paint(Graphics g, int x, int y, int width, int height) { if (image == null) return; g.drawImage(image, x, y, width, height, null); } @Override public String toString() { StringBuilder sb = new StringBuilder(35).append("Tile ").append(key); if (loading) { sb.append(" [LOADING...]"); } if (loaded) { sb.append(" [loaded]"); } if (error) { sb.append(" [ERROR]"); } return sb.toString(); } /** * Note that the hash code does not include the {@link #source}. * Therefore a hash based collection can only contain tiles * of one {@link #source}. */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + xtile; result = prime * result + ytile; result = prime * result + zoom; return result; } /** * Compares this object with obj based on * the fields {@link #xtile}, {@link #ytile} and * {@link #zoom}. * The {@link #source} field is ignored. */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Tile other = (Tile) obj; if (xtile != other.xtile) return false; if (ytile != other.ytile) return false; if (zoom != other.zoom) return false; return getTileSource().equals(other.getTileSource()); } public static String getTileKey(TileSource source, int xtile, int ytile, int zoom) { return zoom + "/" + xtile + "/" + ytile + "@" + source.getName(); } public String getStatus() { if (this.error) return "error"; if (this.loaded) return "loaded"; if (this.loading) return "loading"; return "new"; } public boolean hasError() { return error; } public String getErrorMessage() { return error_message; } public void setError(Exception e) { setError(e.toString()); } public void setError(String message) { error = true; setImage(ERROR_IMAGE); error_message = message; } /** * Puts the given key/value pair to the metadata of the tile. * If value is null, the (possibly existing) key/value pair is removed from * the meta data. * * @param key Key * @param value Value */ public void putValue(String key, String value) { if (value == null || value.isEmpty()) { if (metadata != null) { metadata.remove(key); } return; } if (metadata == null) { metadata = new HashMap<>(); } metadata.put(key, value); } /** * returns the metadata of the Tile * * @param key metadata key that should be returned * @return null if no such metadata exists, or the value of the metadata */ public String getValue(String key) { if (metadata == null) return null; return metadata.get(key); } /** * * @return metadata of the tile */ public Map getMetadata() { if (metadata == null) { metadata = new HashMap<>(); } return metadata; } /** * indicate that loading process for this tile has started */ public void initLoading() { error = false; loading = true; } /** * indicate that loading process for this tile has ended */ public void finishLoading() { loading = false; loaded = true; } /** * * @return TileSource from which this tile comes */ public TileSource getTileSource() { return source; } /** * indicate that loading process for this tile has been canceled */ public void loadingCanceled() { loading = false; loaded = false; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/LayerGroup.java0000644000000000000000000000365313255753416024410 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.util.List; public class LayerGroup extends AbstractLayer { private List layers; public LayerGroup(String name) { super(name); } public LayerGroup(String name, String description) { super(name, description); } public LayerGroup(String name, Style style) { super(name, style); } public LayerGroup(String name, String description, Style style) { super(name, description, style); } public LayerGroup(LayerGroup parent, String name) { super(parent, name); } public LayerGroup(LayerGroup parent, String name, String description, Style style) { super(name, description, style); } public List getLayers() { return layers; } public void setElements(List layers) { this.layers = layers; } public Layer addLayer(String name) { Layer layer = new Layer(this, name); layers = add(layers, layer); return layer; } public LayerGroup add(AbstractLayer layer) { layer.setParent(this); layers = add(layers, layer); return this; } public void calculateVisibleTexts() { Boolean calculate = null; if (layers != null && !layers.isEmpty()) { calculate = layers.get(0).isVisibleTexts(); for (int i = 1; i < layers.size(); i++) { calculate = resultOf(calculate, layers.get(i).isVisibleTexts()); } } setVisibleTexts(calculate); if (getParent() != null) getParent().calculateVisibleTexts(); } public Boolean resultOf(Boolean b1, Boolean b2) { if (b1 != null && b1.equals(b2)) { return b1; } return Boolean.FALSE; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/Demo.java0000644000000000000000000002465013255753416023203 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent; import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener; import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource; import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource; /** * Demonstrates the usage of {@link JMapViewer} * * @author Jan Peter Stotz * */ public class Demo extends JFrame implements JMapViewerEventListener { private static final long serialVersionUID = 1L; private final JMapViewerTree treeMap; private final JLabel zoomLabel; private final JLabel zoomValue; private final JLabel mperpLabelName; private final JLabel mperpLabelValue; /** * Constructs the {@code Demo}. */ public Demo() { super("JMapViewer Demo"); setSize(400, 400); treeMap = new JMapViewerTree("Zones"); // Listen to the map viewer for user operations so components will // receive events and update map().addJMVListener(this); setLayout(new BorderLayout()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setExtendedState(JFrame.MAXIMIZED_BOTH); JPanel panel = new JPanel(new BorderLayout()); JPanel panelTop = new JPanel(); JPanel panelBottom = new JPanel(); JPanel helpPanel = new JPanel(); mperpLabelName = new JLabel("Meters/Pixels: "); mperpLabelValue = new JLabel(String.format("%s", map().getMeterPerPixel())); zoomLabel = new JLabel("Zoom: "); zoomValue = new JLabel(String.format("%s", map().getZoom())); add(panel, BorderLayout.NORTH); add(helpPanel, BorderLayout.SOUTH); panel.add(panelTop, BorderLayout.NORTH); panel.add(panelBottom, BorderLayout.SOUTH); JLabel helpLabel = new JLabel("Use right mouse button to move,\n " + "left double click or mouse wheel to zoom."); helpPanel.add(helpLabel); JButton button = new JButton("setDisplayToFitMapMarkers"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { map().setDisplayToFitMapMarkers(); } }); JComboBox tileSourceSelector = new JComboBox<>(new TileSource[] { new OsmTileSource.Mapnik(), new OsmTileSource.CycleMap(), new BingAerialTileSource(), }); tileSourceSelector.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { map().setTileSource((TileSource) e.getItem()); } }); JComboBox tileLoaderSelector; tileLoaderSelector = new JComboBox<>(new TileLoader[] {new OsmTileLoader(map())}); tileLoaderSelector.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { map().setTileLoader((TileLoader) e.getItem()); } }); map().setTileLoader((TileLoader) tileLoaderSelector.getSelectedItem()); panelTop.add(tileSourceSelector); panelTop.add(tileLoaderSelector); final JCheckBox showMapMarker = new JCheckBox("Map markers visible"); showMapMarker.setSelected(map().getMapMarkersVisible()); showMapMarker.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { map().setMapMarkerVisible(showMapMarker.isSelected()); } }); panelBottom.add(showMapMarker); /// final JCheckBox showTreeLayers = new JCheckBox("Tree Layers visible"); showTreeLayers.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { treeMap.setTreeVisible(showTreeLayers.isSelected()); } }); panelBottom.add(showTreeLayers); /// final JCheckBox showToolTip = new JCheckBox("ToolTip visible"); showToolTip.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { map().setToolTipText(null); } }); panelBottom.add(showToolTip); /// final JCheckBox showTileGrid = new JCheckBox("Tile grid visible"); showTileGrid.setSelected(map().isTileGridVisible()); showTileGrid.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { map().setTileGridVisible(showTileGrid.isSelected()); } }); panelBottom.add(showTileGrid); final JCheckBox showZoomControls = new JCheckBox("Show zoom controls"); showZoomControls.setSelected(map().getZoomControlsVisible()); showZoomControls.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { map().setZoomContolsVisible(showZoomControls.isSelected()); } }); panelBottom.add(showZoomControls); final JCheckBox scrollWrapEnabled = new JCheckBox("Scrollwrap enabled"); scrollWrapEnabled.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { map().setScrollWrapEnabled(scrollWrapEnabled.isSelected()); } }); panelBottom.add(scrollWrapEnabled); panelBottom.add(button); panelTop.add(zoomLabel); panelTop.add(zoomValue); panelTop.add(mperpLabelName); panelTop.add(mperpLabelValue); add(treeMap, BorderLayout.CENTER); // LayerGroup germanyGroup = new LayerGroup("Germany"); Layer germanyWestLayer = germanyGroup.addLayer("Germany West"); Layer germanyEastLayer = germanyGroup.addLayer("Germany East"); MapMarkerDot eberstadt = new MapMarkerDot(germanyEastLayer, "Eberstadt", 49.814284999, 8.642065999); MapMarkerDot ebersheim = new MapMarkerDot(germanyWestLayer, "Ebersheim", 49.91, 8.24); MapMarkerDot empty = new MapMarkerDot(germanyEastLayer, 49.71, 8.64); MapMarkerDot darmstadt = new MapMarkerDot(germanyEastLayer, "Darmstadt", 49.8588, 8.643); map().addMapMarker(eberstadt); map().addMapMarker(ebersheim); map().addMapMarker(empty); Layer franceLayer = treeMap.addLayer("France"); map().addMapMarker(new MapMarkerDot(franceLayer, "La Gallerie", 48.71, -1)); map().addMapMarker(new MapMarkerDot(43.604, 1.444)); map().addMapMarker(new MapMarkerCircle(53.343, -6.267, 0.666)); map().addMapRectangle(new MapRectangleImpl(new Coordinate(53.343, -6.267), new Coordinate(43.604, 1.444))); map().addMapMarker(darmstadt); treeMap.addLayer(germanyWestLayer); treeMap.addLayer(germanyEastLayer); MapPolygon bermudas = new MapPolygonImpl(c(49, 1), c(45, 10), c(40, 5)); map().addMapPolygon(bermudas); map().addMapPolygon(new MapPolygonImpl(germanyEastLayer, "Riedstadt", ebersheim, darmstadt, eberstadt, empty)); map().addMapMarker(new MapMarkerCircle(germanyWestLayer, "North of Suisse", new Coordinate(48, 7), .5)); Layer spain = treeMap.addLayer("Spain"); map().addMapMarker(new MapMarkerCircle(spain, "La Garena", new Coordinate(40.4838, -3.39), .002)); spain.setVisible(Boolean.FALSE); Layer wales = treeMap.addLayer("UK"); map().addMapRectangle(new MapRectangleImpl(wales, "Wales", c(53.35, -4.57), c(51.64, -2.63))); // map.setDisplayPosition(new Coordinate(49.807, 8.6), 11); // map.setTileGridVisible(true); map().addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { map().getAttribution().handleAttribution(e.getPoint(), true); } } }); map().addMouseMotionListener(new MouseAdapter() { @Override public void mouseMoved(MouseEvent e) { Point p = e.getPoint(); boolean cursorHand = map().getAttribution().handleAttributionCursor(p); if (cursorHand) { map().setCursor(new Cursor(Cursor.HAND_CURSOR)); } else { map().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } if (showToolTip.isSelected()) map().setToolTipText(map().getPosition(p).toString()); } }); } private JMapViewer map() { return treeMap.getViewer(); } private static Coordinate c(double lat, double lon) { return new Coordinate(lat, lon); } /** * @param args Main program arguments */ public static void main(String[] args) { new Demo().setVisible(true); } private void updateZoomParameters() { if (mperpLabelValue != null) mperpLabelValue.setText(String.format("%s", map().getMeterPerPixel())); if (zoomValue != null) zoomValue.setText(String.format("%s", map().getZoom())); } @Override public void processCommand(JMVCommandEvent command) { if (command.getCommand().equals(JMVCommandEvent.COMMAND.ZOOM) || command.getCommand().equals(JMVCommandEvent.COMMAND.MOVE)) { updateZoomParameters(); } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/Coordinate.java0000644000000000000000000000370113255753422024375 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.geom.Point2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; /** * This class encapsulates a Point2D.Double and provide access * via lat and lon. * * @author Jan Peter Stotz * */ public class Coordinate implements ICoordinate { private transient Point2D.Double data; public Coordinate(double lat, double lon) { data = new Point2D.Double(lon, lat); } @Override public double getLat() { return data.y; } @Override public void setLat(double lat) { data.y = lat; } @Override public double getLon() { return data.x; } @Override public void setLon(double lon) { data.x = lon; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(data.x); out.writeObject(data.y); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { data = new Point2D.Double(); data.x = (Double) in.readObject(); data.y = (Double) in.readObject(); } @Override public String toString() { return "Coordinate[" + data.y + ", " + data.x + ']'; } @Override public int hashCode() { int hash = 3; hash = 61 * hash + Objects.hashCode(this.data); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Coordinate other = (Coordinate) obj; return Objects.equals(this.data, other.data); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/Style.java0000644000000000000000000000334413255753422023411 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.Stroke; public class Style { private Color color; private Color backColor; private Stroke stroke; private Font font; private static final AlphaComposite TRANSPARENCY = AlphaComposite.getInstance(AlphaComposite.SRC_OVER); private static final AlphaComposite OPAQUE = AlphaComposite.getInstance(AlphaComposite.SRC); public Style() { super(); } public Style(Color color, Color backColor, Stroke stroke, Font font) { super(); this.color = color; this.backColor = backColor; this.stroke = stroke; this.font = font; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public Color getBackColor() { return backColor; } public void setBackColor(Color backColor) { this.backColor = backColor; } public Stroke getStroke() { return stroke; } public void setStroke(Stroke stroke) { this.stroke = stroke; } public Font getFont() { return font; } public void setFont(Font font) { this.font = font; } private static AlphaComposite getAlphaComposite(Color color) { return color.getAlpha() == 255 ? OPAQUE : TRANSPARENCY; } public AlphaComposite getAlphaComposite() { return getAlphaComposite(color); } public AlphaComposite getBackAlphaComposite() { return getAlphaComposite(backColor); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/Layer.java0000644000000000000000000000244113255753422023362 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.util.List; import org.openstreetmap.gui.jmapviewer.interfaces.MapObject; public class Layer extends AbstractLayer { private List elements; public Layer(String name) { super(name); } public Layer(String name, String description) { super(name, description); } public Layer(String name, Style style) { super(name, style); } public Layer(String name, String description, Style style) { super(name, description, style); } public Layer(LayerGroup parent, String name) { super(parent, name); } public Layer(LayerGroup parent, String name, Style style) { super(parent, name, style); } public Layer(LayerGroup parent, String name, String description, Style style) { super(parent, name, description, style); } public List getElements() { return elements; } public void setElements(List elements) { this.elements = elements; } public Layer add(MapObject element) { element.setLayer(this); elements = add(elements, element); return this; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/JMapController.java0000644000000000000000000000211713255753416025204 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelListener; /** * Abstract base class for all mouse controller implementations. For * implementing your own controller create a class that derives from this one * and implements one or more of the following interfaces: *
    *
  • {@link MouseListener}
  • *
  • {@link MouseMotionListener}
  • *
  • {@link MouseWheelListener}
  • *
* * @author Jan Peter Stotz */ public abstract class JMapController { protected JMapViewer map; public JMapController(JMapViewer map) { this.map = map; if (this instanceof MouseListener) map.addMouseListener((MouseListener) this); if (this instanceof MouseWheelListener) map.addMouseWheelListener((MouseWheelListener) this); if (this instanceof MouseMotionListener) map.addMouseMotionListener((MouseMotionListener) this); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/Projected.java0000644000000000000000000000352113255753422024225 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.geom.Point2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.openstreetmap.gui.jmapviewer.interfaces.IProjected; /** * Projected coordinates represented by an encapsulates a Point2D.Double value. */ public class Projected implements IProjected { private transient Point2D.Double data; /** * Constructs a new {@code Projected}. * @param east easting * @param north northing */ public Projected(double east, double north) { data = new Point2D.Double(east, north); } @Override public double getEast() { return data.x; } @Override public double getNorth() { return data.y; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(data.x); out.writeObject(data.y); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { data = new Point2D.Double(); data.x = (Double) in.readObject(); data.y = (Double) in.readObject(); } @Override public String toString() { return "Projected[" + data.y + ", " + data.x + ']'; } @Override public int hashCode() { int hash = 3; hash = 61 * hash + Objects.hashCode(this.data); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Projected other = (Projected) obj; return Objects.equals(this.data, other.data); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/events/0000755000000000000000000000000013255753422022746 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/events/JMVCommandEvent.java0000644000000000000000000000163713255753422026555 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.events; import java.util.EventObject; /** * Used for passing events between UI components and other * objects that register as a JMapViewerEventListener * * @author Jason Huntley * */ public class JMVCommandEvent extends EventObject { public enum COMMAND { MOVE, ZOOM } private COMMAND command; /** * */ private static final long serialVersionUID = 8701544867914969620L; public JMVCommandEvent(COMMAND cmd, Object source) { super(source); setCommand(cmd); } /** * @return the command */ public COMMAND getCommand() { return command; } /** * @param command the command to set */ public void setCommand(COMMAND command) { this.command = command; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/MapMarkerDot.java0000644000000000000000000000330113255753416024633 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.Color; import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker; /** * A simple implementation of the {@link MapMarker} interface. Each map marker * is painted as a circle with a black border line and filled with a specified * color. * * @author Jan Peter Stotz * */ public class MapMarkerDot extends MapMarkerCircle { public static final int DOT_RADIUS = 5; public MapMarkerDot(Coordinate coord) { this(null, null, coord); } public MapMarkerDot(String name, Coordinate coord) { this(null, name, coord); } public MapMarkerDot(Layer layer, Coordinate coord) { this(layer, null, coord); } public MapMarkerDot(Layer layer, String name, Coordinate coord) { this(layer, name, coord, getDefaultStyle()); } public MapMarkerDot(Color color, double lat, double lon) { this(null, null, lat, lon); setColor(color); } public MapMarkerDot(double lat, double lon) { this(null, null, lat, lon); } public MapMarkerDot(Layer layer, double lat, double lon) { this(layer, null, lat, lon); } public MapMarkerDot(Layer layer, String name, double lat, double lon) { this(layer, name, new Coordinate(lat, lon), getDefaultStyle()); } public MapMarkerDot(Layer layer, String name, Coordinate coord, Style style) { super(layer, name, coord, DOT_RADIUS, STYLE.FIXED, style); } public static Style getDefaultStyle() { return new Style(Color.BLACK, Color.YELLOW, null, getDefaultFont()); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/OsmMercator.java0000644000000000000000000001600413255753422024541 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; /** * This class implements the Mercator Projection as it is used by OpenStreetMap * (and google). It provides methods to translate coordinates from 'map space' * into latitude and longitude (on the WGS84 ellipsoid) and vice versa. Map * space is measured in pixels. The origin of the map space is the top left * corner. The map space origin (0,0) has latitude ~85 and longitude -180. * @author Jan Peter Stotz * @author Jason Huntley */ public class OsmMercator { /** * default tile size */ public static final int DEFAUL_TILE_SIZE = 256; /** maximum latitude (north) for mercator display */ public static final double MAX_LAT = 85.05112877980659; /** minimum latitude (south) for mercator display */ public static final double MIN_LAT = -85.05112877980659; /** equatorial earth radius for EPSG:3857 (Mercator) */ public static final double EARTH_RADIUS = 6_378_137; /** * instance with tile size of 256 for easy conversions */ public static final OsmMercator MERCATOR_256 = new OsmMercator(); /** tile size of the displayed tiles */ private int tileSize = DEFAUL_TILE_SIZE; /** * Creates instance with default tile size of 256 */ public OsmMercator() { } /** * Creates instance with provided tile size. * @param tileSize tile size in pixels */ public OsmMercator(int tileSize) { this.tileSize = tileSize; } public double radius(int aZoomlevel) { return (tileSize * (1 << aZoomlevel)) / (2.0 * Math.PI); } /** * Returns the absolut number of pixels in y or x, defined as: 2^Zoomlevel * * tileSize where tileSize is the width of a tile in pixels * * @param aZoomlevel zoom level to request pixel data * @return number of pixels */ public int getMaxPixels(int aZoomlevel) { return tileSize * (1 << aZoomlevel); } public int falseEasting(int aZoomlevel) { return getMaxPixels(aZoomlevel) / 2; } public int falseNorthing(int aZoomlevel) { return -1 * getMaxPixels(aZoomlevel) / 2; } /** * Transform pixelspace to coordinates and get the distance. * * @param x1 the first x coordinate * @param y1 the first y coordinate * @param x2 the second x coordinate * @param y2 the second y coordinate * * @param zoomLevel the zoom level * @return the distance */ public double getDistance(int x1, int y1, int x2, int y2, int zoomLevel) { double la1 = yToLat(y1, zoomLevel); double lo1 = xToLon(x1, zoomLevel); double la2 = yToLat(y2, zoomLevel); double lo2 = xToLon(x2, zoomLevel); return getDistance(la1, lo1, la2, lo2); } /** * Gets the distance using Spherical law of cosines. * * @param la1 the Latitude in degrees * @param lo1 the Longitude in degrees * @param la2 the Latitude from 2nd coordinate in degrees * @param lo2 the Longitude from 2nd coordinate in degrees * @return the distance */ public double getDistance(double la1, double lo1, double la2, double lo2) { double aStartLat = Math.toRadians(la1); double aStartLong = Math.toRadians(lo1); double aEndLat = Math.toRadians(la2); double aEndLong = Math.toRadians(lo2); double distance = Math.acos(Math.sin(aStartLat) * Math.sin(aEndLat) + Math.cos(aStartLat) * Math.cos(aEndLat) * Math.cos(aEndLong - aStartLong)); return EARTH_RADIUS * distance; } /** * Transform longitude to pixelspace * *

* Mathematical optimization
* * x = radius(aZoomlevel) * toRadians(aLongitude) + falseEasting(aZoomLevel)
* x = getMaxPixels(aZoomlevel) / (2 * PI) * (aLongitude * PI) / 180 + getMaxPixels(aZoomlevel) / 2
* x = getMaxPixels(aZoomlevel) * aLongitude / 360 + 180 * getMaxPixels(aZoomlevel) / 360
* x = getMaxPixels(aZoomlevel) * (aLongitude + 180) / 360
*
*

* * @param aLongitude * [-180..180] * @param aZoomlevel zoom level * @return [0..2^Zoomlevel*TILE_SIZE[ */ public double lonToX(double aLongitude, int aZoomlevel) { int mp = getMaxPixels(aZoomlevel); double x = (mp * (aLongitude + 180L)) / 360L; return Math.min(x, mp); } /** * Transforms latitude to pixelspace *

* Mathematical optimization
* * log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))
* * y = -1 * (radius(aZoomlevel) / 2 * log(u)))) - falseNorthing(aZoomlevel))
* y = -1 * (getMaxPixel(aZoomlevel) / 2 * PI / 2 * log(u)) - -1 * getMaxPixel(aZoomLevel) / 2
* y = getMaxPixel(aZoomlevel) / (-4 * PI) * log(u)) + getMaxPixel(aZoomLevel) / 2
* y = getMaxPixel(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)
*
*

* @param aLat * [-90...90] * @param aZoomlevel zoom level * @return [0..2^Zoomlevel*TILE_SIZE[ */ public double latToY(double aLat, int aZoomlevel) { if (aLat < MIN_LAT) aLat = MIN_LAT; else if (aLat > MAX_LAT) aLat = MAX_LAT; double sinLat = Math.sin(Math.toRadians(aLat)); double log = Math.log((1.0 + sinLat) / (1.0 - sinLat)); int mp = getMaxPixels(aZoomlevel); double y = mp * (0.5 - (log / (4.0 * Math.PI))); return Math.min(y, mp - 1); } /** * Transforms pixel coordinate X to longitude * *

* Mathematical optimization
* * lon = toDegree((aX - falseEasting(aZoomlevel)) / radius(aZoomlevel))
* lon = 180 / PI * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel) / (2 * PI)
* lon = 180 * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel))
* lon = 360 / getMaxPixels(aZoomlevel) * (aX - getMaxPixels(aZoomlevel) / 2)
* lon = 360 * aX / getMaxPixels(aZoomlevel) - 180
*
*

* @param aX * [0..2^Zoomlevel*TILE_WIDTH[ * @param aZoomlevel zoom level * @return ]-180..180[ */ public double xToLon(int aX, int aZoomlevel) { return ((360d * aX) / getMaxPixels(aZoomlevel)) - 180.0; } /** * Transforms pixel coordinate Y to latitude * * @param aY * [0..2^Zoomlevel*TILE_WIDTH[ * @param aZoomlevel zoom level * @return [MIN_LAT..MAX_LAT] is about [-85..85] */ public double yToLat(int aY, int aZoomlevel) { aY += falseNorthing(aZoomlevel); double latitude = (Math.PI / 2) - (2 * Math.atan(Math.exp(-1.0 * aY / radius(aZoomlevel)))); return -1 * Math.toDegrees(latitude); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/AbstractLayer.java0000644000000000000000000000517713255753416025062 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.util.ArrayList; import java.util.List; public class AbstractLayer { private LayerGroup parent; private String name; private String description; private Style style; private Boolean visible; private Boolean visibleTexts = Boolean.TRUE; public AbstractLayer(String name) { this(name, (String) null); } public AbstractLayer(String name, String description) { this(name, description, MapMarkerCircle.getDefaultStyle()); } public AbstractLayer(String name, Style style) { this(name, null, style); } public AbstractLayer(String name, String description, Style style) { this(null, name, description, style); } public AbstractLayer(LayerGroup parent, String name) { this(parent, name, MapMarkerCircle.getDefaultStyle()); } public AbstractLayer(LayerGroup parent, String name, Style style) { this(parent, name, null, style); } public AbstractLayer(LayerGroup parent, String name, String description, Style style) { setParent(parent); setName(name); setDescription(description); setStyle(style); setVisible(Boolean.TRUE); if (parent != null) parent.add(this); } public LayerGroup getParent() { return parent; } public void setParent(LayerGroup parent) { this.parent = parent; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Style getStyle() { return style; } public void setStyle(Style style) { this.style = style; } public Boolean isVisible() { return visible; } public void setVisible(Boolean visible) { this.visible = visible; } public static List add(List list, E element) { if (element != null) { if (list == null) list = new ArrayList<>(); if (!list.contains(element)) list.add(element); } return list; } public Boolean isVisibleTexts() { return visibleTexts; } public void setVisibleTexts(Boolean visibleTexts) { this.visibleTexts = visibleTexts; } @Override public String toString() { return name; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/JMapViewerTree.java0000644000000000000000000001445213255753416025147 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSplitPane; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import org.openstreetmap.gui.jmapviewer.checkBoxTree.CheckBoxNodePanel; import org.openstreetmap.gui.jmapviewer.checkBoxTree.CheckBoxTree; import org.openstreetmap.gui.jmapviewer.interfaces.MapObject; /** * Tree of layers for JMapViewer component * @author galo */ public class JMapViewerTree extends JPanel { /** Serial Version UID */ private static final long serialVersionUID = 3050203054402323972L; private JMapViewer map; private CheckBoxTree tree; private JPanel treePanel; private JSplitPane splitPane; public JMapViewerTree(String name) { this(name, false); } public JMapViewerTree(String name, boolean treeVisible) { super(); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); tree = new CheckBoxTree(name); treePanel = new JPanel(new BorderLayout()); treePanel.add(tree, BorderLayout.CENTER); treePanel.add(new JLabel("
Use right mouse button to
show/hide texts
"), BorderLayout.SOUTH); map = new JMapViewer(); splitPane.setOneTouchExpandable(true); splitPane.setDividerLocation(150); //Provide minimum sizes for the two components in the split pane Dimension minimumSize = new Dimension(100, 50); //tree.setMinimumSize(minimumSize); map.setMinimumSize(minimumSize); createRefresh(); setLayout(new BorderLayout()); setTreeVisible(treeVisible); tree.addNodeListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { maybeShowPopup(e); } @Override public void mouseReleased(MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) { AbstractLayer layer = ((CheckBoxNodePanel) e.getComponent()).getData().getAbstractLayer(); if (layer != null) JMapViewerTree.this.createPopupMenu(layer).show(e.getComponent(), e.getX(), e.getY()); } } }); } private JPopupMenu createPopupMenu(final AbstractLayer layer) { JMenuItem menuItemShow = new JMenuItem("show texts"); JMenuItem menuItemHide = new JMenuItem("hide texts"); //Create the popup menu. JPopupMenu popup = new JPopupMenu(); // Create items if (layer.isVisibleTexts() == null) { popup.add(menuItemShow); popup.add(menuItemHide); } else if (layer.isVisibleTexts()) popup.add(menuItemHide); else popup.add(menuItemShow); menuItemShow.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { setVisibleTexts(layer, true); if (layer.getParent() != null) layer.getParent().calculateVisibleTexts(); map.repaint(); } }); menuItemHide.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { setVisibleTexts(layer, false); if (layer.getParent() != null) layer.getParent().calculateVisibleTexts(); map.repaint(); } }); return popup; } private static void setVisibleTexts(AbstractLayer layer, boolean visible) { layer.setVisibleTexts(visible); if (layer instanceof LayerGroup) { LayerGroup group = (LayerGroup) layer; if (group.getLayers() != null) for (AbstractLayer al: group.getLayers()) { setVisibleTexts(al, visible); } } } public Layer addLayer(String name) { Layer layer = new Layer(name); this.addLayer(layer); return layer; } public JMapViewerTree addLayer(Layer layer) { tree.addLayer(layer); return this; } public JMapViewerTree addLayer(MapObject element) { //element.getLayer().add(element); return addLayer(element.getLayer()); } public Layer removeFromLayer(MapObject element) { element.getLayer().getElements().remove(element); return element.getLayer(); } public static int size(List list) { return list == null ? 0 : list.size(); } public JMapViewer getViewer() { return map; } public CheckBoxTree getTree() { return tree; } public void addMapObject(MapObject o){ } public void setTreeVisible(boolean visible) { removeAll(); revalidate(); if (visible) { splitPane.setLeftComponent(treePanel); splitPane.setRightComponent(map); add(splitPane, BorderLayout.CENTER); } else add(map, BorderLayout.CENTER); repaint(); } private void createRefresh() { tree.getModel().addTreeModelListener(new TreeModelListener() { @Override public void treeNodesChanged(final TreeModelEvent e) { repaint(); } @Override public void treeNodesInserted(TreeModelEvent arg0) { // TODO Auto-generated method stub } @Override public void treeNodesRemoved(TreeModelEvent arg0) { // TODO Auto-generated method stub } @Override public void treeStructureChanged(TreeModelEvent arg0) { // TODO Auto-generated method stub } }); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/MemoryTileCache.java0000644000000000000000000001471213255753422025324 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import org.openstreetmap.gui.jmapviewer.interfaces.TileCache; import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; /** * {@link TileCache} implementation that stores all {@link Tile} objects in * memory up to a certain limit ({@link #getCacheSize()}). If the limit is * exceeded the least recently used {@link Tile} objects will be deleted. * * @author Jan Peter Stotz */ public class MemoryTileCache implements TileCache { protected static final Logger log = Logger.getLogger(MemoryTileCache.class.getName()); /** * Default cache size */ protected int cacheSize; protected final Map hash; /** * List of all tiles in their last recently used order */ protected final CacheLinkedListElement lruTiles; /** * Constructs a new {@code MemoryTileCache}. */ public MemoryTileCache() { this(200); } /** * Constructs a new {@code MemoryTileCache}. * @param cacheSize size of the cache */ public MemoryTileCache(int cacheSize) { this.cacheSize = cacheSize; hash = new HashMap<>(cacheSize); lruTiles = new CacheLinkedListElement(); } @Override public synchronized void addTile(Tile tile) { CacheEntry entry = createCacheEntry(tile); if (hash.put(tile.getKey(), entry) == null) { // only if hash hadn't had the element, add it to LRU lruTiles.addFirst(entry); if (hash.size() > cacheSize || lruTiles.getElementCount() > cacheSize) { removeOldEntries(); } } } @Override public synchronized Tile getTile(TileSource source, int x, int y, int z) { CacheEntry entry = hash.get(Tile.getTileKey(source, x, y, z)); if (entry == null) return null; lruTiles.moveElementToFirstPos(entry); return entry.tile; } /** * Removes the least recently used tiles */ protected synchronized void removeOldEntries() { try { while (lruTiles.getElementCount() > cacheSize) { removeEntry(lruTiles.getLastElement()); } } catch (NullPointerException e) { log.warning(e.getMessage()); } } protected synchronized void removeEntry(CacheEntry entry) { hash.remove(entry.tile.getKey()); lruTiles.removeEntry(entry); } protected CacheEntry createCacheEntry(Tile tile) { return new CacheEntry(tile); } @Override public synchronized void clear() { hash.clear(); lruTiles.clear(); } @Override public synchronized int getTileCount() { return hash.size(); } @Override public synchronized int getCacheSize() { return cacheSize; } /** * Changes the maximum number of {@link Tile} objects that this cache holds. * * @param cacheSize * new maximum number of tiles */ public synchronized void setCacheSize(int cacheSize) { this.cacheSize = cacheSize; if (hash.size() > cacheSize) removeOldEntries(); } /** * Linked list element holding the {@link Tile} and links to the * {@link #next} and {@link #prev} item in the list. */ protected static class CacheEntry { private Tile tile; private CacheEntry next; private CacheEntry prev; protected CacheEntry(Tile tile) { this.tile = tile; } @Override public String toString() { return tile.toString(); } } /** * Special implementation of a double linked list for {@link CacheEntry} * elements. It supports element removal in constant time - in difference to * the Java implementation which needs O(n). * * @author Jan Peter Stotz */ protected static class CacheLinkedListElement { protected CacheEntry firstElement; protected CacheEntry lastElement; protected int elementCount; /** * Constructs a new {@code CacheLinkedListElement}. */ public CacheLinkedListElement() { clear(); } public void clear() { elementCount = 0; firstElement = null; lastElement = null; } /** * Add the element to the head of the list. * * @param element new element to be added */ public void addFirst(CacheEntry element) { if (element == null) return; if (elementCount == 0) { firstElement = element; lastElement = element; element.prev = null; element.next = null; } else { element.next = firstElement; firstElement.prev = element; element.prev = null; firstElement = element; } elementCount++; } /** * Removes the specified element from the list. * * @param element element to be removed */ public void removeEntry(CacheEntry element) { if (element == null) return; if (element.next != null) { element.next.prev = element.prev; } if (element.prev != null) { element.prev.next = element.next; } if (element == firstElement) firstElement = element.next; if (element == lastElement) lastElement = element.prev; element.next = null; element.prev = null; elementCount--; } public void moveElementToFirstPos(CacheEntry entry) { if (firstElement == entry) return; removeEntry(entry); addFirst(entry); } public int getElementCount() { return elementCount; } public CacheEntry getLastElement() { return lastElement; } public CacheEntry getFirstElement() { return firstElement; } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/0000755000000000000000000000000013255753422024003 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java0000644000000000000000000001311213255753416027541 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.util.Map; import java.util.Set; /** * Data class that keeps basic information about a tile source. * * @since 31122 */ public class TileSourceInfo { /** id for this imagery entry, optional at the moment */ protected String id; /** URL of the imagery service */ protected String url; /** name of the imagery layer */ protected String name; /** headers meaning, that there is no tile at this zoom level */ protected Map> noTileHeaders; /** checksum of empty tiles */ protected Map> noTileChecksums; /** minimum zoom level supported by the tile source */ protected int minZoom; /** maximum zoom level supported by the tile source */ protected int maxZoom; /** cookies that needs to be sent to tile source */ protected String cookies = ""; /** tile size of the displayed tiles */ protected int tileSize = -1; /** mapping <header key, metadata key> */ protected Map metadataHeaders; /** supports "/status" and "/dirty" mode (tile re-rendering) */ protected boolean modTileFeatures; /** * Create a TileSourceInfo class * * @param name name * @param baseUrl base URL * @param id unique id */ public TileSourceInfo(String name, String baseUrl, String id) { this.name = name; this.url = baseUrl; this.id = id; } /** * Create a TileSourceInfo class * * @param name name */ public TileSourceInfo(String name) { this(name, null, null); } /** * Creates empty TileSourceInfo class */ public TileSourceInfo() { this(null, null, null); } /** * Request name of the tile source * @return name of the tile source */ public final String getName() { return name; } /** * Request URL of the tile source * @return url of the tile source */ public final String getUrl() { return url; } /** * Request ID of the tile source. Id can be null. This gets the configured id as is. * Due to a user error, this may not be unique. * @return id of the tile source */ public final String getId() { return id; } /** * Request header information for empty tiles for servers delivering such tile types * @return map of headers, that when set, means that this is "no tile at this zoom level" situation * @since 32022 */ public Map> getNoTileHeaders() { return noTileHeaders; } /** * Checkusm for empty tiles for servers delivering such tile types * @return map of checksums, that when detected, means that this is "no tile at this zoom level" situation * @since 32022 */ public Map> getNoTileChecksums() { return noTileChecksums; } /** * Request supported minimum zoom level * @return minimum zoom level supported by tile source */ public int getMinZoom() { return minZoom; } /** * Request supported maximum zoom level * @return maximum zoom level supported by tile source */ public int getMaxZoom() { return maxZoom; } /** * Request cookies to be sent together with request * @return cookies to be sent along with request to tile source */ public String getCookies() { return cookies; } /** * Request tile size of this tile source * @return tile size provided by this tile source, or -1 when default value should be used */ public int getTileSize() { return tileSize; } /** * Request metadata headers * @return mapping <HTTP header name, Metadata key name> for copying HTTP headers to Tile metadata * @since 31125 */ public Map getMetadataHeaders() { return metadataHeaders; } /** * Sets the tile size provided by this tile source * @param tileSize tile size in pixels */ public final void setTileSize(int tileSize) { if (tileSize == 0 || tileSize < -1) { throw new AssertionError("Invalid tile size: " + tileSize); } this.tileSize = tileSize; } /** * Sets the tile URL. * @param url tile URL */ public final void setUrl(String url) { this.url = url; } /** * Sets the tile name. * @param name tile name */ public final void setName(String name) { this.name = name; } /** * Sets the tile id. * @param id tile id */ public final void setId(String id) { this.id = id; } /** * Determines if this imagery supports "/status" and "/dirty" mode (tile re-rendering). * @return true if it supports "/status" and "/dirty" mode (tile re-rendering) */ public final boolean isModTileFeatures() { return modTileFeatures; } /** * Sets whether this imagery supports "/status" and "/dirty" mode (tile re-rendering). * @param modTileFeatures true if it supports "/status" and "/dirty" mode (tile re-rendering) */ public final void setModTileFeatures(boolean modTileFeatures) { this.modTileFeatures = modTileFeatures; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java0000644000000000000000000001525113255753416030075 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.awt.Point; import java.util.Random; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.OsmMercator; import org.openstreetmap.gui.jmapviewer.TileXY; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; /** * This tilesource uses different to OsmMercator projection. * * Earth is assumed an ellipsoid in this projection, unlike * sphere in OsmMercator, so latitude calculation differs a lot. * * The longitude calculation is the same as in OsmMercator, * we inherit it from AbstractTMSTileSource. * * TODO: correct getDistance() method. */ public class ScanexTileSource extends TMSTileSource { private static final String DEFAULT_URL = "http://maps.kosmosnimki.ru"; private static final int DEFAULT_MAXZOOM = 14; private static final String API_KEY = "4018C5A9AECAD8868ED5DEB2E41D09F7"; private enum ScanexLayer { IRS("irs", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=BAC78D764F0443BD9AF93E7A998C9F5B"), SPOT("spot", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=F51CE95441284AF6B2FC319B609C7DEC"); private final String name; private final String uri; ScanexLayer(String name, String uri) { this.name = name; this.uri = uri; } public String getName() { return name; } public String getUri() { return uri; } } /** IRS by default */ private ScanexLayer layer = ScanexLayer.IRS; private TemplatedTMSTileSource TemplateSource = null; /** cached latitude used in {@link #tileYToLat(double, int)} */ private double cachedLat; /** * Constructs a new {@code ScanexTileSource}. * @param info tile source info */ public ScanexTileSource(TileSourceInfo info) { super(info); String url = info.getUrl(); /** * The formulae in tileYToLat() and latToTileY() have 2^8 * hardcoded in them, so explicitly state that. For now * the assignment matches OsmMercator.DEFAUL_TILE_SIZE, and * thus is extraneous. But let it be there just in case if * OsmMercator changes. */ this.tileSize = 256; for (ScanexLayer slayer : ScanexLayer.values()) { if (url.equalsIgnoreCase(slayer.getName())) { this.layer = slayer; // Override baseUrl and maxZoom in base class. this.baseUrl = DEFAULT_URL; if (maxZoom == 0) this.maxZoom = DEFAULT_MAXZOOM; return; } } /** If not "irs" or "spot" keyword, then a custom URL. */ TemplatedTMSTileSource.checkUrl(info.getUrl()); this.TemplateSource = new TemplatedTMSTileSource(info); } @Override public String getExtension() { return "jpeg"; } @Override public String getTileUrl(int zoom, int tilex, int tiley) { if (this.TemplateSource != null) return this.TemplateSource.getTileUrl(zoom, tilex, tiley); else return this.getBaseUrl() + getTilePath(zoom, tilex, tiley); } @Override public String getTilePath(int zoom, int tilex, int tiley) { int tmp = (int) Math.pow(2.0, zoom - 1); tilex = tilex - tmp; tiley = tmp - tiley - 1; return this.layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom; } // Latitude to Y and back calculations. private static final double RADIUS_E = 6378137; /* radius of Earth at equator, m */ private static final double EQUATOR = 40075016.68557849; /* equator length, m */ private static final double E = 0.0818191908426; /* eccentricity of Earth's ellipsoid */ @Override public Point latLonToXY(double lat, double lon, int zoom) { return new Point( (int) Math.round(osmMercator.lonToX(lon, zoom)), (int) Math.round(latToTileY(lat, zoom)) ); } @Override public ICoordinate xyToLatLon(int x, int y, int zoom) { return new Coordinate( tileYToLat(y, zoom), osmMercator.xToLon(x, zoom) ); } @Override public TileXY latLonToTileXY(double lat, double lon, int zoom) { return new TileXY( osmMercator.lonToX(lon, zoom) / getTileSize(), latToTileY(lat, zoom) ); } @Override public ICoordinate tileXYToLatLon(int x, int y, int zoom) { return new Coordinate( tileYToLat(y, zoom), osmMercator.xToLon(x * getTileSize(), zoom) ); } private double latToTileY(double lat, int zoom) { double tmp = Math.tan(Math.PI/4 * (1 + lat/90)); double pow = Math.pow(Math.tan(Math.PI/4 + Math.asin(E * Math.sin(Math.toRadians(lat)))/2), E); return (EQUATOR/2 - (RADIUS_E * Math.log(tmp/pow))) * Math.pow(2.0, zoom) / EQUATOR; } /* * To solve inverse formula latitude = f(y) we use * Newton's method. We cache previous calculated latitude, * because new one is usually close to the old one. In case * if solution gets out of bounds, we reset to a new random value. */ private double tileYToLat(double y, int zoom) { double lat0; double lat = cachedLat; do { lat0 = lat; lat = lat - Math.toDegrees(nextTerm(Math.toRadians(lat), y, zoom)); if (lat > OsmMercator.MAX_LAT || lat < OsmMercator.MIN_LAT) { Random r = new Random(); lat = OsmMercator.MIN_LAT + r.nextInt((int) (OsmMercator.MAX_LAT - OsmMercator.MIN_LAT)); } } while (Math.abs(lat0 - lat) > 0.000001); cachedLat = lat; return lat; } /* Next term in Newton's polynomial */ private static double nextTerm(double lat, double y, int zoom) { double sinl = Math.sin(lat); double cosl = Math.cos(lat); zoom = (int) Math.pow(2.0, zoom - 1); double ec = Math.exp((1 - y/zoom)*Math.PI); double f = Math.tan(Math.PI/4+lat/2) - ec * Math.pow(Math.tan(Math.PI/4 + Math.asin(E * sinl)/2), E); double df = 1/(1 - sinl) - ec * E * cosl/((1 - E * sinl) * (Math.sqrt(1 - E * E * sinl * sinl))); return f/df; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java0000644000000000000000000001674613255753422031012 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.awt.Point; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.openstreetmap.gui.jmapviewer.OsmMercator; import org.openstreetmap.gui.jmapviewer.Tile; import org.openstreetmap.gui.jmapviewer.TileXY; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; /** * Class generalizing all tile based tile sources * * @author Wiktor Niesiobędzki * */ public abstract class AbstractTMSTileSource extends AbstractTileSource { protected String name; protected String baseUrl; protected String id; private final Map> noTileHeaders; private final Map> noTileChecksums; private final Map metadataHeaders; protected boolean modTileFeatures; protected int tileSize; /** * Creates an instance based on TileSource information * * @param info description of the Tile Source */ public AbstractTMSTileSource(TileSourceInfo info) { this.name = info.getName(); this.baseUrl = info.getUrl(); if (baseUrl != null && baseUrl.endsWith("/")) { baseUrl = baseUrl.substring(0, baseUrl.length()-1); } this.id = info.getUrl(); this.noTileHeaders = info.getNoTileHeaders(); this.noTileChecksums = info.getNoTileChecksums(); this.metadataHeaders = info.getMetadataHeaders(); this.modTileFeatures = info.isModTileFeatures(); this.tileSize = info.getTileSize(); } /** * @return default tile size to use, when not set in Imagery Preferences */ @Override public int getDefaultTileSize() { return OsmMercator.DEFAUL_TILE_SIZE; } @Override public String getName() { return name; } @Override public String getId() { return id; } @Override public int getMaxZoom() { return 21; } @Override public int getMinZoom() { return 0; } /** * @return image extension, used for URL creation */ public String getExtension() { return "png"; } /** * @param zoom level of the tile * @param tilex tile number in x axis * @param tiley tile number in y axis * @return String containg path part of URL of the tile * @throws IOException when subclass cannot return the tile URL */ public String getTilePath(int zoom, int tilex, int tiley) throws IOException { return "/" + zoom + "/" + tilex + "/" + tiley + "." + getExtension(); } /** * @return Base part of the URL of the tile source */ public String getBaseUrl() { return this.baseUrl; } @Override public String getTileUrl(int zoom, int tilex, int tiley) throws IOException { return this.getBaseUrl() + getTilePath(zoom, tilex, tiley); } @Override public String toString() { return getName(); } /* * Most tilesources use OsmMercator projection. */ @Override public int getTileSize() { if (tileSize <= 0) { return getDefaultTileSize(); } return tileSize; } @Override public Point latLonToXY(ICoordinate point, int zoom) { return latLonToXY(point.getLat(), point.getLon(), zoom); } @Override public ICoordinate xyToLatLon(Point point, int zoom) { return xyToLatLon(point.x, point.y, zoom); } @Override public TileXY latLonToTileXY(ICoordinate point, int zoom) { return latLonToTileXY(point.getLat(), point.getLon(), zoom); } @Override public ICoordinate tileXYToLatLon(TileXY xy, int zoom) { return tileXYToLatLon(xy.getXIndex(), xy.getYIndex(), zoom); } @Override public ICoordinate tileXYToLatLon(Tile tile) { return tileXYToLatLon(tile.getXtile(), tile.getYtile(), tile.getZoom()); } @Override public int getTileXMax(int zoom) { return getTileMax(zoom); } @Override public int getTileXMin(int zoom) { return 0; } @Override public int getTileYMax(int zoom) { return getTileMax(zoom); } @Override public int getTileYMin(int zoom) { return 0; } @Override public boolean isNoTileAtZoom(Map> headers, int statusCode, byte[] content) { if (noTileHeaders != null && headers != null) { for (Entry> searchEntry: noTileHeaders.entrySet()) { List headerVals = headers.get(searchEntry.getKey()); if (headerVals != null) { for (String headerValue: headerVals) { for (String val: searchEntry.getValue()) { if (headerValue.matches(val)) { return true; } } } } } } if (noTileChecksums != null && content != null) { for (Entry> searchEntry: noTileChecksums.entrySet()) { MessageDigest md = null; try { md = MessageDigest.getInstance(searchEntry.getKey()); } catch (NoSuchAlgorithmException e) { break; } byte[] byteDigest = md.digest(content); final int len = byteDigest.length; char[] hexChars = new char[len * 2]; for (int i = 0, j = 0; i < len; i++) { final int v = byteDigest[i]; int vn = (v & 0xf0) >> 4; hexChars[j++] = (char) (vn + (vn >= 10 ? 'a'-10 : '0')); vn = (v & 0xf); hexChars[j++] = (char) (vn + (vn >= 10 ? 'a'-10 : '0')); } for (String val: searchEntry.getValue()) { if (new String(hexChars).equalsIgnoreCase(val)) { return true; } } } } return super.isNoTileAtZoom(headers, statusCode, content); } @Override public Map getMetadata(Map> headers) { Map ret = new HashMap<>(); if (metadataHeaders != null && headers != null) { for (Entry searchEntry: metadataHeaders.entrySet()) { List headerVals = headers.get(searchEntry.getKey()); if (headerVals != null) { for (String headerValue: headerVals) { ret.put(searchEntry.getValue(), headerValue); } } } } return ret; } @Override public String getTileId(int zoom, int tilex, int tiley) { return this.baseUrl + "/" + zoom + "/" + tilex + "/" + tiley; } @Override public boolean isModTileFeatures() { return modTileFeatures; } private static int getTileMax(int zoom) { return (int) Math.pow(2.0, zoom) - 1; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java0000644000000000000000000001017413255753414027314 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.awt.Point; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.OsmMercator; import org.openstreetmap.gui.jmapviewer.Projected; import org.openstreetmap.gui.jmapviewer.Tile; import org.openstreetmap.gui.jmapviewer.TileRange; import org.openstreetmap.gui.jmapviewer.TileXY; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; import org.openstreetmap.gui.jmapviewer.interfaces.IProjected; /** * TMS tile source. */ public class TMSTileSource extends AbstractTMSTileSource { protected int maxZoom; protected int minZoom; protected OsmMercator osmMercator; /** * Constructs a new {@code TMSTileSource}. * @param info tile source information */ public TMSTileSource(TileSourceInfo info) { super(info); minZoom = info.getMinZoom(); maxZoom = info.getMaxZoom(); this.osmMercator = new OsmMercator(this.getTileSize()); } @Override public int getMinZoom() { return (minZoom == 0) ? super.getMinZoom() : minZoom; } @Override public int getMaxZoom() { return (maxZoom == 0) ? super.getMaxZoom() : maxZoom; } @Override public double getDistance(double lat1, double lon1, double lat2, double lon2) { return osmMercator.getDistance(lat1, lon1, lat2, lon2); } @Override public Point latLonToXY(double lat, double lon, int zoom) { return new Point( (int) Math.round(osmMercator.lonToX(lon, zoom)), (int) Math.round(osmMercator.latToY(lat, zoom)) ); } @Override public ICoordinate xyToLatLon(int x, int y, int zoom) { return new Coordinate( osmMercator.yToLat(y, zoom), osmMercator.xToLon(x, zoom) ); } @Override public TileXY latLonToTileXY(double lat, double lon, int zoom) { return new TileXY( osmMercator.lonToX(lon, zoom) / getTileSize(), osmMercator.latToY(lat, zoom) / getTileSize() ); } @Override public ICoordinate tileXYToLatLon(int x, int y, int zoom) { return new Coordinate( osmMercator.yToLat(y * getTileSize(), zoom), osmMercator.xToLon(x * getTileSize(), zoom) ); } @Override public IProjected tileXYtoProjected(int x, int y, int zoom) { double mercatorWidth = 2 * Math.PI * OsmMercator.EARTH_RADIUS; double f = mercatorWidth * getTileSize() / osmMercator.getMaxPixels(zoom); return new Projected(f * x - mercatorWidth / 2, -(f * y - mercatorWidth / 2)); } @Override public TileXY projectedToTileXY(IProjected p, int zoom) { double mercatorWidth = 2 * Math.PI * OsmMercator.EARTH_RADIUS; double f = mercatorWidth * getTileSize() / osmMercator.getMaxPixels(zoom); return new TileXY((p.getEast() + mercatorWidth / 2) / f, (-p.getNorth() + mercatorWidth / 2) / f); } @Override public boolean isInside(Tile inner, Tile outer) { int dz = inner.getZoom() - outer.getZoom(); if (dz < 0) return false; return outer.getXtile() == inner.getXtile() >> dz && outer.getYtile() == inner.getYtile() >> dz; } @Override public TileRange getCoveringTileRange(Tile tile, int newZoom) { if (newZoom <= tile.getZoom()) { int dz = tile.getZoom() - newZoom; TileXY xy = new TileXY(tile.getXtile() >> dz, tile.getYtile() >> dz); return new TileRange(xy, xy, newZoom); } else { int dz = newZoom - tile.getZoom(); TileXY t1 = new TileXY(tile.getXtile() << dz, tile.getYtile() << dz); TileXY t2 = new TileXY(t1.getX() + (1 << dz) - 1, t1.getY() + (1 << dz) - 1); return new TileRange(t1, t2, newZoom); } } @Override public String getServerCRS() { return "EPSG:3857"; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java0000644000000000000000000001255513255753416031163 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource; /** * Handles templated TMS Tile Source. Templated means, that some patterns within * URL gets substituted. * * Supported parameters * {zoom} - substituted with zoom level * {z} - as above * {NUMBER-zoom} - substituted with result of equation "NUMBER - zoom", * eg. {20-zoom} for zoom level 15 will result in 5 in this place * {zoom+number} - substituted with result of equation "zoom + number", * eg. {zoom+5} for zoom level 15 will result in 20. * {x} - substituted with X tile number * {y} - substituted with Y tile number * {!y} - substituted with Yahoo Y tile number * {-y} - substituted with reversed Y tile number * {switch:VAL_A,VAL_B,VAL_C,...} - substituted with one of VAL_A, VAL_B, VAL_C. Usually * used to specify many tile servers * {header:(HEADER_NAME,HEADER_VALUE)} - sets the headers to be sent to tile server */ public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTileSource { private Random rand; private String[] randomParts; private final Map headers = new HashMap<>(); // CHECKSTYLE.OFF: SingleSpaceSeparator private static final String COOKIE_HEADER = "Cookie"; private static final String PATTERN_ZOOM = "\\{(?:(\\d+)-)?z(?:oom)?([+-]\\d+)?\\}"; private static final String PATTERN_X = "\\{x\\}"; private static final String PATTERN_Y = "\\{y\\}"; private static final String PATTERN_Y_YAHOO = "\\{!y\\}"; private static final String PATTERN_NEG_Y = "\\{-y\\}"; private static final String PATTERN_SWITCH = "\\{switch:([^}]+)\\}"; private static final String PATTERN_HEADER = "\\{header\\(([^,]+),([^}]+)\\)\\}"; // CHECKSTYLE.ON: SingleSpaceSeparator private static final String[] ALL_PATTERNS = { PATTERN_HEADER, PATTERN_ZOOM, PATTERN_X, PATTERN_Y, PATTERN_Y_YAHOO, PATTERN_NEG_Y, PATTERN_SWITCH }; /** * Creates Templated TMS Tile Source based on ImageryInfo * @param info imagery info */ public TemplatedTMSTileSource(TileSourceInfo info) { super(info); String cookies = info.getCookies(); if (cookies != null && !cookies.isEmpty()) { headers.put(COOKIE_HEADER, cookies); } handleTemplate(); } private void handleTemplate() { // Capturing group pattern on switch values Matcher m = Pattern.compile(".*"+PATTERN_SWITCH+".*").matcher(baseUrl); if (m.matches()) { rand = new Random(); randomParts = m.group(1).split(","); } Pattern pattern = Pattern.compile(PATTERN_HEADER); StringBuffer output = new StringBuffer(); Matcher matcher = pattern.matcher(baseUrl); while (matcher.find()) { headers.put(matcher.group(1), matcher.group(2)); matcher.appendReplacement(output, ""); } matcher.appendTail(output); baseUrl = output.toString(); } @Override public Map getHeaders() { return headers; } @Override public String getTileUrl(int zoom, int tilex, int tiley) { int finalZoom = zoom; Matcher m = Pattern.compile(".*"+PATTERN_ZOOM+".*").matcher(this.baseUrl); if (m.matches()) { if (m.group(1) != null) { finalZoom = Integer.parseInt(m.group(1))-zoom; } if (m.group(2) != null) { String ofs = m.group(2); if (ofs.startsWith("+")) ofs = ofs.substring(1); finalZoom += Integer.parseInt(ofs); } } String r = this.baseUrl .replaceAll(PATTERN_ZOOM, Integer.toString(finalZoom)) .replaceAll(PATTERN_X, Integer.toString(tilex)) .replaceAll(PATTERN_Y, Integer.toString(tiley)) .replaceAll(PATTERN_Y_YAHOO, Integer.toString((int) Math.pow(2, zoom-1)-1-tiley)) .replaceAll(PATTERN_NEG_Y, Integer.toString((int) Math.pow(2, zoom)-1-tiley)); if (rand != null) { r = r.replaceAll(PATTERN_SWITCH, randomParts[rand.nextInt(randomParts.length)]); } return r; } /** * Checks if url is acceptable by this Tile Source * @param url URL to check */ public static void checkUrl(String url) { assert url != null && !"".equals(url) : "URL cannot be null or empty"; Matcher m = Pattern.compile("\\{[^}]*\\}").matcher(url); while (m.find()) { boolean isSupportedPattern = false; for (String pattern : ALL_PATTERNS) { if (m.group().matches(pattern)) { isSupportedPattern = true; break; } } if (!isSupportedPattern) { throw new IllegalArgumentException( m.group() + " is not a valid TMS argument. Please check this server URL:\n" + url); } } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java0000644000000000000000000003176513255753414030657 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.awt.Image; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.JMapViewer; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Tile source for the Bing Maps REST Imagery API. * @see MSDN */ public class BingAerialTileSource extends TMSTileSource { private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU"; private static volatile Future> attributions; // volatile is required for getAttribution(), see below. private static String imageUrlTemplate; private static Integer imageryZoomMax; private static String[] subdomains; private static final Pattern subdomainPattern = Pattern.compile("\\{subdomain\\}"); private static final Pattern quadkeyPattern = Pattern.compile("\\{quadkey\\}"); private static final Pattern culturePattern = Pattern.compile("\\{culture\\}"); private String brandLogoUri; /** * Constructs a new {@code BingAerialTileSource}. */ public BingAerialTileSource() { super(new TileSourceInfo("Bing", null, null)); } /** * Constructs a new {@code BingAerialTileSource}. * @param info imagery info */ public BingAerialTileSource(TileSourceInfo info) { super(info); } protected static class Attribution { private String attributionText; private int minZoom; private int maxZoom; private Coordinate min; private Coordinate max; } @Override public String getTileUrl(int zoom, int tilex, int tiley) throws IOException { // make sure that attribution is loaded. otherwise subdomains is null. if (getAttribution() == null) throw new IOException("Attribution is not loaded yet"); int t = (zoom + tilex + tiley) % subdomains.length; String subdomain = subdomains[t]; String url = imageUrlTemplate; url = subdomainPattern.matcher(url).replaceAll(subdomain); url = quadkeyPattern.matcher(url).replaceAll(computeQuadTree(zoom, tilex, tiley)); return url; } protected URL getAttributionUrl() throws MalformedURLException { return new URL("https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&output=xml&key=" + API_KEY); } protected List parseAttributionText(InputSource xml) throws IOException { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(xml); XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xpath = xPathFactory.newXPath(); imageUrlTemplate = xpath.compile("//ImageryMetadata/ImageUrl/text()").evaluate(document).replace( "http://ecn.{subdomain}.tiles.virtualearth.net/", "https://ecn.{subdomain}.tiles.virtualearth.net/"); imageUrlTemplate = culturePattern.matcher(imageUrlTemplate).replaceAll(Locale.getDefault().toString()); imageryZoomMax = Integer.valueOf(xpath.compile("//ImageryMetadata/ZoomMax/text()").evaluate(document)); NodeList subdomainTxt = (NodeList) xpath.compile("//ImageryMetadata/ImageUrlSubdomains/string/text()") .evaluate(document, XPathConstants.NODESET); subdomains = new String[subdomainTxt.getLength()]; for (int i = 0; i < subdomainTxt.getLength(); i++) { subdomains[i] = subdomainTxt.item(i).getNodeValue(); } brandLogoUri = xpath.compile("/Response/BrandLogoUri/text()").evaluate(document); XPathExpression attributionXpath = xpath.compile("Attribution/text()"); XPathExpression coverageAreaXpath = xpath.compile("CoverageArea"); XPathExpression zoomMinXpath = xpath.compile("ZoomMin/text()"); XPathExpression zoomMaxXpath = xpath.compile("ZoomMax/text()"); XPathExpression southLatXpath = xpath.compile("BoundingBox/SouthLatitude/text()"); XPathExpression westLonXpath = xpath.compile("BoundingBox/WestLongitude/text()"); XPathExpression northLatXpath = xpath.compile("BoundingBox/NorthLatitude/text()"); XPathExpression eastLonXpath = xpath.compile("BoundingBox/EastLongitude/text()"); NodeList imageryProviderNodes = (NodeList) xpath.compile("//ImageryMetadata/ImageryProvider") .evaluate(document, XPathConstants.NODESET); List attributionsList = new ArrayList<>(imageryProviderNodes.getLength()); for (int i = 0; i < imageryProviderNodes.getLength(); i++) { Node providerNode = imageryProviderNodes.item(i); String attribution = attributionXpath.evaluate(providerNode); NodeList coverageAreaNodes = (NodeList) coverageAreaXpath.evaluate(providerNode, XPathConstants.NODESET); for (int j = 0; j < coverageAreaNodes.getLength(); j++) { Node areaNode = coverageAreaNodes.item(j); Attribution attr = new Attribution(); attr.attributionText = attribution; attr.maxZoom = Integer.parseInt(zoomMaxXpath.evaluate(areaNode)); attr.minZoom = Integer.parseInt(zoomMinXpath.evaluate(areaNode)); Double southLat = Double.valueOf(southLatXpath.evaluate(areaNode)); Double northLat = Double.valueOf(northLatXpath.evaluate(areaNode)); Double westLon = Double.valueOf(westLonXpath.evaluate(areaNode)); Double eastLon = Double.valueOf(eastLonXpath.evaluate(areaNode)); attr.min = new Coordinate(southLat, westLon); attr.max = new Coordinate(northLat, eastLon); attributionsList.add(attr); } } return attributionsList; } catch (SAXException e) { System.err.println("Could not parse Bing aerials attribution metadata."); e.printStackTrace(); } catch (ParserConfigurationException | XPathExpressionException | NumberFormatException e) { e.printStackTrace(); } return null; } @Override public int getMaxZoom() { if (imageryZoomMax != null) return imageryZoomMax; else return 22; } @Override public boolean requiresAttribution() { return true; } @Override public String getAttributionLinkURL() { // Terms of Use URL to comply with Bing Terms of Use // (the requirement is that we have such a link at the bottom of the window) return "https://www.microsoft.com/maps/assets/docs/terms.aspx"; } @Override public Image getAttributionImage() { try { final InputStream imageResource = JMapViewer.class.getResourceAsStream("images/bing_maps.png"); if (imageResource != null) { return ImageIO.read(imageResource); } else { // Some Linux distributions (like Debian) will remove Bing logo from sources, so get it at runtime for (int i = 0; i < 5 && getAttribution() == null; i++) { // Makes sure attribution is loaded if (JMapViewer.debug) { System.out.println("Bing attribution attempt " + (i+1)); } } if (brandLogoUri != null && !brandLogoUri.isEmpty()) { System.out.println("Reading Bing logo from "+brandLogoUri); return ImageIO.read(new URL(brandLogoUri)); } } } catch (IOException e) { System.err.println("Error while retrieving Bing logo: "+e.getMessage()); } return null; } @Override public String getAttributionImageURL() { return "https://opengeodata.org/microsoft-imagery-details"; } @Override public String getTermsOfUseText() { return null; } @Override public String getTermsOfUseURL() { return "https://opengeodata.org/microsoft-imagery-details"; } protected Callable> getAttributionLoaderCallable() { return new Callable>() { @Override public List call() throws Exception { int waitTimeSec = 1; while (true) { try { InputSource xml = new InputSource(getAttributionUrl().openStream()); List r = parseAttributionText(xml); System.out.println("Successfully loaded Bing attribution data."); return r; } catch (IOException ex) { System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds."); Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec)); waitTimeSec *= 2; } } } }; } protected List getAttribution() { if (attributions == null) { // see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html synchronized (BingAerialTileSource.class) { if (attributions == null) { final FutureTask> loader = new FutureTask<>(getAttributionLoaderCallable()); new Thread(loader, "bing-attribution-loader").start(); attributions = loader; } } } try { return attributions.get(0, TimeUnit.MILLISECONDS); } catch (TimeoutException ex) { System.err.println("Bing: attribution data is not yet loaded."); } catch (ExecutionException ex) { throw new RuntimeException(ex.getCause()); } catch (InterruptedException ign) { System.err.println("InterruptedException: " + ign.getMessage()); } return null; } @Override public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { try { final List data = getAttribution(); if (data == null) return "Error loading Bing attribution data"; StringBuilder a = new StringBuilder(); for (Attribution attr : data) { if (zoom <= attr.maxZoom && zoom >= attr.minZoom) { if (topLeft.getLon() < attr.max.getLon() && botRight.getLon() > attr.min.getLon() && topLeft.getLat() > attr.min.getLat() && botRight.getLat() < attr.max.getLat()) { a.append(attr.attributionText); a.append(' '); } } } return a.toString(); } catch (RuntimeException e) { e.printStackTrace(); } return "Error loading Bing attribution data"; } private static String computeQuadTree(int zoom, int tilex, int tiley) { StringBuilder k = new StringBuilder(); for (int i = zoom; i > 0; i--) { char digit = 48; int mask = 1 << (i - 1); if ((tilex & mask) != 0) { digit += (char) 1; } if ((tiley & mask) != 0) { digit += (char) 2; } k.append(digit); } return k.toString(); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/OsmTileSource.java0000644000000000000000000000702513255753416027412 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.io.IOException; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; /** * OSM Tile source. */ public class OsmTileSource { /** * The default "Mapnik" OSM tile source. */ public static class Mapnik extends AbstractOsmTileSource { private static final String PATTERN = "https://%s.tile.openstreetmap.org"; private static final String[] SERVER = {"a", "b", "c"}; private int serverNum; /** * Constructs a new {@code "Mapnik"} tile source. */ public Mapnik() { super("Mapnik", PATTERN, "MAPNIK"); modTileFeatures = true; } @Override public String getBaseUrl() { String url = String.format(this.baseUrl, new Object[] {SERVER[serverNum]}); serverNum = (serverNum + 1) % SERVER.length; return url; } } /** * The "Cycle Map" OSM tile source. */ public static class CycleMap extends AbstractOsmTileSource { private static final String PATTERN = "http://%s.tile.opencyclemap.org/cycle"; private static final String[] SERVER = {"a", "b", "c"}; private int serverNum; /** * Constructs a new {@code CycleMap} tile source. */ public CycleMap() { super("Cyclemap", PATTERN, "opencyclemap"); } @Override public String getBaseUrl() { String url = String.format(this.baseUrl, new Object[] {SERVER[serverNum]}); serverNum = (serverNum + 1) % SERVER.length; return url; } @Override public int getMaxZoom() { return 18; } } /** * The "Transport Map" OSM tile source. * * Template for thunderforest.com. */ public abstract static class TransportMap extends AbstractOsmTileSource { private static final String PATTERN = "https://%s.tile.thunderforest.com/transport"; private static final String[] SERVER = {"a", "b", "c"}; private int serverNum; /** * Constructs a new {@code TransportMap} tile source. */ public TransportMap() { super("OSM Transport Map", PATTERN, "osmtransportmap"); } /** * Get the thunderforest API key. * * Needs to be registered at their web site. * @return the API key */ protected abstract String getApiKey(); @Override public String getBaseUrl() { String url = String.format(this.baseUrl, new Object[] {SERVER[serverNum]}); serverNum = (serverNum + 1) % SERVER.length; return url; } @Override public int getMaxZoom() { return 18; } @Override public String getTileUrl(int zoom, int tilex, int tiley) throws IOException { return this.getBaseUrl() + getTilePath(zoom, tilex, tiley) + "?apikey=" + getApiKey(); } @Override public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { return "Maps © Thunderforest, Data © OpenStreetMap contributors"; } @Override public String getAttributionLinkURL() { return "http://www.thunderforest.com/"; } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java0000644000000000000000000000473613255753416030425 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.awt.Image; import java.util.List; import java.util.Map; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; public abstract class AbstractTileSource implements TileSource { protected String attributionText; protected String attributionLinkURL; protected Image attributionImage; protected String attributionImageURL; protected String termsOfUseText; protected String termsOfUseURL; @Override public boolean requiresAttribution() { return attributionText != null || attributionLinkURL != null || attributionImage != null || termsOfUseText != null || termsOfUseURL != null; } @Override public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { return attributionText; } @Override public String getAttributionLinkURL() { return attributionLinkURL; } @Override public Image getAttributionImage() { return attributionImage; } @Override public String getAttributionImageURL() { return attributionImageURL; } @Override public String getTermsOfUseText() { return termsOfUseText; } @Override public String getTermsOfUseURL() { return termsOfUseURL; } public void setAttributionText(String attributionText) { this.attributionText = attributionText; } public void setAttributionLinkURL(String attributionLinkURL) { this.attributionLinkURL = attributionLinkURL; } public void setAttributionImage(Image attributionImage) { this.attributionImage = attributionImage; } public void setAttributionImageURL(String attributionImageURL) { this.attributionImageURL = attributionImageURL; } public void setTermsOfUseText(String termsOfUseText) { this.termsOfUseText = termsOfUseText; } public void setTermsOfUseURL(String termsOfUseURL) { this.termsOfUseURL = termsOfUseURL; } @Override public boolean isNoTileAtZoom(Map> headers, int statusCode, byte[] content) { // default handler - when HTTP 404 is returned, then treat this situation as no tile at this zoom level return statusCode == 404; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java0000644000000000000000000000346113255753422031073 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.tilesources; import java.awt.Image; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; /** * Abstract class for OSM Tile sources */ public abstract class AbstractOsmTileSource extends TMSTileSource { /** * The OSM attribution. Must be always in line with * https://www.openstreetmap.org/copyright/en */ public static final String DEFAULT_OSM_ATTRIBUTION = "\u00a9 OpenStreetMap contributors"; /** * Constructs a new OSM tile source * @param name Source name as displayed in GUI * @param baseUrl Source URL * @param id unique id for the tile source; contains only characters that * are safe for file names; can be null */ public AbstractOsmTileSource(String name, String baseUrl, String id) { super(new TileSourceInfo(name, baseUrl, id)); } @Override public int getMaxZoom() { return 19; } @Override public boolean requiresAttribution() { return true; } @Override public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { return DEFAULT_OSM_ATTRIBUTION; } @Override public String getAttributionLinkURL() { return "https://openstreetmap.org/"; } @Override public Image getAttributionImage() { return null; } @Override public String getAttributionImageURL() { return null; } @Override public String getTermsOfUseText() { return null; } @Override public String getTermsOfUseURL() { return "https://www.openstreetmap.org/copyright"; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/FeatureAdapter.java0000644000000000000000000000553413255753416025213 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.Desktop; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.logging.Logger; public final class FeatureAdapter { private static BrowserAdapter browserAdapter = new DefaultBrowserAdapter(); private static TranslationAdapter translationAdapter = new DefaultTranslationAdapter(); private static LoggingAdapter loggingAdapter = new DefaultLoggingAdapter(); private FeatureAdapter() { // private constructor for utility classes } public interface BrowserAdapter { void openLink(String url); } public interface TranslationAdapter { String tr(String text, Object... objects); // TODO: more i18n functions } public interface LoggingAdapter { Logger getLogger(String name); } public static void registerBrowserAdapter(BrowserAdapter browserAdapter) { FeatureAdapter.browserAdapter = browserAdapter; } public static void registerTranslationAdapter(TranslationAdapter translationAdapter) { FeatureAdapter.translationAdapter = translationAdapter; } public static void registerLoggingAdapter(LoggingAdapter loggingAdapter) { FeatureAdapter.loggingAdapter = loggingAdapter; } public static void openLink(String url) { browserAdapter.openLink(url); } public static String tr(String text, Object... objects) { return translationAdapter.tr(text, objects); } public static Logger getLogger(String name) { return loggingAdapter.getLogger(name); } public static class DefaultBrowserAdapter implements BrowserAdapter { @Override public void openLink(String url) { if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { try { Desktop.getDesktop().browse(new URI(url)); } catch (IOException e) { e.printStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); } } else { System.err.println(tr("Opening link not supported on current platform (''{0}'')", url)); } } } public static class DefaultTranslationAdapter implements TranslationAdapter { @Override public String tr(String text, Object... objects) { return MessageFormat.format(text, objects); } } public static class DefaultLoggingAdapter implements LoggingAdapter { @Override public Logger getLogger(String name) { return Logger.getLogger(name); } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/MapRectangleImpl.java0000644000000000000000000000522213255753416025475 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Stroke; import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle; public class MapRectangleImpl extends MapObjectImpl implements MapRectangle { private Coordinate topLeft; private Coordinate bottomRight; public MapRectangleImpl(Coordinate topLeft, Coordinate bottomRight) { this(null, null, topLeft, bottomRight); } public MapRectangleImpl(String name, Coordinate topLeft, Coordinate bottomRight) { this(null, name, topLeft, bottomRight); } public MapRectangleImpl(Layer layer, Coordinate topLeft, Coordinate bottomRight) { this(layer, null, topLeft, bottomRight); } public MapRectangleImpl(Layer layer, String name, Coordinate topLeft, Coordinate bottomRight) { this(layer, name, topLeft, bottomRight, getDefaultStyle()); } public MapRectangleImpl(Layer layer, String name, Coordinate topLeft, Coordinate bottomRight, Style style) { super(layer, name, style); this.topLeft = topLeft; this.bottomRight = bottomRight; } @Override public Coordinate getTopLeft() { return topLeft; } @Override public Coordinate getBottomRight() { return bottomRight; } @Override public void paint(Graphics g, Point topLeft, Point bottomRight) { // Prepare graphics Color oldColor = g.getColor(); g.setColor(getColor()); Stroke oldStroke = null; if (g instanceof Graphics2D) { Graphics2D g2 = (Graphics2D) g; oldStroke = g2.getStroke(); g2.setStroke(getStroke()); } // Draw g.drawRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y); // Restore graphics g.setColor(oldColor); if (g instanceof Graphics2D) { ((Graphics2D) g).setStroke(oldStroke); } int width = bottomRight.x-topLeft.x; int height = bottomRight.y-topLeft.y; Point p = new Point(topLeft.x+(width/2), topLeft.y+(height/2)); if (getLayer() == null || getLayer().isVisibleTexts()) paintText(g, p); } public static Style getDefaultStyle() { return new Style(Color.BLUE, null, new BasicStroke(2), getDefaultFont()); } @Override public String toString() { return "MapRectangle from " + getTopLeft() + " to " + getBottomRight(); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/0000755000000000000000000000000013255753422023565 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/CachedTileLoader.java0000644000000000000000000000044013255753416027545 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; /** * Interface that allow cleaning the tile cache without specifying exact type of loader */ public interface CachedTileLoader { void clearCache(TileSource source); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/ICoordinate.java0000644000000000000000000000037713255753422026637 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; public interface ICoordinate { double getLat(); void setLat(double lat); double getLon(); void setLon(double lon); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/MapObject.java0000644000000000000000000000111413255753416026274 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.awt.Color; import java.awt.Font; import java.awt.Stroke; import org.openstreetmap.gui.jmapviewer.Layer; import org.openstreetmap.gui.jmapviewer.Style; public interface MapObject { Layer getLayer(); void setLayer(Layer layer); Style getStyle(); Style getStyleAssigned(); Color getColor(); Color getBackColor(); Stroke getStroke(); Font getFont(); String getName(); boolean isVisible(); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/MapMarker.java0000644000000000000000000000263713255753422026317 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.awt.Graphics; import java.awt.Point; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.JMapViewer; /** * Interface to be implemented by all one dimensional elements that can be displayed on the map. * * @author Jan Peter Stotz * @see JMapViewer#addMapMarker(MapMarker) * @see JMapViewer#getMapMarkerList() */ public interface MapMarker extends MapObject, ICoordinate { enum STYLE { FIXED, VARIABLE } /** * @return Latitude and Longitude of the map marker position */ Coordinate getCoordinate(); /** * @return Latitude of the map marker position */ @Override double getLat(); /** * @return Longitude of the map marker position */ @Override double getLon(); /** * @return Radius of the map marker position */ double getRadius(); /** * @return Style of the map marker */ STYLE getMarkerStyle(); /** * Paints the map marker on the map. The position specifies the * coordinates within g * * @param g graphics * @param position coordinates * @param radius radius */ void paint(Graphics g, Point position, int radius); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/JMapViewerEventListener.java0000644000000000000000000000072013255753416031153 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.util.EventListener; import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent; /** * Must be implemented for processing commands while user * interacts with map viewer. * * @author Jason Huntley * */ public interface JMapViewerEventListener extends EventListener { void processCommand(JMVCommandEvent command); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoaderListener.java0000644000000000000000000000117613255753416030172 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import org.openstreetmap.gui.jmapviewer.Tile; /** * This listener listens to successful tile loads. */ @FunctionalInterface public interface TileLoaderListener { /** * Will be called if a new {@link Tile} has been loaded successfully. * Loaded can mean downloaded or loaded from file cache. * * @param tile The tile * @param success {@code true} if the tile has been loaded successfully, {@code false} otherwise */ void tileLoadingFinished(Tile tile, boolean success); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TileClearController.java0000644000000000000000000000052713255753422030344 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.io.File; public interface TileClearController { void initClearDir(File dir); void initClearFiles(File[] files); boolean cancel(); void fileDeleted(File file); void clearFinished(); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/Attributed.java0000644000000000000000000000313213255753416026541 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.awt.Image; public interface Attributed { /** * @return True if the tile source requires attribution in text or image form. */ boolean requiresAttribution(); /** * @param zoom The optional zoom level for the view. * @param botRight The bottom right of the bounding box for attribution. * @param topLeft The top left of the bounding box for attribution. * @return Attribution text for the image source. */ String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight); /** * @return The URL to open when the user clicks the attribution text. */ String getAttributionLinkURL(); /** * @return The URL for the attribution image. Null if no image should be displayed. */ Image getAttributionImage(); /** * @return The URL to open when the user clicks the attribution image. * When return value is null, the image is still displayed (provided getAttributionImage() * returns a value other than null), but the image does not link to a website. */ String getAttributionImageURL(); /** * @return The attribution "Terms of Use" text. * In case it returns null, but getTermsOfUseURL() is not null, a default * terms of use text is used. */ String getTermsOfUseText(); /** * @return The URL to open when the user clicks the attribution "Terms of Use" text. */ String getTermsOfUseURL(); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/IProjected.java0000644000000000000000000000076613255753416026474 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; /** * Projected coordinates (east / north space). * * For most projections, one unit in projected space is roughly one meter, but * can also be degrees or feet. */ public interface IProjected { /** * Returns easting. * @return easting */ double getEast(); /** * Returns northing. * @return northing */ double getNorth(); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TileCache.java0000644000000000000000000000310413255753416026252 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import org.openstreetmap.gui.jmapviewer.JMapViewer; import org.openstreetmap.gui.jmapviewer.Tile; /** * Implement this interface for creating your custom tile cache for * {@link JMapViewer}. * * @author Jan Peter Stotz */ public interface TileCache { /** * Retrieves a tile from the cache if present, otherwise null * will be returned. * * @param source * the tile source * @param x * tile number on the x axis of the tile to be retrieved * @param y * tile number on the y axis of the tile to be retrieved * @param z * zoom level of the tile to be retrieved * @return the requested tile or null if the tile is not * present in the cache */ Tile getTile(TileSource source, int x, int y, int z); /** * Adds a tile to the cache. How long after adding a tile can be retrieved * via {@link #getTile(TileSource, int, int, int)} is unspecified and depends on the * implementation. * * @param tile the tile to be added */ void addTile(Tile tile); /** * @return the number of tiles hold by the cache */ int getTileCount(); /** * Clears the cache deleting all tiles from memory. */ void clear(); /** * Size of the cache. * @return maximum number of tiles in cache */ int getCacheSize(); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoader.java0000644000000000000000000000271613255753416026465 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import org.openstreetmap.gui.jmapviewer.Tile; /** * Interface for implementing a tile loader. Tiles are usually loaded via HTTP * or from a file. * * @author Jan Peter Stotz */ public interface TileLoader { /** * A typical implementation of this function should create and return a * new {@link TileJob} instance that performs the load action. * * @param tile the tile to be loaded * @return {@link TileJob} implementation that performs the desired load * action. */ TileJob createTileLoaderJob(Tile tile); /** * cancels all outstanding tasks in the queue. This should rollback the state of the tiles in the queue * to loading = false / loaded = false */ void cancelOutstandingTasks(); /** * Determines whether this {@link TileLoader} has tasks which have not completed. * @return whether this {@link TileLoader} has tasks which have not completed. This answer may well be * "approximate" given that many implementations will be using mechanisms where a queue's state can change * during the computation. */ default boolean hasOutstandingTasks() { // default implementation supplied just to make transition easier for external implementors throw new UnsupportedOperationException("Not implemented"); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java0000644000000000000000000002157013255753416026516 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.awt.Point; import java.io.IOException; import java.util.List; import java.util.Map; import org.openstreetmap.gui.jmapviewer.JMapViewer; import org.openstreetmap.gui.jmapviewer.Tile; import org.openstreetmap.gui.jmapviewer.TileRange; import org.openstreetmap.gui.jmapviewer.TileXY; /** * * @author Jan Peter Stotz */ public interface TileSource extends Attributed { /** * Specifies the maximum zoom value. The number of zoom levels is [0.. * {@link #getMaxZoom()}]. * * @return maximum zoom value that has to be smaller or equal to * {@link JMapViewer#MAX_ZOOM} */ int getMaxZoom(); /** * Specifies the minimum zoom value. This value is usually 0. * Only for maps that cover a certain region up to a limited zoom level * this method should return a value different than 0. * * @return minimum zoom value - usually 0 */ int getMinZoom(); /** * A tile layer name as displayed to the user. * * @return Name of the tile layer */ String getName(); /** * A unique id for this tile source. * * Unlike the name it has to be unique and has to consist only of characters * valid for filenames. * * @return the id */ String getId(); /** * Constructs the tile url. * * @param zoom zoom level * @param tilex X coordinate * @param tiley Y coordinate * @return fully qualified url for downloading the specified tile image * @throws IOException if any I/O error occurs */ String getTileUrl(int zoom, int tilex, int tiley) throws IOException; /** * Creates tile identifier that is unique among all tile sources, but the same tile will always * get the same identifier. Used for creation of cache key. * * @param zoom zoom level * @param tilex X coordinate * @param tiley Y coordinate * @return tile identifier */ String getTileId(int zoom, int tilex, int tiley); /** * Specifies how large each tile is. * @return The size of a single tile in pixels. -1 if default size should be used */ int getTileSize(); /** * @return default tile size, for this tile source * TODO: @since */ int getDefaultTileSize(); /** * Gets the distance using Spherical law of cosines. * @param la1 latitude of first point * @param lo1 longitude of first point * @param la2 latitude of second point * @param lo2 longitude of second point * @return the distance betwen first and second point, in m. */ double getDistance(double la1, double lo1, double la2, double lo2); /** * Transforms longitude and latitude to pixel space (as if all tiles at specified zoom level where joined). * @param lon longitude * @param lat latitude * @param zoom zoom level * @return the pixel coordinates */ Point latLonToXY(double lat, double lon, int zoom); /** * Transforms longitude and latitude to pixel space (as if all tiles at specified zoom level where joined). * @param point point * @param zoom zoom level * @return the pixel coordinates */ Point latLonToXY(ICoordinate point, int zoom); /** * Transforms a point in pixel space to longitude/latitude (WGS84). * @param point point * @param zoom zoom level * @return WGS84 Coordinates of given point */ ICoordinate xyToLatLon(Point point, int zoom); /** * Transforms a point in pixel space to longitude/latitude (WGS84). * @param x X coordinate * @param y Y coordinate * @param zoom zoom level * @return WGS84 Coordinates of given point */ ICoordinate xyToLatLon(int x, int y, int zoom); /** * Transforms longitude and latitude to tile indices. * @param lon longitude * @param lat latitude * @param zoom zoom level * @return x and y tile indices */ TileXY latLonToTileXY(double lat, double lon, int zoom); /** * Transforms longitude and latitude to tile indices. * @param point point * @param zoom zoom level * @return x and y tile indices */ TileXY latLonToTileXY(ICoordinate point, int zoom); /** * Transforms tile indices to longitude and latitude. * @param xy X/Y tile indices * @param zoom zoom level * @return WGS84 coordinates of given tile */ ICoordinate tileXYToLatLon(TileXY xy, int zoom); /** * Determines to longitude and latitude of a tile. * (Refers to the tile origin - upper left tile corner) * @param tile Tile * @return WGS84 coordinates of given tile */ ICoordinate tileXYToLatLon(Tile tile); /** * Transforms tile indices to longitude and latitude. * @param x x tile index * @param y y tile index * @param zoom zoom level * @return WGS84 coordinates of given tile */ ICoordinate tileXYToLatLon(int x, int y, int zoom); /** * Get maximum x index of tile for specified zoom level. * @param zoom zoom level * @return maximum x index of tile for specified zoom level */ int getTileXMax(int zoom); /** * Get minimum x index of tile for specified zoom level. * @param zoom zoom level * @return minimum x index of tile for specified zoom level */ int getTileXMin(int zoom); /** * Get maximum y index of tile for specified zoom level. * @param zoom zoom level * @return maximum y index of tile for specified zoom level */ int getTileYMax(int zoom); /** * Get minimum y index of tile for specified zoom level * @param zoom zoom level * @return minimum y index of tile for specified zoom level */ int getTileYMin(int zoom); /** * Determines, if the returned data from TileSource represent "no tile at this zoom level" situation. Detection * algorithms differ per TileSource, so each TileSource should implement each own specific way. * * @param headers HTTP headers from response from TileSource server * @param statusCode HTTP status code * @param content byte array representing the data returned from the server * @return true, if "no tile at this zoom level" situation detected */ boolean isNoTileAtZoom(Map> headers, int statusCode, byte[] content); /** * Extracts metadata about the tile based on HTTP headers * * @param headers HTTP headers from Tile Source server * @return tile metadata */ Map getMetadata(Map> headers); /** * Convert tile indices (x/y/zoom) into projected coordinates of the tile origin. * @param x x tile index * @param y z tile index * @param zoom zoom level * @return projected coordinates of the tile origin */ IProjected tileXYtoProjected(int x, int y, int zoom); /** * Convert projected coordinates to tile indices. * @param p projected coordinates * @param zoom zoom level * @return corresponding tile index x/y (floating point, truncate to integer * for tile index) */ TileXY projectedToTileXY(IProjected p, int zoom); /** * Check if one tile is inside another tile. * @param inner the tile that is suspected to be inside the other tile * @param outer the tile that might contain the first tile * @return true if first tile is inside second tile (or both are identical), * false otherwise */ boolean isInside(Tile inner, Tile outer); /** * Returns a range of tiles, that cover a given tile, which is * usually at a different zoom level. * * In standard tile layout, 4 tiles cover a tile one zoom lower, 16 tiles * cover a tile 2 zoom levels below etc. * If the zoom level of the covering tiles is greater or equal, a single * tile suffices. * * @param tile the tile to cover * @param newZoom zoom level of the covering tiles * @return TileRange of all covering tiles at zoom newZoom */ TileRange getCoveringTileRange(Tile tile, int newZoom); /** * Get coordinate reference system for this tile source. * * E.g. "EPSG:3857" for Google-Mercator. * @return code for the coordinate reference system in use */ String getServerCRS(); /** * Determines if this imagery supports "/dirty" mode (tile re-rendering). * @return true if it supports "/dirty" mode (tile re-rendering) */ default boolean isModTileFeatures() { return false; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TileJob.java0000644000000000000000000000106213255753416025762 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; /** * Interface for implementing a tile loading job. Tiles are usually loaded via HTTP * or from a file. * * @author Dirk Stöcker */ public interface TileJob extends Runnable { /** * submits download job to backend. */ void submit(); /** * submits download job to backend. * @param force true if the load should skip all the caches (local & remote) */ void submit(boolean force); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/TemplatedTileSource.java0000644000000000000000000000065413255753416030356 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.util.Map; /** * Interface for template tile sources, @see TemplatedTMSTileSource * * @author Wiktor Niesiobędzki * @since 1.10 */ public interface TemplatedTileSource extends TileSource { /** * * @return headers to be sent with http requests */ Map getHeaders(); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/MapRectangle.java0000644000000000000000000000223713255753416027001 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.awt.Graphics; import java.awt.Point; import org.openstreetmap.gui.jmapviewer.Coordinate; import org.openstreetmap.gui.jmapviewer.JMapViewer; /** * Interface to be implemented by rectangles that can be displayed on the map. * * @author Stefan Zeller * @see JMapViewer#addMapRectangle(MapRectangle) * @see JMapViewer#getMapRectangleList() */ public interface MapRectangle extends MapObject { /** * @return Latitude/Longitude of top left of rectangle */ Coordinate getTopLeft(); /** * @return Latitude/Longitude of bottom right of rectangle */ Coordinate getBottomRight(); /** * Paints the map rectangle on the map. The topLeft and * bottomRight are specifying the coordinates within g * * @param g graphics structure for painting * @param topLeft lop left edge of painting region * @param bottomRight bottom right edge of painting region */ void paint(Graphics g, Point topLeft, Point bottomRight); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/interfaces/MapPolygon.java0000644000000000000000000000206013255753422026513 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.interfaces; import java.awt.Graphics; import java.awt.Point; import java.awt.Polygon; import java.util.List; /** * Interface to be implemented by polygons that can be displayed on the map. * * @author Vincent Privat */ public interface MapPolygon extends MapObject { /** * @return Latitude/Longitude of each point of polygon */ List getPoints(); /** * Paints the map polygon on the map. The points * are specifying the coordinates within g * * @param g graphics * @param points list of points defining the polygon to draw */ void paint(Graphics g, List points); /** * Paints the map polygon on the map. The polygon * is specifying the coordinates within g * * @param g graphics * @param polygon polygon to draw */ void paint(Graphics g, Polygon polygon); } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/MapPolygonImpl.java0000644000000000000000000000700613255753422025217 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Stroke; import java.util.Arrays; import java.util.List; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; public class MapPolygonImpl extends MapObjectImpl implements MapPolygon { private List points; public MapPolygonImpl(ICoordinate... points) { this(null, null, points); } public MapPolygonImpl(List points) { this(null, null, points); } public MapPolygonImpl(String name, List points) { this(null, name, points); } public MapPolygonImpl(String name, ICoordinate... points) { this(null, name, points); } public MapPolygonImpl(Layer layer, List points) { this(layer, null, points); } public MapPolygonImpl(Layer layer, String name, List points) { this(layer, name, points, getDefaultStyle()); } public MapPolygonImpl(Layer layer, String name, ICoordinate... points) { this(layer, name, Arrays.asList(points), getDefaultStyle()); } public MapPolygonImpl(Layer layer, String name, List points, Style style) { super(layer, name, style); this.points = points; } @Override public List getPoints() { return this.points; } @Override public void paint(Graphics g, List points) { Polygon polygon = new Polygon(); for (Point p : points) { polygon.addPoint(p.x, p.y); } paint(g, polygon); } @Override public void paint(Graphics g, Polygon polygon) { // Prepare graphics Color oldColor = g.getColor(); g.setColor(getColor()); Stroke oldStroke = null; if (g instanceof Graphics2D) { Graphics2D g2 = (Graphics2D) g; oldStroke = g2.getStroke(); g2.setStroke(getStroke()); } // Draw g.drawPolygon(polygon); if (g instanceof Graphics2D && getBackColor() != null) { Graphics2D g2 = (Graphics2D) g; Composite oldComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); g2.setPaint(getBackColor()); g2.fillPolygon(polygon); g2.setComposite(oldComposite); } // Restore graphics g.setColor(oldColor); if (g instanceof Graphics2D) { ((Graphics2D) g).setStroke(oldStroke); } Rectangle rec = polygon.getBounds(); Point corner = rec.getLocation(); Point p = new Point(corner.x+(rec.width/2), corner.y+(rec.height/2)); if (getLayer() == null || getLayer().isVisibleTexts()) paintText(g, p); } public static Style getDefaultStyle() { return new Style(Color.BLUE, new Color(100, 100, 100, 50), new BasicStroke(2), getDefaultFont()); } @Override public String toString() { return "MapPolygon [points=" + points + ']'; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/checkBoxTree/0000755000000000000000000000000013255753422024010 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeRenderer.java0000644000000000000000000001111313255753416030636 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.checkBoxTree; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.event.MouseAdapter; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeCellRenderer; /** * Renderer for checkBox Tree * * @author galo */ public class CheckBoxNodeRenderer implements TreeCellRenderer { private final CheckBoxNodePanel panel = new CheckBoxNodePanel(); private final DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer(); private final Color selectionForeground; private final Color selectionBackground; private final Color textForeground; private final Color textBackground; /** * Constructs a new {@code CheckBoxNodeRenderer}. */ public CheckBoxNodeRenderer() { final Font fontValue = UIManager.getFont("Tree.font"); if (fontValue != null) panel.getLabel().setFont(fontValue); final Boolean focusPainted = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon"); panel.check.setFocusPainted(focusPainted != null && focusPainted); selectionForeground = UIManager.getColor("Tree.selectionForeground"); selectionBackground = UIManager.getColor("Tree.selectionBackground"); textForeground = UIManager.getColor("Tree.textForeground"); textBackground = UIManager.getColor("Tree.textBackground"); } protected CheckBoxNodePanel getPanel() { return panel; } public void addNodeListener(MouseAdapter listener) { panel.addMouseListener(listener); } // -- TreeCellRenderer methods -- @Override public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean selected, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { CheckBoxNodeData data = null; if (value instanceof DefaultMutableTreeNode) { final DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; final Object userObject = node.getUserObject(); if (userObject instanceof CheckBoxNodeData) { data = (CheckBoxNodeData) userObject; } } //final String stringValue = // tree.convertValueToText(value, selected, expanded, leaf, row, false); //panel.label.setText(stringValue); panel.setSelected(Boolean.FALSE); panel.setEnabled(tree.isEnabled()); if (selected) { panel.setForeground(selectionForeground); panel.setBackground(selectionBackground); panel.getLabel().setForeground(selectionForeground); panel.getLabel().setBackground(selectionBackground); } else { panel.setForeground(textForeground); panel.setBackground(textBackground); panel.getLabel().setForeground(textForeground); panel.getLabel().setBackground(textBackground); } if (data == null) { // not a check box node; return default cell renderer return defaultRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); } //panel.label.setText(data.getText()); panel.setData(data); panel.setSelected(data.isSelected()); return panel; } /* private JPopupMenu createPopupMenu(final AbstractLayer layer) { JMenuItem menuItem; //Create the popup menu. if (layer.isVisibleTexts()) menuItem = new JMenuItem("hide texts"); else menuItem = new JMenuItem("show texts"); JPopupMenu popup = new JPopupMenu(); popup.add(menuItem); menuItem.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent arg0) { setVisibleTexts(layer, !layer.isVisibleTexts()); } }); return popup; } private void setVisibleTexts(AbstractLayer layer, boolean visible) { layer.setVisibleTexts(visible); if (layer instanceof LayerGroup) { LayerGroup group = (LayerGroup) layer; if (group.getLayers() != null) for (AbstractLayer al : group.getLayers()) { setVisibleTexts(al, visible); } } } */ } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeData.java0000644000000000000000000000237513255753416027753 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.checkBoxTree; import org.openstreetmap.gui.jmapviewer.AbstractLayer; import org.openstreetmap.gui.jmapviewer.LayerGroup; /** * Node Data for checkBox Tree * * @author galo */ public class CheckBoxNodeData { private AbstractLayer layer; public CheckBoxNodeData(final AbstractLayer layer) { this.layer = layer; } public CheckBoxNodeData(final String txt) { this(new LayerGroup(txt)); } public CheckBoxNodeData(final String txt, final Boolean selected) { this(new LayerGroup(txt)); layer.setVisible(selected); } public Boolean isSelected() { return layer.isVisible(); } public void setSelected(final Boolean newValue) { layer.setVisible(newValue); } public String getText() { return layer.getName(); } public AbstractLayer getAbstractLayer() { return layer; } public void setAbstractLayer(final AbstractLayer layer) { this.layer = layer; } @Override public String toString() { return getClass().getSimpleName() + '[' + getText() + '/' + isSelected() + ']'; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxTree.java0000644000000000000000000002321113255753422027160 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.checkBoxTree; import java.awt.BorderLayout; import java.awt.event.MouseAdapter; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import org.openstreetmap.gui.jmapviewer.AbstractLayer; import org.openstreetmap.gui.jmapviewer.Layer; import org.openstreetmap.gui.jmapviewer.LayerGroup; /** * JTree for checkBox Tree Layers * * @author galo */ public class CheckBoxTree extends JTree { /** Serial Version UID */ private static final long serialVersionUID = 6943401106938034256L; private final CheckBoxNodeEditor editor; public CheckBoxTree(AbstractLayer layer) { this(new CheckBoxNodeData(layer)); } public CheckBoxTree(String rootName) { this(new CheckBoxNodeData(rootName)); } public CheckBoxTree(CheckBoxNodeData root) { this(new DefaultMutableTreeNode(root)); } public CheckBoxTree(DefaultMutableTreeNode node) { super(new DefaultTreeModel(node)); final CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer(); setCellRenderer(renderer); editor = new CheckBoxNodeEditor(this); setCellEditor(editor); setEditable(true); // listen for changes in the model (including check box toggles) getModel().addTreeModelListener(new TreeModelListener() { @Override public void treeNodesChanged(final TreeModelEvent e) { DefaultTreeModel model = (DefaultTreeModel) e.getSource(); Object[] nodes = e.getChildren(); DefaultMutableTreeNode node; if (nodes == null || nodes.length == 0) { node = node(model.getRoot()); } else { node = node(nodes[0]); } nodeChanged(node); repaint(); } @Override public void treeNodesInserted(final TreeModelEvent e) { //System.out.println("nodes inserted"); } @Override public void treeNodesRemoved(final TreeModelEvent e) { //System.out.println("nodes removed"); } @Override public void treeStructureChanged(final TreeModelEvent e) { //System.out.println("structure changed"); } }); } public void addNodeListener(MouseAdapter listener) { editor.addNodeListener(listener); } public static void main(final String[] args) { final DefaultMutableTreeNode root = new DefaultMutableTreeNode(new CheckBoxNodeData("Root", Boolean.TRUE)); final DefaultMutableTreeNode accessibility = add(root, "Accessibility", true); add(accessibility, "Move system caret with focus/selection changes", false); add(accessibility, "Always expand alt text for images", true); root.add(accessibility); final DefaultMutableTreeNode browsing = new DefaultMutableTreeNode(new CheckBoxNodeData("Browsing", null)); add(browsing, "Notify when downloads complete", true); add(browsing, "Disable script debugging", true); add(browsing, "Use AutoComplete", true); add(browsing, "Browse in a new process", false); root.add(browsing); final CheckBoxTree tree = new CheckBoxTree(root); ((DefaultMutableTreeNode) tree.getModel().getRoot()).add(new DefaultMutableTreeNode(new CheckBoxNodeData("gggg", null))); ((DefaultTreeModel) tree.getModel()).reload(); // listen for changes in the selection tree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(final TreeSelectionEvent e) { //System.out.println("selection changed"); } }); // show the tree on screen final JFrame frame = new JFrame("CheckBox Tree"); final JScrollPane scrollPane = new JScrollPane(tree); frame.getContentPane().add(scrollPane, BorderLayout.CENTER); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 150); frame.setVisible(true); } private static Boolean childStatus(DefaultMutableTreeNode node) { Boolean status = data(node.getChildAt(0)).isSelected(); for (int i = 1; i < node.getChildCount() && status != null; i++) { if (!status.equals( data(node.getChildAt(i)).isSelected() )) return null; } return status; } private static void changeParents(DefaultMutableTreeNode node) { if (node != null) { DefaultMutableTreeNode parent = node(node.getParent()); if (parent != null) { CheckBoxNodeData dataParent = data(parent); Boolean childStatus = childStatus(parent); if (childStatus != null && !childStatus.equals(dataParent.isSelected())) { dataParent.setSelected(childStatus); changeParents(parent); } } } } private static void nodeChanged(DefaultMutableTreeNode node) { if (node != null) { changeParents(node); setChildrens(node, data(node).isSelected()); } } private static void setChildrens(DefaultMutableTreeNode node, Boolean value) { for (int i = 0; i < node.getChildCount(); i++) { DefaultMutableTreeNode childNode = node(node.getChildAt(i)); if (!data(childNode).isSelected().equals(data(node).isSelected())) { data(childNode).setSelected(data(node).isSelected()); setChildrens(childNode, value); } } } public DefaultMutableTreeNode rootNode() { return node(getModel().getRoot()); } public LayerGroup rootLayer() { return (LayerGroup) rootData().getAbstractLayer(); } public CheckBoxNodeData rootData() { return data(rootNode()); } private static DefaultMutableTreeNode node(Object node) { return (DefaultMutableTreeNode) node; } public static CheckBoxNodeData data(DefaultMutableTreeNode node) { return node == null ? null : (CheckBoxNodeData) node.getUserObject(); } private static CheckBoxNodeData data(Object node) { return data(node(node)); } private static DefaultMutableTreeNode add(final DefaultMutableTreeNode parent, final String text, final boolean checked) { final CheckBoxNodeData data = new CheckBoxNodeData(text, checked); final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data); parent.add(node); return node; } public static CheckBoxNodeData createNodeData(AbstractLayer layer) { return new CheckBoxNodeData(layer); } public static DefaultMutableTreeNode createNode(AbstractLayer layer) { return new DefaultMutableTreeNode(createNodeData(layer)); } /*public DefaultMutableTreeNode addLayerGroup(LayerGroup group) { if (group != null){ if (group.getParent() == null){ return add(rootNode(), group); } else { DefaultMutableTreeNode parentGroup = searchNode(group.getParent()); if(parentGroup==null) parentGroup = addLayerGroup(group.getParent()); DefaultMutableTreeNode node = add(parentGroup, group); return node; } }else return null; }*/ public Layer addLayer(String name) { Layer layer = new Layer(name); addLayer(layer); return layer; } public DefaultMutableTreeNode addLayer(AbstractLayer layer) { if (layer != null) { DefaultMutableTreeNode parent; if (layer.getParent() == null) { rootLayer().add(layer); parent = rootNode(); } else { parent = searchNode(layer.getParent()); if (parent == null) parent = addLayer(layer.getParent()); } return add(parent, layer); } else return null; } public DefaultMutableTreeNode add(DefaultMutableTreeNode parent, final AbstractLayer layer) { layer.setVisible(data(parent).isSelected()); DefaultMutableTreeNode node = createNode(layer); parent.add(node); ((DefaultTreeModel) getModel()).reload(); //System.out.println("Created node "+layer+" upper of "+data(parent)); return node; } public DefaultMutableTreeNode searchNode(AbstractLayer layer) { return searchNode(rootNode(), layer); } public DefaultMutableTreeNode searchNode(DefaultMutableTreeNode node, AbstractLayer layer) { CheckBoxNodeData data = CheckBoxTree.data(node); if (data.getAbstractLayer() == layer) return node; else { DefaultMutableTreeNode found = null; for (int i = 0; i < node.getChildCount() && found == null; i++) { found = searchNode((DefaultMutableTreeNode) node.getChildAt(i), layer); } return found; } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodePanel.java0000644000000000000000000000265113255753422030133 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.checkBoxTree; import java.awt.BorderLayout; import java.awt.Insets; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; /** * Node Panel for checkBox Tree * * @author galo */ public class CheckBoxNodePanel extends JPanel { /** Serial Version UID */ private static final long serialVersionUID = -7236481597785619029L; private final JLabel label = new JLabel(); private CheckBoxNodeData data; public final JCheckBox check = new JCheckBox(); public CheckBoxNodePanel() { this.check.setMargin(new Insets(0, 0, 0, 0)); setLayout(new BorderLayout()); add(check, BorderLayout.WEST); add(label, BorderLayout.CENTER); } public void setSelected(Boolean bool) { if (bool == null) { check.getModel().setPressed(true); check.getModel().setArmed(true); } else { check.setSelected(bool.booleanValue()); check.getModel().setArmed(false); } } public CheckBoxNodeData getData() { data.setSelected(check.isSelected()); return data; } public void setData(CheckBoxNodeData data) { this.data = data; label.setText(data.getText()); } public JLabel getLabel() { return label; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeEditor.java0000644000000000000000000000702613255753416030326 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer.checkBoxTree; import java.awt.Component; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.EventObject; import javax.swing.AbstractCellEditor; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreePath; /** * Editor for checkBox Tree * * @author galo */ public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor { /** SerialVersionUID */ private static final long serialVersionUID = -8921320784224636657L; private final CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer(); private final CheckBoxTree theTree; public CheckBoxNodeEditor(final CheckBoxTree tree) { theTree = tree; } @Override public Object getCellEditorValue() { final CheckBoxNodePanel panel = renderer.getPanel(); /*final CheckBoxNodeData checkBoxNode = new CheckBoxNodeData(panel.label.getText(), panel.check.isSelected()); return checkBoxNode; CheckBoxNodeData data = search(theTree.rootNode(), panel.label.getText()); data.setSelected(panel.check.isSelected());*/ return panel.getData(); } /*public CheckBoxNodeData search(DefaultMutableTreeNode node, String name){ CheckBoxNodeData data = CheckBoxTree.data(node); if(data.getText().equals(name)) return data; else{ data = null; for(int i=0; i aUnderline = new HashMap<>(); aUnderline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); ATTR_LINK_FONT = ATTR_FONT.deriveFont(aUnderline); } public void initialize(Attributed source) { this.source = source; boolean requireAttr = source.requiresAttribution(); if (requireAttr) { attrImage = source.getAttributionImage(); attrTermsText = source.getTermsOfUseText(); attrTermsUrl = source.getTermsOfUseURL(); if (attrTermsUrl != null && attrTermsText == null) { attrTermsText = tr("Background Terms of Use"); } } else { attrImage = null; attrTermsUrl = null; } } public void paintAttribution(Graphics g, int width, int height, ICoordinate topLeft, ICoordinate bottomRight, int zoom, ImageObserver observer) { if (source == null || !source.requiresAttribution()) { attrToUBounds = null; attrImageBounds = null; attrTextBounds = null; return; } // Draw attribution Font font = g.getFont(); g.setFont(ATTR_LINK_FONT); // Draw terms of use text int termsTextHeight = 0; int termsTextY = height; if (attrTermsText != null) { Rectangle2D termsStringBounds = g.getFontMetrics().getStringBounds(attrTermsText, g); int textRealHeight = (int) termsStringBounds.getHeight(); termsTextHeight = textRealHeight - 5; int termsTextWidth = (int) termsStringBounds.getWidth(); termsTextY = height - termsTextHeight; int x = 2; int y = height - termsTextHeight; attrToUBounds = new Rectangle(x, y-termsTextHeight, termsTextWidth, textRealHeight); g.setColor(Color.black); g.drawString(attrTermsText, x + 1, y + 1); g.setColor(Color.white); g.drawString(attrTermsText, x, y); } else { attrToUBounds = null; } // Draw attribution logo if (attrImage != null) { int x = 2; int imgWidth = attrImage.getWidth(observer); int imgHeight = attrImage.getHeight(observer); int y = termsTextY - imgHeight - termsTextHeight - 5; attrImageBounds = new Rectangle(x, y, imgWidth, imgHeight); g.drawImage(attrImage, x, y, null); } else { attrImageBounds = null; } g.setFont(ATTR_FONT); String attributionText = source.getAttributionText(zoom, topLeft, bottomRight); if (attributionText == null) { // In case attribution text has been forgotte, display URL attributionText = source.getAttributionLinkURL(); } if (attributionText != null) { Rectangle2D stringBounds = g.getFontMetrics().getStringBounds(attributionText, g); int textHeight = (int) stringBounds.getHeight() - 5; int x = width - (int) stringBounds.getWidth(); int y = height - textHeight; g.setColor(Color.black); g.drawString(attributionText, x + 1, y + 1); g.setColor(Color.white); g.drawString(attributionText, x, y); attrTextBounds = new Rectangle(x, y-textHeight, (int) stringBounds.getWidth(), (int) stringBounds.getHeight()); } else { attrTextBounds = null; } g.setFont(font); } public boolean handleAttributionCursor(Point p) { if (attrTextBounds != null && attrTextBounds.contains(p)) { return true; } else if (attrImageBounds != null && attrImageBounds.contains(p)) { return true; } else if (attrToUBounds != null && attrToUBounds.contains(p)) { return true; } return false; } public boolean handleAttribution(Point p, boolean click) { if (source == null || !source.requiresAttribution()) return false; if (attrTextBounds != null && attrTextBounds.contains(p)) { String attributionURL = source.getAttributionLinkURL(); if (attributionURL != null) { if (click) { FeatureAdapter.openLink(attributionURL); } return true; } } else if (attrImageBounds != null && attrImageBounds.contains(p)) { String attributionImageURL = source.getAttributionImageURL(); if (attributionImageURL != null) { if (click) { FeatureAdapter.openLink(source.getAttributionImageURL()); } return true; } } else if (attrToUBounds != null && attrToUBounds.contains(p)) { String termsOfUseURL = source.getTermsOfUseURL(); if (termsOfUseURL != null) { if (click) { FeatureAdapter.openLink(termsOfUseURL); } return true; } } return false; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/images/0000755000000000000000000000000013255753422022707 5ustar rootrootjmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/images/plus.png0000644000000000000000000000020513255753422024375 0ustar rootrootPNG  IHDR |lLIDATxcF9 p&2H,`PR0#q ?>AyaXb DFuIb#IENDB`jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/images/error.png0000644000000000000000000000622113255753416024552 0ustar rootrootPNG  IHDR\rf XIDATxbaDQ0 F8#ϣ`f|FF{ .i1`4ةc"Bѭ7 J @ @ @ @P @ @ @ @P @ @ @ @(33 !&W!%\D@R@EU@VmPKmT($ř8N \ ߎcxN!^K/;;_;6 $zR, pɾ/\(;["X <&pTؗMr[p <Zr8BUD\(I!S*b-[`iE8.) %e{{K!ȿTE)VoJK!LL`_<!g6Rq5Ѯ i{o% x{#/t}˜4'CF#ɍ#?W]N;Gg1Zh;mcէ/bL:AˁIZtG~Oŧ/bwtgeH x"G#6F4O>J" 8dizL׍`t9|Z bkFh_򋍈@*ǝL$)!;/_`FW=A%آW>Dlmo9]F"̻pbL'f`뿈w4)&&∡//ʑ.#B ٲdwb[n73['%3Gr``]$!\@^|Nlc0ǝl`/}Qv H"1[\(=++$OomMߍ@{5 ?8vvܷ<P8u;B~;qIK@k-w1H"~T5CW~`w$~r}i!?(7?+{oOtcZɏo#ct ҖRq {#~g56.KB!kj +#B>S'ٛ"@ q#$ɟĻT- a6F ]9IDATxA 0ФXm0D`i'C@\?)Z΁ ~T3?< 2,i\۠[-Tpގ9_O >e&}O?_[W'; @3Bc׀q<(~*3䑾P~{\W}ǿ̾w_I0 N FGjJ(RU! j%*GjK%R*k'[ kQ _*}zE=.mn_FW\Mmj ((C&3@!C`5~@^Mů_xn6d)ճNU QJ[7(j_H$[ }'ں%7bk#y$fFMcLFζ84ٻ{̑I-)24%DWoB.]*Ox?\p.IҌ0Eqôl\{jb``؂k̜><&ӌKWů#`]w lPpt[ K>!H2a&zǻ'&N7'S'dBȾHEq ѠdK. B|ܳm|6tK:\-Iol؇ Jt\MeD"jJ~u,ДH|ݏǶM15t4\ !C=fbUm;wow9[Lz(Y[v=}{d4 gp-\)8ME Аݽuߖ-5@ɓr_*DwGͻ`舑A@8n6:Y-tZ#2W_qCBpVSιua6 R.'OxO_1KCL9A (1u2u`p; }BɯիV;}kضҠm SP_!م 0,7(rǽ̈́ |v1/P+n)Zبg^=V4Z>CAQ(tt̸0 ƥ@"ppG*g^#=᧸ S~͙/F12e3N ]@#[D\{0 @X6&&7˴UCW8Y" Α{tww ,D("gZE1s{VCceN$Ӌg_"S˖S(|h/ɳG_1zv*)FWh6(#h{eԮOw݋s^KW,Dj %z @_ٽwX4-L0iQPY"{AY|G1;uyۀ|/jW?}re)|U^eُFbMP<7w6_Jzv>CO۱ùx|4Aaitp<& l qiA`F/ݫOF~IfYFk7W_ y+'ϿrD(i]i6_8EyoA0x6s+N T&܁}X}W.ta!wr$ @S f8(ZO? /9 įK[P+~@z - [kUO5>wۆ]R#k g#"Pݎzd:|Č|9!J~ɜF X`͜~~JtH_Oz.rF kyt"^A _ɯ5"3gb~ۚWg :ϧο0%ŮA^BH [RJ_JJH[ W&Μ.,6Cʦ_{~S!SO~3# bSɖ-  皋0kJeRp['B ~$C!cdt|Q0a^˥Nc%NOKW|sO[tY+jϕsP?m72F^ʹ}='?ݱ^Ӌ=wǛ&Oj^ħYf"_qkʘlew[z_vuC ̵#9fucyr0m;'f+5Q"G2J~%>I>7ӟCohA3t/[@n`YV ܂C{YSs(k@OAɯJ}S\;w|}yC.) xX?Ce Y!-cϾvݿ2{y2 O6R(,3wסӟwv u;0'V˞`uڃ4 cu> cc}S  DB@Wlze3COgn2phq/g VT@@X =NǷzGtQr*ܡP{`a:;>ݥm@A`'\Pku .^:jXwT C *K48 nEy99`fܩP+nڳ659SCѰ E l];8ktƕ@T3,t 0uQ0 (Ւ^͇dmߝ̻6haAC+ I8ͫ0ik _csP͟!xnlXrw0\ JsR~3}V͟ P }2.rK;ہ0έ.I "&~fת>B5[~>[+\Jo03z}_qSܥZ΂kPk@L\^A?!SƤt-TW?dnwLj5sW Z"D7W(6wQk5t>34r@4p8\ ΅%Wƅs.R>*- ^ ڋm,h, ѵR_W$H23j:dž{kb4?,eƲbeIJ3F` h>4*cHk; )MCJ4~߾o}ݯUWjt5yOc,8k$K.ŧ{5nL(A9pZ}FUɯWlAR|w-6} ]3@to]v3YX5 )|Po5`brJo_@7޺$P~7\'c 񘱸}}=zsߓ<49֮|j(",󷡅V2<2Q3'" X,qK}?d" +Q~p.С'a`A\zv_>Ԧ4'z:(q2qE3N^hz|Y a$Qڧaa4C`A?|fZvB m3Ns?yU4񅌒6Vʅ5Jq5z;nJ]=%7`9W  :Ff刦ξX>mVj2?:w)?_A&s>\p&LƱڂ1@ V-o:>)8.P,YXHth.Ra%<ӆ(@H !"hiiX @\`nFE (~_z#~m| [o]* Ǚ+? I6LFAzίgg^״ۮ(?=ԙ+gr5' %h$j#nЈEvP]&2iJ~u?ֹ]yC{ l@K*=3guc-΅ Z=y84 "uDZL E""YC׺&w+yyM^`_'w㼼 'cH7IAI.͖':.ye>|m)|K?WpaBr"?2JZRdp=?O\< R#zJ0P-(9 C(7[fvWϾ Yvxއ$y0~'Zyͯ0T7_aq/G#_j^0絪:ߙ 5{ov+?{HoM C!; qןMqaF|w!Ol\NMBƚxBIXW|~ޖ ۈIENDB`jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/package.html0000644000000000000000000000101613255753422023721 0ustar rootroot org.openstreetmap.gui.jmapviewer

This package and all sub-packages are belonging to the Java component JMapViewer

JMapViewer is designed to run as stand-alone component without any further requirements. Therefore please do not add any code that depends on other libraries or applications. Only functions and methods provided by the runtime library of Java 8 should be used.

jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/TileController.java0000644000000000000000000000520413255753416025252 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import org.openstreetmap.gui.jmapviewer.interfaces.TileCache; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener; import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; public class TileController { protected TileLoader tileLoader; protected TileCache tileCache; protected TileSource tileSource; public TileController(TileSource source, TileCache tileCache, TileLoaderListener listener) { this.tileSource = source; this.tileLoader = new OsmTileLoader(listener); this.tileCache = tileCache; } /** * retrieves a tile from the cache. If the tile is not present in the cache * a load job is added to the working queue of {@link TileLoader}. * * @param tilex the X position of the tile * @param tiley the Y position of the tile * @param zoom the zoom level of the tile * @return specified tile from the cache or null if the tile * was not found in the cache. */ public Tile getTile(int tilex, int tiley, int zoom) { int max = 1 << zoom; if (tilex < 0 || tilex >= max || tiley < 0 || tiley >= max) return null; Tile tile = tileCache.getTile(tileSource, tilex, tiley, zoom); if (tile == null) { tile = new Tile(tileSource, tilex, tiley, zoom); tileCache.addTile(tile); tile.loadPlaceholderFromCache(tileCache); } if (tile.error) { tile.loadPlaceholderFromCache(tileCache); } if (!tile.isLoaded()) { tileLoader.createTileLoaderJob(tile).submit(); } return tile; } public TileCache getTileCache() { return tileCache; } public void setTileCache(TileCache tileCache) { this.tileCache = tileCache; } public TileLoader getTileLoader() { return tileLoader; } public void setTileLoader(TileLoader tileLoader) { this.tileLoader = tileLoader; } public TileSource getTileLayerSource() { return tileSource; } public TileSource getTileSource() { return tileSource; } public void setTileSource(TileSource tileSource) { this.tileSource = tileSource; } /** * Removes all jobs from the queue that are currently not being processed. * */ public void cancelOutstandingJobs() { tileLoader.cancelOutstandingTasks(); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/DefaultMapController.java0000644000000000000000000001475213255753416026407 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.util.Locale; /** * Default map controller which implements map moving by pressing the right * mouse button and zooming by double click or by mouse wheel. * * @author Jan Peter Stotz * */ public class DefaultMapController extends JMapController implements MouseListener, MouseMotionListener, MouseWheelListener { private static final int MOUSE_BUTTONS_MASK = MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK; private static final int MAC_MOUSE_BUTTON3_MASK = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK; private Point lastDragPoint; private boolean isMoving; private boolean movementEnabled = true; private int movementMouseButton = MouseEvent.BUTTON3; private int movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK; private boolean wheelZoomEnabled = true; private boolean doubleClickZoomEnabled = true; /** * Constructs a new {@code DefaultMapController}. * @param map map panel */ public DefaultMapController(JMapViewer map) { super(map); } @Override public void mouseDragged(MouseEvent e) { if (!movementEnabled || !isMoving) return; // Is only the selected mouse button pressed? if ((e.getModifiersEx() & MOUSE_BUTTONS_MASK) == movementMouseButtonMask || (isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) { Point p = e.getPoint(); if (lastDragPoint != null) { int diffx = lastDragPoint.x - p.x; int diffy = lastDragPoint.y - p.y; map.moveMap(diffx, diffy); } lastDragPoint = p; } } @Override public void mouseClicked(MouseEvent e) { if (doubleClickZoomEnabled && e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) { map.zoomIn(e.getPoint()); } } @Override public void mousePressed(MouseEvent e) { if (e.getButton() == movementMouseButton || (isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) { lastDragPoint = null; isMoving = true; } } @Override public void mouseReleased(MouseEvent e) { if (e.getButton() == movementMouseButton || (isPlatformOsx() && e.getButton() == MouseEvent.BUTTON1)) { lastDragPoint = null; isMoving = false; } } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (wheelZoomEnabled) { int rotation = JMapViewer.zoomReverseWheel ? -e.getWheelRotation() : e.getWheelRotation(); map.setZoom(map.getZoom() - rotation, e.getPoint()); } } /** * Determines if the map pane is allowed to be moved using the mouse * @return {@code true} to allow the map pane to be moved using the mouse */ public boolean isMovementEnabled() { return movementEnabled; } /** * Enables or disables that the map pane can be moved using the mouse. * * @param movementEnabled {@code true} to allow the map pane to be moved using the mouse */ public void setMovementEnabled(boolean movementEnabled) { this.movementEnabled = movementEnabled; } public int getMovementMouseButton() { return movementMouseButton; } /** * Sets the mouse button that is used for moving the map. Possible values are: *
    *
  • {@link MouseEvent#BUTTON1} (left mouse button)
  • *
  • {@link MouseEvent#BUTTON2} (middle mouse button)
  • *
  • {@link MouseEvent#BUTTON3} (right mouse button)
  • *
* * @param movementMouseButton the mouse button that is used for moving the map */ public void setMovementMouseButton(int movementMouseButton) { this.movementMouseButton = movementMouseButton; switch (movementMouseButton) { case MouseEvent.BUTTON1: movementMouseButtonMask = MouseEvent.BUTTON1_DOWN_MASK; break; case MouseEvent.BUTTON2: movementMouseButtonMask = MouseEvent.BUTTON2_DOWN_MASK; break; case MouseEvent.BUTTON3: movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK; break; default: throw new RuntimeException("Unsupported button"); } } public boolean isWheelZoomEnabled() { return wheelZoomEnabled; } public void setWheelZoomEnabled(boolean wheelZoomEnabled) { this.wheelZoomEnabled = wheelZoomEnabled; } public boolean isDoubleClickZoomEnabled() { return doubleClickZoomEnabled; } public void setDoubleClickZoomEnabled(boolean doubleClickZoomEnabled) { this.doubleClickZoomEnabled = doubleClickZoomEnabled; } @Override public void mouseEntered(MouseEvent e) { // do nothing } @Override public void mouseExited(MouseEvent e) { // do nothing } @Override public void mouseMoved(MouseEvent e) { // Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired. // if (isPlatformOsx()) { if (!movementEnabled || !isMoving) return; // Is only the selected mouse button pressed? if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) { Point p = e.getPoint(); if (lastDragPoint != null) { int diffx = lastDragPoint.x - p.x; int diffy = lastDragPoint.y - p.y; map.moveMap(diffx, diffy); } lastDragPoint = p; } } } /** * Replies true if we are currently running on OSX * * @return true if we are currently running on OSX */ public static boolean isPlatformOsx() { String os = System.getProperty("os.name"); return os != null && os.toLowerCase(Locale.ENGLISH).startsWith("mac os x"); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/MapObjectImpl.java0000644000000000000000000000727113255753416025005 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Point; import java.awt.Stroke; import javax.swing.UIManager; public abstract class MapObjectImpl { private Layer layer; private String name; private Style style; private Boolean visible; public MapObjectImpl(String name) { this(null, name, null); } public MapObjectImpl(Layer layer) { this(layer, null, null); } public MapObjectImpl(Layer layer, String name, Style style) { super(); this.layer = layer; this.name = name; this.style = style; } public Layer getLayer() { return layer; } public void setLayer(Layer layer) { this.layer = layer; } public Style getStyle() { return style; } public Style getStyleAssigned() { return style == null ? (layer == null ? null : layer.getStyle()) : style; } public void setStyle(Style style) { this.style = style; } public Color getColor() { Style styleAssigned = getStyleAssigned(); return styleAssigned == null ? null : getStyleAssigned().getColor(); } public void setColor(Color color) { if (style == null && color != null) style = new Style(); if (style != null) style.setColor(color); } public Color getBackColor() { Style styleAssigned = getStyleAssigned(); return styleAssigned == null ? null : getStyleAssigned().getBackColor(); } public void setBackColor(Color backColor) { if (style == null && backColor != null) style = new Style(); if (style != null) style.setBackColor(backColor); } public Stroke getStroke() { Style styleAssigned = getStyleAssigned(); return styleAssigned == null ? null : getStyleAssigned().getStroke(); } public void setStroke(Stroke stroke) { if (style == null && stroke != null) style = new Style(); if (style != null) style.setStroke(stroke); } public Font getFont() { Style styleAssigned = getStyleAssigned(); return styleAssigned == null ? null : getStyleAssigned().getFont(); } public void setFont(Font font) { if (style == null && font != null) style = new Style(); if (style != null) style.setFont(font); } private boolean isVisibleLayer() { return layer == null || layer.isVisible() == null ? true : layer.isVisible(); } public boolean isVisible() { return visible == null ? isVisibleLayer() : visible.booleanValue(); } public void setVisible(Boolean visible) { this.visible = visible; } public String getName() { return name; } public void setName(String txt) { this.name = txt; } public static Font getDefaultFont() { Font f = UIManager.getDefaults().getFont("TextField.font"); if (f == null) { f = Font.decode(null); } return new Font(f.getName(), Font.BOLD, f.getSize()); } public void paintText(Graphics g, Point position) { if (name != null && g != null && position != null) { if (getFont() == null) { Font f = getDefaultFont(); setFont(new Font(f.getName(), Font.BOLD, f.getSize())); } g.setColor(Color.DARK_GRAY); g.setFont(getFont()); g.drawString(name, position.x+MapMarkerDot.DOT_RADIUS+2, position.y+MapMarkerDot.DOT_RADIUS); } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java0000644000000000000000000012511413255753422024322 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent; import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent.COMMAND; import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener; import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker; import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon; import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle; import org.openstreetmap.gui.jmapviewer.interfaces.TileCache; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener; import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource; /** * Provides a simple panel that displays pre-rendered map tiles loaded from the * OpenStreetMap project. * * @author Jan Peter Stotz * @author Jason Huntley */ public class JMapViewer extends JPanel implements TileLoaderListener { private static final long serialVersionUID = 1L; /** whether debug mode is enabled or not */ public static boolean debug; /** option to reverse zoom direction with mouse wheel */ public static boolean zoomReverseWheel; /** * Vectors for clock-wise tile painting */ private static final Point[] move = {new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1)}; /** Maximum zoom level */ public static final int MAX_ZOOM = 22; /** Minimum zoom level */ public static final int MIN_ZOOM = 0; protected transient List mapMarkerList; protected transient List mapRectangleList; protected transient List mapPolygonList; protected boolean mapMarkersVisible; protected boolean mapRectanglesVisible; protected boolean mapPolygonsVisible; protected boolean tileGridVisible; protected boolean scrollWrapEnabled; protected transient TileController tileController; /** * x- and y-position of the center of this map-panel on the world map * denoted in screen pixel regarding the current zoom level. */ protected Point center; /** * Current zoom level */ protected int zoom; protected JSlider zoomSlider; protected JButton zoomInButton; protected JButton zoomOutButton; /** * Apparence of zoom controls. */ public enum ZOOM_BUTTON_STYLE { /** Zoom buttons are displayed horizontally (default) */ HORIZONTAL, /** Zoom buttons are displayed vertically */ VERTICAL } protected ZOOM_BUTTON_STYLE zoomButtonStyle; protected transient TileSource tileSource; protected transient AttributionSupport attribution = new AttributionSupport(); protected EventListenerList evtListenerList = new EventListenerList(); /** * Creates a standard {@link JMapViewer} instance that can be controlled via * mouse: hold right mouse button for moving, double click left mouse button * or use mouse wheel for zooming. Loaded tiles are stored in a * {@link MemoryTileCache} and the tile loader uses 4 parallel threads for * retrieving the tiles. */ public JMapViewer() { this(new MemoryTileCache()); new DefaultMapController(this); } /** * Creates a new {@link JMapViewer} instance. * @param tileCache The cache where to store tiles * @param downloadThreadCount not used anymore * @deprecated use {@link #JMapViewer(TileCache)} */ @Deprecated public JMapViewer(TileCache tileCache, int downloadThreadCount) { this(tileCache); } /** * Creates a new {@link JMapViewer} instance. * @param tileCache The cache where to store tiles * */ public JMapViewer(TileCache tileCache) { tileSource = new OsmTileSource.Mapnik(); tileController = new TileController(tileSource, tileCache, this); mapMarkerList = Collections.synchronizedList(new ArrayList()); mapPolygonList = Collections.synchronizedList(new ArrayList()); mapRectangleList = Collections.synchronizedList(new ArrayList()); mapMarkersVisible = true; mapRectanglesVisible = true; mapPolygonsVisible = true; tileGridVisible = false; setLayout(null); initializeZoomSlider(); setMinimumSize(new Dimension(tileSource.getTileSize(), tileSource.getTileSize())); setPreferredSize(new Dimension(400, 400)); setDisplayPosition(new Coordinate(50, 9), 3); } @Override public String getToolTipText(MouseEvent event) { return super.getToolTipText(event); } protected void initializeZoomSlider() { zoomSlider = new JSlider(MIN_ZOOM, tileController.getTileSource().getMaxZoom()); zoomSlider.setOrientation(JSlider.VERTICAL); zoomSlider.setBounds(10, 10, 30, 150); zoomSlider.setOpaque(false); zoomSlider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { setZoom(zoomSlider.getValue()); } }); zoomSlider.setFocusable(false); add(zoomSlider); int size = 18; URL url = JMapViewer.class.getResource("images/plus.png"); if (url != null) { ImageIcon icon = new ImageIcon(url); zoomInButton = new JButton(icon); } else { zoomInButton = new JButton("+"); zoomInButton.setFont(new Font("sansserif", Font.BOLD, 9)); zoomInButton.setMargin(new Insets(0, 0, 0, 0)); } zoomInButton.setBounds(4, 155, size, size); zoomInButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { zoomIn(); } }); zoomInButton.setFocusable(false); add(zoomInButton); url = JMapViewer.class.getResource("images/minus.png"); if (url != null) { ImageIcon icon = new ImageIcon(url); zoomOutButton = new JButton(icon); } else { zoomOutButton = new JButton("-"); zoomOutButton.setFont(new Font("sansserif", Font.BOLD, 9)); zoomOutButton.setMargin(new Insets(0, 0, 0, 0)); } zoomOutButton.setBounds(8 + size, 155, size, size); zoomOutButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { zoomOut(); } }); zoomOutButton.setFocusable(false); add(zoomOutButton); } /** * Changes the map pane so that it is centered on the specified coordinate * at the given zoom level. * * @param to * specified coordinate * @param zoom * {@link #MIN_ZOOM} <= zoom level <= {@link #MAX_ZOOM} */ public void setDisplayPosition(ICoordinate to, int zoom) { setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), to, zoom); } /** * Changes the map pane so that the specified coordinate at the given zoom * level is displayed on the map at the screen coordinate * mapPoint. * * @param mapPoint * point on the map denoted in pixels where the coordinate should * be set * @param to * specified coordinate * @param zoom * {@link #MIN_ZOOM} <= zoom level <= * {@link TileSource#getMaxZoom()} */ public void setDisplayPosition(Point mapPoint, ICoordinate to, int zoom) { Point p = tileSource.latLonToXY(to, zoom); setDisplayPosition(mapPoint, p.x, p.y, zoom); } /** * Sets the display position. * @param x X coordinate * @param y Y coordinate * @param zoom zoom level, between {@link #MIN_ZOOM} and {@link #MAX_ZOOM} */ public void setDisplayPosition(int x, int y, int zoom) { setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), x, y, zoom); } /** * Sets the display position. * @param mapPoint map point * @param x X coordinate * @param y Y coordinate * @param zoom zoom level, between {@link #MIN_ZOOM} and {@link #MAX_ZOOM} */ public void setDisplayPosition(Point mapPoint, int x, int y, int zoom) { if (zoom > tileController.getTileSource().getMaxZoom() || zoom < MIN_ZOOM) return; // Get the plain tile number Point p = new Point(); p.x = x - mapPoint.x + getWidth() / 2; p.y = y - mapPoint.y + getHeight() / 2; center = p; setIgnoreRepaint(true); try { int oldZoom = this.zoom; this.zoom = zoom; if (oldZoom != zoom) { zoomChanged(oldZoom); } if (zoomSlider.getValue() != zoom) { zoomSlider.setValue(zoom); } } finally { setIgnoreRepaint(false); repaint(); } } /** * Sets the displayed map pane and zoom level so that all chosen map elements are visible. * @param markers whether to consider markers * @param rectangles whether to consider rectangles * @param polygons whether to consider polygons */ public void setDisplayToFitMapElements(boolean markers, boolean rectangles, boolean polygons) { int nbElemToCheck = 0; if (markers && mapMarkerList != null) nbElemToCheck += mapMarkerList.size(); if (rectangles && mapRectangleList != null) nbElemToCheck += mapRectangleList.size(); if (polygons && mapPolygonList != null) nbElemToCheck += mapPolygonList.size(); if (nbElemToCheck == 0) return; int xMin = Integer.MAX_VALUE; int yMin = Integer.MAX_VALUE; int xMax = Integer.MIN_VALUE; int yMax = Integer.MIN_VALUE; int mapZoomMax = tileController.getTileSource().getMaxZoom(); if (markers && mapMarkerList != null) { synchronized (this) { for (MapMarker marker : mapMarkerList) { if (marker.isVisible()) { Point p = tileSource.latLonToXY(marker.getCoordinate(), mapZoomMax); xMax = Math.max(xMax, p.x); yMax = Math.max(yMax, p.y); xMin = Math.min(xMin, p.x); yMin = Math.min(yMin, p.y); } } } } if (rectangles && mapRectangleList != null) { synchronized (this) { for (MapRectangle rectangle : mapRectangleList) { if (rectangle.isVisible()) { Point bottomRight = tileSource.latLonToXY(rectangle.getBottomRight(), mapZoomMax); Point topLeft = tileSource.latLonToXY(rectangle.getTopLeft(), mapZoomMax); xMax = Math.max(xMax, bottomRight.x); yMax = Math.max(yMax, topLeft.y); xMin = Math.min(xMin, topLeft.x); yMin = Math.min(yMin, bottomRight.y); } } } } if (polygons && mapPolygonList != null) { synchronized (this) { for (MapPolygon polygon : mapPolygonList) { if (polygon.isVisible()) { for (ICoordinate c : polygon.getPoints()) { Point p = tileSource.latLonToXY(c, mapZoomMax); xMax = Math.max(xMax, p.x); yMax = Math.max(yMax, p.y); xMin = Math.min(xMin, p.x); yMin = Math.min(yMin, p.y); } } } } } int height = Math.max(0, getHeight()); int width = Math.max(0, getWidth()); int newZoom = mapZoomMax; int x = xMax - xMin; int y = yMax - yMin; while (x > width || y > height) { newZoom--; x >>= 1; y >>= 1; } x = xMin + (xMax - xMin) / 2; y = yMin + (yMax - yMin) / 2; int z = 1 << (mapZoomMax - newZoom); x /= z; y /= z; setDisplayPosition(x, y, newZoom); } /** * Sets the displayed map pane and zoom level so that all map markers are visible. */ public void setDisplayToFitMapMarkers() { setDisplayToFitMapElements(true, false, false); } /** * Sets the displayed map pane and zoom level so that all map rectangles are visible. */ public void setDisplayToFitMapRectangles() { setDisplayToFitMapElements(false, true, false); } /** * Sets the displayed map pane and zoom level so that all map polygons are visible. */ public void setDisplayToFitMapPolygons() { setDisplayToFitMapElements(false, false, true); } /** * @return the center */ public Point getCenter() { return center; } /** * @param center the center to set */ public void setCenter(Point center) { this.center = center; } /** * Calculates the latitude/longitude coordinate of the center of the * currently displayed map area. * * @return latitude / longitude */ public ICoordinate getPosition() { return tileSource.xyToLatLon(center, zoom); } /** * Converts the relative pixel coordinate (regarding the top left corner of * the displayed map) into a latitude / longitude coordinate * * @param mapPoint * relative pixel coordinate regarding the top left corner of the * displayed map * @return latitude / longitude */ public ICoordinate getPosition(Point mapPoint) { return getPosition(mapPoint.x, mapPoint.y); } /** * Converts the relative pixel coordinate (regarding the top left corner of * the displayed map) into a latitude / longitude coordinate * * @param mapPointX X coordinate * @param mapPointY Y coordinate * @return latitude / longitude */ public ICoordinate getPosition(int mapPointX, int mapPointY) { int x = center.x + mapPointX - getWidth() / 2; int y = center.y + mapPointY - getHeight() / 2; return tileSource.xyToLatLon(x, y, zoom); } /** * Calculates the position on the map of a given coordinate * * @param lat latitude * @param lon longitude * @param checkOutside check if the point is outside the displayed area * @return point on the map or null if the point is not visible * and checkOutside set to true */ public Point getMapPosition(double lat, double lon, boolean checkOutside) { Point p = tileSource.latLonToXY(lat, lon, zoom); p.translate(-(center.x - getWidth() / 2), -(center.y - getHeight() /2)); if (checkOutside && (p.x < 0 || p.y < 0 || p.x > getWidth() || p.y > getHeight())) { return null; } return p; } /** * Calculates the position on the map of a given coordinate * * @param lat latitude * @param lon longitude * @return point on the map or null if the point is not visible */ public Point getMapPosition(double lat, double lon) { return getMapPosition(lat, lon, true); } /** * Calculates the position on the map of a given coordinate * * @param lat Latitude * @param lon longitude * @param offset Offset respect Latitude * @param checkOutside check if the point is outside the displayed area * @return Integer the radius in pixels */ public Integer getLatOffset(double lat, double lon, double offset, boolean checkOutside) { Point p = tileSource.latLonToXY(lat + offset, lon, zoom); int y = p.y - (center.y - getHeight() / 2); if (checkOutside && (y < 0 || y > getHeight())) { return null; } return y; } /** * Calculates the position on the map of a given coordinate * * @param marker MapMarker object that define the x,y coordinate * @param p coordinate * @return Integer the radius in pixels */ public Integer getRadius(MapMarker marker, Point p) { if (marker.getMarkerStyle() == MapMarker.STYLE.FIXED) return (int) marker.getRadius(); else if (p != null) { Integer radius = getLatOffset(marker.getLat(), marker.getLon(), marker.getRadius(), false); radius = radius == null ? null : p.y - radius; return radius; } else return null; } /** * Calculates the position on the map of a given coordinate * * @param coord coordinate * @return point on the map or null if the point is not visible */ public Point getMapPosition(Coordinate coord) { if (coord != null) return getMapPosition(coord.getLat(), coord.getLon()); else return null; } /** * Calculates the position on the map of a given coordinate * * @param coord coordinate * @param checkOutside check if the point is outside the displayed area * @return point on the map or null if the point is not visible * and checkOutside set to true */ public Point getMapPosition(ICoordinate coord, boolean checkOutside) { if (coord != null) return getMapPosition(coord.getLat(), coord.getLon(), checkOutside); else return null; } /** * Gets the meter per pixel. * * @return the meter per pixel */ public double getMeterPerPixel() { Point origin = new Point(5, 5); Point center = new Point(getWidth() / 2, getHeight() / 2); double pDistance = center.distance(origin); ICoordinate originCoord = getPosition(origin); ICoordinate centerCoord = getPosition(center); double mDistance = tileSource.getDistance(originCoord.getLat(), originCoord.getLon(), centerCoord.getLat(), centerCoord.getLon()); return mDistance / pDistance; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); int iMove = 0; int tilesize = tileSource.getTileSize(); int tilex = center.x / tilesize; int tiley = center.y / tilesize; int offsx = center.x % tilesize; int offsy = center.y % tilesize; int w2 = getWidth() / 2; int h2 = getHeight() / 2; int posx = w2 - offsx; int posy = h2 - offsy; int diffLeft = offsx; int diffRight = tilesize - offsx; int diffTop = offsy; int diffBottom = tilesize - offsy; boolean startLeft = diffLeft < diffRight; boolean startTop = diffTop < diffBottom; if (startTop) { if (startLeft) { iMove = 2; } else { iMove = 3; } } else { if (startLeft) { iMove = 1; } else { iMove = 0; } } // calculate the visibility borders int xMin = -tilesize; int yMin = -tilesize; int xMax = getWidth(); int yMax = getHeight(); // calculate the length of the grid (number of squares per edge) int gridLength = 1 << zoom; // paint the tiles in a spiral, starting from center of the map boolean painted = true; int x = 0; while (painted) { painted = false; for (int i = 0; i < 4; i++) { if (i % 2 == 0) { x++; } for (int j = 0; j < x; j++) { if (xMin <= posx && posx <= xMax && yMin <= posy && posy <= yMax) { // tile is visible Tile tile; if (scrollWrapEnabled) { // in case tilex is out of bounds, grab the tile to use for wrapping int tilexWrap = ((tilex % gridLength) + gridLength) % gridLength; tile = tileController.getTile(tilexWrap, tiley, zoom); } else { tile = tileController.getTile(tilex, tiley, zoom); } if (tile != null) { tile.paint(g, posx, posy, tilesize, tilesize); if (tileGridVisible) { g.drawRect(posx, posy, tilesize, tilesize); } } painted = true; } Point p = move[iMove]; posx += p.x * tilesize; posy += p.y * tilesize; tilex += p.x; tiley += p.y; } iMove = (iMove + 1) % move.length; } } // outer border of the map int mapSize = tilesize << zoom; if (scrollWrapEnabled) { g.drawLine(0, h2 - center.y, getWidth(), h2 - center.y); g.drawLine(0, h2 - center.y + mapSize, getWidth(), h2 - center.y + mapSize); } else { g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize); } // g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20); // keep x-coordinates from growing without bound if scroll-wrap is enabled if (scrollWrapEnabled) { center.x = center.x % mapSize; } if (mapPolygonsVisible && mapPolygonList != null) { synchronized (this) { for (MapPolygon polygon : mapPolygonList) { if (polygon.isVisible()) paintPolygon(g, polygon); } } } if (mapRectanglesVisible && mapRectangleList != null) { synchronized (this) { for (MapRectangle rectangle : mapRectangleList) { if (rectangle.isVisible()) paintRectangle(g, rectangle); } } } if (mapMarkersVisible && mapMarkerList != null) { synchronized (this) { for (MapMarker marker : mapMarkerList) { if (marker.isVisible()) paintMarker(g, marker); } } } attribution.paintAttribution(g, getWidth(), getHeight(), getPosition(0, 0), getPosition(getWidth(), getHeight()), zoom, this); } /** * Paint a single marker. * @param g Graphics used for painting * @param marker marker to paint */ protected void paintMarker(Graphics g, MapMarker marker) { Point p = getMapPosition(marker.getLat(), marker.getLon(), marker.getMarkerStyle() == MapMarker.STYLE.FIXED); Integer radius = getRadius(marker, p); if (scrollWrapEnabled) { int tilesize = tileSource.getTileSize(); int mapSize = tilesize << zoom; if (p == null) { p = getMapPosition(marker.getLat(), marker.getLon(), false); radius = getRadius(marker, p); } marker.paint(g, p, radius); int xSave = p.x; int xWrap = xSave; // overscan of 15 allows up to 30-pixel markers to gracefully scroll off the edge of the panel while ((xWrap -= mapSize) >= -15) { p.x = xWrap; marker.paint(g, p, radius); } xWrap = xSave; while ((xWrap += mapSize) <= getWidth() + 15) { p.x = xWrap; marker.paint(g, p, radius); } } else { if (p != null) { marker.paint(g, p, radius); } } } /** * Paint a single rectangle. * @param g Graphics used for painting * @param rectangle rectangle to paint */ protected void paintRectangle(Graphics g, MapRectangle rectangle) { Coordinate topLeft = rectangle.getTopLeft(); Coordinate bottomRight = rectangle.getBottomRight(); if (topLeft != null && bottomRight != null) { Point pTopLeft = getMapPosition(topLeft, false); Point pBottomRight = getMapPosition(bottomRight, false); if (pTopLeft != null && pBottomRight != null) { rectangle.paint(g, pTopLeft, pBottomRight); if (scrollWrapEnabled) { int tilesize = tileSource.getTileSize(); int mapSize = tilesize << zoom; int xTopLeftSave = pTopLeft.x; int xTopLeftWrap = xTopLeftSave; int xBottomRightSave = pBottomRight.x; int xBottomRightWrap = xBottomRightSave; while ((xBottomRightWrap -= mapSize) >= 0) { xTopLeftWrap -= mapSize; pTopLeft.x = xTopLeftWrap; pBottomRight.x = xBottomRightWrap; rectangle.paint(g, pTopLeft, pBottomRight); } xTopLeftWrap = xTopLeftSave; xBottomRightWrap = xBottomRightSave; while ((xTopLeftWrap += mapSize) <= getWidth()) { xBottomRightWrap += mapSize; pTopLeft.x = xTopLeftWrap; pBottomRight.x = xBottomRightWrap; rectangle.paint(g, pTopLeft, pBottomRight); } } } } } /** * Paint a single polygon. * @param g Graphics used for painting * @param polygon polygon to paint */ protected void paintPolygon(Graphics g, MapPolygon polygon) { List coords = polygon.getPoints(); if (coords != null && coords.size() >= 3) { List points = new ArrayList<>(); for (ICoordinate c : coords) { Point p = getMapPosition(c, false); if (p == null) { return; } points.add(p); } polygon.paint(g, points); if (scrollWrapEnabled) { int tilesize = tileSource.getTileSize(); int mapSize = tilesize << zoom; List pointsWrapped = new ArrayList<>(points); boolean keepWrapping = true; while (keepWrapping) { for (Point p : pointsWrapped) { p.x -= mapSize; if (p.x < 0) { keepWrapping = false; } } polygon.paint(g, pointsWrapped); } pointsWrapped = new ArrayList<>(points); keepWrapping = true; while (keepWrapping) { for (Point p : pointsWrapped) { p.x += mapSize; if (p.x > getWidth()) { keepWrapping = false; } } polygon.paint(g, pointsWrapped); } } } } /** * Moves the visible map pane. * * @param x * horizontal movement in pixel. * @param y * vertical movement in pixel */ public void moveMap(int x, int y) { tileController.cancelOutstandingJobs(); // Clear outstanding load center.x += x; center.y += y; repaint(); this.fireJMVEvent(new JMVCommandEvent(COMMAND.MOVE, this)); } /** * @return the current zoom level */ public int getZoom() { return zoom; } /** * Increases the current zoom level by one */ public void zoomIn() { setZoom(zoom + 1); } /** * Increases the current zoom level by one * @param mapPoint point to choose as center for new zoom level */ public void zoomIn(Point mapPoint) { setZoom(zoom + 1, mapPoint); } /** * Decreases the current zoom level by one */ public void zoomOut() { setZoom(zoom - 1); } /** * Decreases the current zoom level by one * * @param mapPoint point to choose as center for new zoom level */ public void zoomOut(Point mapPoint) { setZoom(zoom - 1, mapPoint); } /** * Set the zoom level and center point for display * * @param zoom new zoom level * @param mapPoint point to choose as center for new zoom level */ public void setZoom(int zoom, Point mapPoint) { if (zoom > tileController.getTileSource().getMaxZoom() || zoom < tileController.getTileSource().getMinZoom() || zoom == this.zoom) return; ICoordinate zoomPos = getPosition(mapPoint); tileController.cancelOutstandingJobs(); // Clearing outstanding load // requests setDisplayPosition(mapPoint, zoomPos, zoom); this.fireJMVEvent(new JMVCommandEvent(COMMAND.ZOOM, this)); } /** * Set the zoom level * * @param zoom new zoom level */ public void setZoom(int zoom) { setZoom(zoom, new Point(getWidth() / 2, getHeight() / 2)); } /** * Every time the zoom level changes this method is called. Override it in * derived implementations for adapting zoom dependent values. The new zoom * level can be obtained via {@link #getZoom()}. * * @param oldZoom the previous zoom level */ protected void zoomChanged(int oldZoom) { zoomSlider.setToolTipText("Zoom level " + zoom); zoomInButton.setToolTipText("Zoom to level " + (zoom + 1)); zoomOutButton.setToolTipText("Zoom to level " + (zoom - 1)); zoomOutButton.setEnabled(zoom > tileController.getTileSource().getMinZoom()); zoomInButton.setEnabled(zoom < tileController.getTileSource().getMaxZoom()); } /** * Determines whether the tile grid is visible or not. * @return {@code true} if the tile grid is visible, {@code false} otherwise */ public boolean isTileGridVisible() { return tileGridVisible; } /** * Sets whether the tile grid is visible or not. * @param tileGridVisible {@code true} if the tile grid is visible, {@code false} otherwise */ public void setTileGridVisible(boolean tileGridVisible) { this.tileGridVisible = tileGridVisible; repaint(); } /** * Determines whether {@link MapMarker}s are painted or not. * @return {@code true} if {@link MapMarker}s are painted, {@code false} otherwise */ public boolean getMapMarkersVisible() { return mapMarkersVisible; } /** * Enables or disables painting of the {@link MapMarker} * * @param mapMarkersVisible {@code true} to enable painting of markers * @see #addMapMarker(MapMarker) * @see #getMapMarkerList() */ public void setMapMarkerVisible(boolean mapMarkersVisible) { this.mapMarkersVisible = mapMarkersVisible; repaint(); } /** * Sets the list of {@link MapMarker}s. * @param mapMarkerList list of {@link MapMarker}s */ public void setMapMarkerList(List mapMarkerList) { this.mapMarkerList = mapMarkerList; repaint(); } /** * Returns the list of {@link MapMarker}s. * @return list of {@link MapMarker}s */ public List getMapMarkerList() { return mapMarkerList; } /** * Sets the list of {@link MapRectangle}s. * @param mapRectangleList list of {@link MapRectangle}s */ public void setMapRectangleList(List mapRectangleList) { this.mapRectangleList = mapRectangleList; repaint(); } /** * Returns the list of {@link MapRectangle}s. * @return list of {@link MapRectangle}s */ public List getMapRectangleList() { return mapRectangleList; } /** * Sets the list of {@link MapPolygon}s. * @param mapPolygonList list of {@link MapPolygon}s */ public void setMapPolygonList(List mapPolygonList) { this.mapPolygonList = mapPolygonList; repaint(); } /** * Returns the list of {@link MapPolygon}s. * @return list of {@link MapPolygon}s */ public List getMapPolygonList() { return mapPolygonList; } /** * Add a {@link MapMarker}. * @param marker map marker to add */ public void addMapMarker(MapMarker marker) { mapMarkerList.add(marker); repaint(); } /** * Remove a {@link MapMarker}. * @param marker map marker to remove */ public void removeMapMarker(MapMarker marker) { mapMarkerList.remove(marker); repaint(); } /** * Remove all {@link MapMarker}s. */ public void removeAllMapMarkers() { mapMarkerList.clear(); repaint(); } /** * Add a {@link MapRectangle}. * @param rectangle map rectangle to add */ public void addMapRectangle(MapRectangle rectangle) { mapRectangleList.add(rectangle); repaint(); } /** * Remove a {@link MapRectangle}. * @param rectangle map rectangle to remove */ public void removeMapRectangle(MapRectangle rectangle) { mapRectangleList.remove(rectangle); repaint(); } /** * Remove all {@link MapRectangle}s. */ public void removeAllMapRectangles() { mapRectangleList.clear(); repaint(); } /** * Add a {@link MapPolygon}. * @param polygon map polygon to add */ public void addMapPolygon(MapPolygon polygon) { mapPolygonList.add(polygon); repaint(); } /** * Remove a {@link MapPolygon}. * @param polygon map polygon to remove */ public void removeMapPolygon(MapPolygon polygon) { mapPolygonList.remove(polygon); repaint(); } /** * Remove all {@link MapPolygon}s. */ public void removeAllMapPolygons() { mapPolygonList.clear(); repaint(); } /** * Sets whether zoom controls are displayed or not. * @param visible {@code true} if zoom controls are displayed, {@code false} otherwise * @deprecated use {@link #setZoomControlsVisible(boolean)} */ @Deprecated public void setZoomContolsVisible(boolean visible) { setZoomControlsVisible(visible); } /** * Sets whether zoom controls are displayed or not. * @param visible {@code true} if zoom controls are displayed, {@code false} otherwise */ public void setZoomControlsVisible(boolean visible) { zoomSlider.setVisible(visible); zoomInButton.setVisible(visible); zoomOutButton.setVisible(visible); } /** * Determines whether zoom controls are displayed or not. * @return {@code true} if zoom controls are displayed, {@code false} otherwise */ public boolean getZoomControlsVisible() { return zoomSlider.isVisible(); } /** * Sets the tile source. * @param tileSource tile source */ public void setTileSource(TileSource tileSource) { if (tileSource.getMaxZoom() > MAX_ZOOM) throw new RuntimeException("Maximum zoom level too high"); if (tileSource.getMinZoom() < MIN_ZOOM) throw new RuntimeException("Minimum zoom level too low"); ICoordinate position = getPosition(); this.tileSource = tileSource; tileController.setTileSource(tileSource); zoomSlider.setMinimum(tileSource.getMinZoom()); zoomSlider.setMaximum(tileSource.getMaxZoom()); tileController.cancelOutstandingJobs(); if (zoom > tileSource.getMaxZoom()) { setZoom(tileSource.getMaxZoom()); } attribution.initialize(tileSource); setDisplayPosition(position, zoom); repaint(); } @Override public void tileLoadingFinished(Tile tile, boolean success) { tile.setLoaded(success); repaint(); } /** * Determines whether the {@link MapRectangle}s are painted or not. * @return {@code true} if the {@link MapRectangle}s are painted, {@code false} otherwise */ public boolean isMapRectanglesVisible() { return mapRectanglesVisible; } /** * Enables or disables painting of the {@link MapRectangle}s. * * @param mapRectanglesVisible {@code true} to enable painting of rectangles * @see #addMapRectangle(MapRectangle) * @see #getMapRectangleList() */ public void setMapRectanglesVisible(boolean mapRectanglesVisible) { this.mapRectanglesVisible = mapRectanglesVisible; repaint(); } /** * Determines whether the {@link MapPolygon}s are painted or not. * @return {@code true} if the {@link MapPolygon}s are painted, {@code false} otherwise */ public boolean isMapPolygonsVisible() { return mapPolygonsVisible; } /** * Enables or disables painting of the {@link MapPolygon}s. * * @param mapPolygonsVisible {@code true} to enable painting of polygons * @see #addMapPolygon(MapPolygon) * @see #getMapPolygonList() */ public void setMapPolygonsVisible(boolean mapPolygonsVisible) { this.mapPolygonsVisible = mapPolygonsVisible; repaint(); } /** * Determines whether scroll wrap is enabled or not. * @return {@code true} if scroll wrap is enabled, {@code false} otherwise */ public boolean isScrollWrapEnabled() { return scrollWrapEnabled; } /** * Sets whether scroll wrap is enabled or not. * @param scrollWrapEnabled {@code true} if scroll wrap is enabled, {@code false} otherwise */ public void setScrollWrapEnabled(boolean scrollWrapEnabled) { this.scrollWrapEnabled = scrollWrapEnabled; repaint(); } /** * Returns the zoom controls apparence style (horizontal/vertical). * @return {@link ZOOM_BUTTON_STYLE#VERTICAL} or {@link ZOOM_BUTTON_STYLE#HORIZONTAL} */ public ZOOM_BUTTON_STYLE getZoomButtonStyle() { return zoomButtonStyle; } /** * Sets the zoom controls apparence style (horizontal/vertical). * @param style {@link ZOOM_BUTTON_STYLE#VERTICAL} or {@link ZOOM_BUTTON_STYLE#HORIZONTAL} */ public void setZoomButtonStyle(ZOOM_BUTTON_STYLE style) { zoomButtonStyle = style; if (zoomSlider == null || zoomInButton == null || zoomOutButton == null) { return; } switch (style) { case VERTICAL: zoomSlider.setBounds(10, 27, 30, 150); zoomInButton.setBounds(14, 8, 20, 20); zoomOutButton.setBounds(14, 176, 20, 20); break; case HORIZONTAL: default: zoomSlider.setBounds(10, 10, 30, 150); zoomInButton.setBounds(4, 155, 18, 18); zoomOutButton.setBounds(26, 155, 18, 18); break; } repaint(); } /** * Returns the tile controller. * @return the tile controller */ public TileController getTileController() { return tileController; } /** * Return tile information caching class * @return tile cache * @see TileController#getTileCache() */ public TileCache getTileCache() { return tileController.getTileCache(); } /** * Sets the tile loader. * @param loader tile loader */ public void setTileLoader(TileLoader loader) { tileController.setTileLoader(loader); } /** * Returns attribution. * @return attribution */ public AttributionSupport getAttribution() { return attribution; } /** * @param listener listener to set */ public void addJMVListener(JMapViewerEventListener listener) { evtListenerList.add(JMapViewerEventListener.class, listener); } /** * @param listener listener to remove */ public void removeJMVListener(JMapViewerEventListener listener) { evtListenerList.remove(JMapViewerEventListener.class, listener); } /** * Send an update to all objects registered with viewer * * @param evt event to dispatch */ private void fireJMVEvent(JMVCommandEvent evt) { Object[] listeners = evtListenerList.getListenerList(); for (int i = 0; i < listeners.length; i += 2) { if (listeners[i] == JMapViewerEventListener.class) { ((JMapViewerEventListener) listeners[i + 1]).processCommand(evt); } } } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/OsmTileLoader.java0000644000000000000000000001465213255753422025020 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import org.openstreetmap.gui.jmapviewer.interfaces.TileJob; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader; import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener; /** * A {@link TileLoader} implementation that loads tiles from OSM. * * @author Jan Peter Stotz */ public class OsmTileLoader implements TileLoader { private static final ThreadPoolExecutor jobDispatcher = (ThreadPoolExecutor) Executors.newFixedThreadPool(8); private final class OsmTileJob implements TileJob { private final Tile tile; private InputStream input; private boolean force; private OsmTileJob(Tile tile) { this.tile = tile; } @Override public void run() { synchronized (tile) { if ((tile.isLoaded() && !tile.hasError()) || tile.isLoading()) return; tile.loaded = false; tile.error = false; tile.loading = true; } try { URLConnection conn = loadTileFromOsm(tile); if (force) { conn.setUseCaches(false); } loadTileMetadata(tile, conn); if ("no-tile".equals(tile.getValue("tile-info"))) { tile.setError("No tile at this zoom level"); } else { input = conn.getInputStream(); try { tile.loadImage(input); } finally { input.close(); input = null; } } tile.setLoaded(true); listener.tileLoadingFinished(tile, true); } catch (IOException e) { tile.setError(e.getMessage()); listener.tileLoadingFinished(tile, false); if (input == null) { try { System.err.println("Failed loading " + tile.getUrl() +": " +e.getClass() + ": " + e.getMessage()); } catch (IOException ioe) { ioe.printStackTrace(); } } } finally { tile.loading = false; tile.setLoaded(true); } } @Override public void submit() { submit(false); } @Override public void submit(boolean force) { this.force = force; jobDispatcher.execute(this); } } /** * Holds the HTTP headers. Insert e.g. User-Agent here when default should not be used. */ public Map headers = new HashMap<>(); public int timeoutConnect; public int timeoutRead; protected TileLoaderListener listener; public OsmTileLoader(TileLoaderListener listener) { this(listener, null); } public OsmTileLoader(TileLoaderListener listener, Map headers) { this.headers.put("Accept", "text/html, image/png, image/jpeg, image/gif, */*"); if (headers != null) { this.headers.putAll(headers); } this.listener = listener; } @Override public TileJob createTileLoaderJob(final Tile tile) { return new OsmTileJob(tile); } protected URLConnection loadTileFromOsm(Tile tile) throws IOException { URL url; url = new URL(tile.getUrl()); URLConnection urlConn = url.openConnection(); if (urlConn instanceof HttpURLConnection) { prepareHttpUrlConnection((HttpURLConnection) urlConn); } return urlConn; } protected void loadTileMetadata(Tile tile, URLConnection urlConn) { String str = urlConn.getHeaderField("X-VE-TILEMETA-CaptureDatesRange"); if (str != null) { tile.putValue("capture-date", str); } str = urlConn.getHeaderField("X-VE-Tile-Info"); if (str != null) { tile.putValue("tile-info", str); } Long lng = urlConn.getExpiration(); if (lng.equals(0L)) { try { str = urlConn.getHeaderField("Cache-Control"); if (str != null) { for (String token: str.split(",")) { if (token.startsWith("max-age=")) { lng = Long.parseLong(token.substring(8)) * 1000 + System.currentTimeMillis(); } } } } catch (NumberFormatException e) { // ignore malformed Cache-Control headers if (JMapViewer.debug) { System.err.println(e.getMessage()); } } } if (!lng.equals(0L)) { tile.putValue("expires", lng.toString()); } } protected void prepareHttpUrlConnection(HttpURLConnection urlConn) { for (Entry e : headers.entrySet()) { urlConn.setRequestProperty(e.getKey(), e.getValue()); } if (timeoutConnect != 0) urlConn.setConnectTimeout(timeoutConnect); if (timeoutRead != 0) urlConn.setReadTimeout(timeoutRead); } @Override public String toString() { return getClass().getSimpleName(); } @Override public boolean hasOutstandingTasks() { return jobDispatcher.getTaskCount() > jobDispatcher.getCompletedTaskCount(); } @Override public void cancelOutstandingTasks() { jobDispatcher.getQueue().clear(); } /** * Sets the maximum number of concurrent connections the tile loader will do * @param num number of conncurent connections */ public static void setConcurrentConnections(int num) { jobDispatcher.setMaximumPoolSize(num); } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/TileXY.java0000644000000000000000000000243213255753422023464 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; /** * @author w * */ public class TileXY { /** * x index of the tile (horizontal) */ private final double x; /** * y number of the tile (vertical) */ private final double y; /** * Returns an instance of coordinates. * * @param x number of the tile * @param y number of the tile */ public TileXY(double x, double y) { this.x = x; this.y = y; } /** * @return x index of the tile as integer */ public int getXIndex() { return x < 0 ? (int) Math.ceil(x) : (int) Math.floor(x); } /** * @return y index of the tile as integer */ public int getYIndex() { return y < 0 ? (int) Math.ceil(y) : (int) Math.floor(y); } /** * @return x index as double, might be non integral, when the point is not topleft corner of the tile */ public double getX() { return x; } /** * @return y index as double, might be non integral, when the point is not topleft corner of the tile */ public double getY() { return y; } @Override public String toString() { return "TileXY{" + x + ", " + y + "}"; } } jmapviewer-2.7/src/org/openstreetmap/gui/jmapviewer/TileRange.java0000644000000000000000000000261113255753422024157 0ustar rootroot// License: GPL. For details, see Readme.txt file. package org.openstreetmap.gui.jmapviewer; /** * This is a rectangular range of tiles. */ public class TileRange { protected int minX; protected int maxX; protected int minY; protected int maxY; protected int zoom; protected TileRange() { } /** * Constructs a new {@code TileRange}. * @param t1 first tile * @param t2 second tile * @param zoom zoom level */ public TileRange(TileXY t1, TileXY t2, int zoom) { minX = (int) Math.floor(Math.min(t1.getX(), t2.getX())); minY = (int) Math.floor(Math.min(t1.getY(), t2.getY())); maxX = (int) Math.ceil(Math.max(t1.getX(), t2.getX())); maxY = (int) Math.ceil(Math.max(t1.getY(), t2.getY())); this.zoom = zoom; } /** * Constructs a new {@code TileRange}. * @param r existing tile range to copy */ public TileRange(TileRange r) { minX = r.minX; minY = r.minY; maxX = r.maxX; maxY = r.maxY; zoom = r.zoom; } protected double tilesSpanned() { return Math.sqrt(1.0 * this.size()); } /** * Returns size * @return size */ public int size() { int xSpan = maxX - minX + 1; int ySpan = maxY - minY + 1; return xSpan * ySpan; } }