All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.limetri.client.mapviewer.swing.JXMapViewer Maven / Gradle / Ivy

/**
 *  Copyright (C) 2008-2013 LimeTri. All rights reserved.
 *
 *  AgroSense 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 3 of the License, or (at your option) any later
 *  version.
 *
 *  There are special exceptions to the terms and conditions of the GPLv3 as it
 *  is applied to this software, see the FLOSS License Exception
 *  .
 *
 *  AgroSense 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
 *  AgroSense. If not, see .
 */
package eu.limetri.client.mapviewer.swing;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.beans.DesignMode;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;

import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.painter.AbstractPainter;
import org.jdesktop.swingx.painter.Painter;

import eu.limetri.client.mapviewer.data.AbstractMapViewer;
import eu.limetri.client.mapviewer.data.GeoPosition;
import eu.limetri.client.mapviewer.data.MapViewer;
import eu.limetri.client.mapviewer.data.RepaintCallback;
import eu.limetri.client.mapviewer.data.Tile;
import eu.limetri.client.mapviewer.data.TileFactory;
import eu.limetri.client.mapviewer.data.TileFactoryInfo;
import eu.limetri.client.mapviewer.data.common.TileFactoryInfoSelectionEventHandler;
import eu.limetri.client.mapviewer.data.util.ScaleUtil;
import eu.limetri.client.mapviewer.data.util.ScaleUtil.UnitSystem;
import eu.limetri.client.mapviewer.swing.empty.EmptyTileFactory;
import eu.limetri.client.mapviewer.swing.impl.OfflineTileFactorySwing;
import eu.limetri.client.mapviewer.swing.input.CenterMapListener;
import eu.limetri.client.mapviewer.swing.input.PanKeyListener;
import eu.limetri.client.mapviewer.swing.input.PanMouseInputListener;
import eu.limetri.client.mapviewer.swing.input.ZoomMouseWheelListenerCursor;

/**
 * A tile oriented map component that can easily be used with tile sources on
 * the web like Google and Yahoo maps, satellite data such as NASA imagery, and
 * also with file based sources like pre-processed NASA images. A known map
 * provider can be used with the SLMapServerInfo, which will connect to a 2km
 * resolution version of NASA's Blue Marble Next Generation imagery. @see
 * SLMapServerInfo for more information.
 * 
 * Note, the JXMapViewer has three center point properties. The
 * addressLocation property represents an abstract center of the map.
 * This would usually be something like the first item in a search result. It is
 * a {@link GeoPosition}. The centerPosition property represents the
 * current center point of the map. If the user pans the map then the
 * centerPosition point will change but the addressLocation will not.
 * Calling recenterToAddressLocation() will move the map back to that
 * center address. The center property represents the same point as the
 * centerPosition property, but as a Point2D in pixel space instead of a
 * GeoPosition in lat/long space. Note that the center property is a Point2D in
 * the entire world bitmap, not in the portion of the map currently visible. You
 * can use the getViewportBounds() method to find the portion of the map
 * currently visible and adjust your calculations accordingly. Changing the
 * center property will change the centerPosition property and
 * vice versa. All three properties are bound.
 * 
 * @author [email protected]
 * @see eu.limetri.client.mapviewer.data.bmng.SLMapServerInfo
 */
public class JXMapViewer extends JXPanel implements DesignMode, MapViewer> {

	private static final long serialVersionUID = 1L;

	public static final int MAX_ZOOM = 17;
	
	public static final int MIN_ZOOM = 1;
	
	public static final String ZOOM = "zoom";
	
	/**
	 * Indicates whether the component should recenter the map when the "middle"
	 * mouse button is pressed
	 */
	private boolean recenterOnClickEnabled = true;

	/**
	 * The overlay to delegate to for painting the "foreground" of the map
	 * component. This would include painting waypoints, day/night, etc. Also
	 * receives mouse events.
	 */
	private Painter overlay;

	private boolean designTime;

	private Image loadingImage;

	private AbstractMapViewer> abstractMapViewer;

	private boolean setZoomFlag = false;
	
	private boolean zoomEnabled = false;
	
	private boolean typeSelectionEnabled = false;
	
	private MapTypeSelectionPanelSwing typeSelectionPanel;

	// a property change listener which forces repaints when tiles finish
	// loading
	private TileLoadListener tileLoadListener = new TileLoadListener();

	private JPanel rightPanel;

	private ZoomPanel zoomPanel;

	private Point oldStartPoint;

	private Point oldEndPoint;
        
        private CenterMapListener centerMapListener;
	
	/**
	 * Create a new JXMapViewer. By default it will use the EmptyTileFactory
	 */
	public JXMapViewer() {
		setLayout(new BorderLayout());
		initMapViewer();
		initInputListeners();
		
		rightPanel = new JPanel(new GridBagLayout());
		rightPanel.setOpaque(false);
		
		GridBagConstraints gc = new GridBagConstraints();
		gc.gridx = 0;
		gc.gridy = 10;
		gc.weighty = 1;
		gc.weightx = 1;
		
		//TODO replace with miglayout
		JPanel fillPanel = new JPanel();
		fillPanel.setOpaque(false);
		rightPanel.add(fillPanel, gc);
		
		add(rightPanel, BorderLayout.EAST);

		
		abstractMapViewer.setTileFactory(new EmptyTileFactory());
		setZoomEnabled(true);
		
		// make a dummy loading image
		try {
			URL url = this.getClass().getResource(
					"mapviewer/resources/loading.png");
			this.setLoadingImage(ImageIO.read(url));
		} catch (Throwable ex) {
			BufferedImage img = new BufferedImage(16, 16,
					BufferedImage.TYPE_INT_ARGB);
			Graphics2D g2 = img.createGraphics();
			g2.setColor(Color.black);
			g2.fillRect(0, 0, 16, 16);
			g2.dispose();
			this.setLoadingImage(img);
		}

		setBackgroundPainter(new AbstractPainter() {
			protected void doPaint(Graphics2D g, JXPanel component, int width,
					int height) {
				doPaintComponent(g);
			}
		});
		
		abstractMapViewer.addRepaintCallback(new RepaintCallback() {
			
			@Override
			public void repaint() {
				//TODO right panel is blinking while repainting
				rightPanel.repaint();
			}
		});
		

	}

	protected void initInputListeners() {
		MouseInputListener mia = new PanMouseInputListener(this);
                centerMapListener = new CenterMapListener(this);
		this.addMouseListener(mia);
		this.addMouseMotionListener(mia);
		this.addMouseListener(centerMapListener);
		addMouseWheelListener(new ZoomMouseWheelListenerCursor(this));
		addKeyListener(new PanKeyListener(this));
	}
	
	private void initMapViewer() {
		abstractMapViewer = new AbstractMapViewer>() {

			@Override
			protected void repaint() {
				JXMapViewer.this.repaint();
			}

			@Override
			protected int getWidth() {
				return JXMapViewer.this.getWidth();
			}

			@Override
			protected Insets getInsets() {
				return JXMapViewer.this.getInsets();
			}

			@Override
			protected int getHeight() {
				return JXMapViewer.this.getHeight();
			}

			@Override
			protected void firePropertyChange(String propertyName,
					Object oldValue, Object newValue) {
				JXMapViewer.this.firePropertyChange(propertyName, oldValue,
						newValue);
			}
		};
	}

	private void doPaintComponent(Graphics g) {
		if (!isDesignTime()) {
			//correcting bounds of map
			Point2D center = abstractMapViewer.getCenter();
			int viewerHeight = getHeight();
            int maxHeight = (int) (getTileFactory().getMapSize(getZoom()).getHeight() * getTileFactory().getTileSize(getZoom()));
			
			double y = center.getY();
            
			if (y < (viewerHeight/2)) {
            	y = viewerHeight / 2;
            }
            
            if (y > maxHeight - (viewerHeight/2)) {
            	y = maxHeight - (viewerHeight/2);
            }
            
            if (y != center.getY()) {
            	abstractMapViewer.setCenter(new Point2D.Double(center.getX(), y));
            }
            
			//
			int zoom = getZoom();
			Rectangle viewportBounds = abstractMapViewer.getViewportBounds();
			drawMapTiles(g, zoom, viewportBounds);
			drawOverlays(zoom, g, viewportBounds);
			drawScale(g);
		}

		super.paintBorder(g);
	}

	/**
	 * Indicate that the component is being used at design time, such as in a
	 * visual editor like NetBeans' Matisse
	 * 
	 * @param b
	 *            indicates if the component is being used at design time
	 */
	public void setDesignTime(boolean b) {
		this.designTime = b;
	}

	/**
	 * Indicates whether the component is being used at design time, such as in
	 * a visual editor like NetBeans' Matisse
	 * 
	 * @return boolean indicating if the component is being used at design time
	 */
	public boolean isDesignTime() {
		return designTime;
	}

	/**
	 * Draw the map tiles. This method is for implementation use only.
	 * 
	 * @param g
	 *            Graphics
	 * @param zoom
	 *            zoom level to draw at
	 * @param viewportBounds
	 *            the bounds to draw within
	 */
	protected void drawMapTiles(final Graphics g, final int zoom,
			Rectangle viewportBounds) {
		
		int size = getTileFactory().getTileSize(zoom);
		Dimension mapSize = getTileFactory().getMapSize(zoom);

		// calculate the "visible" viewport area in tiles
		int numWide = viewportBounds.width / size + 2;
		int numHigh = viewportBounds.height / size + 2;

		TileFactoryInfo info = getTileFactory().getInfo();
		int tpx = (int) Math.floor(viewportBounds.getX() / info.getTileSize(0));
		int tpy = (int) Math.floor(viewportBounds.getY() / info.getTileSize(0));

		// fetch the tiles from the factory and store them in the tiles cache
		// attach the tileLoadListener
		for (int x = 0; x <= numWide; x++) {
			for (int y = 0; y <= numHigh; y++) {
				int itpx = x + tpx;// topLeftTile.getX();
				int itpy = y + tpy;// topLeftTile.getY();
				// only proceed if the specified tile point lies within the area
				// being painted
				if (g.getClipBounds().intersects(new Rectangle(itpx * size - viewportBounds.x, itpy	* size - viewportBounds.y, size, size))) {
					
					Tile tile = getTileFactory().getTile(itpx, itpy, zoom);
					
					if (!tile.isLoaded()) {
						tile.addUniquePropertyChangeListener("loaded", tileLoadListener); // this is a filthy hack
					}
					
					int ox = ((itpx * getTileFactory().getTileSize(zoom)) - viewportBounds.x);
					int oy = ((itpy * getTileFactory().getTileSize(zoom)) - viewportBounds.y);

					// if the tile is off the map to the north/south, then just
					// don't paint anything
					if (isTileOnMap(itpx, itpy, mapSize)) {
						if (isOpaque()) {
							g.setColor(getBackground());
							g.fillRect(ox, oy, size, size);
						}
					} else if (tile.isLoaded()) {
						g.drawImage(tile.getImage(), ox, oy, null);
					} else {
						//show resized image while loading
						Tile superTile = getTileFactory().getTile(itpx / 2, itpy / 2, zoom + 1);
						
						if (superTile.isLoaded()) {
							
							int offX = (itpx % 2) * size / 2;
							int offY = (itpy % 2) * size / 2;
							g.drawImage(superTile.getImage(), ox, oy, ox + size, oy + size, offX, offY, offX + size / 2, offY + size / 2, null);
							
						} else {
						
							int imageX = (getTileFactory().getTileSize(zoom) - getLoadingImage().getWidth(null)) / 2;
							int imageY = (getTileFactory().getTileSize(zoom) - getLoadingImage().getHeight(null)) / 2;
							g.setColor(Color.GRAY);
							g.fillRect(ox, oy, size, size);
							g.drawImage(getLoadingImage(), ox + imageX,	oy + imageY, null);
						}
					}
					
				}
			}
		}
	}

	private void drawOverlays(final int zoom, final Graphics g,
			final Rectangle viewportBounds) {
		if (overlay != null) {
			overlay.paint((Graphics2D) g, this, getWidth(), getHeight());
		}
	}

	private boolean isTileOnMap(int x, int y, Dimension mapSize) {
		return y >= mapSize.getHeight();
	}

	/**
	 * Sets the map overlay. This is a Painter which will paint on top of the
	 * map. It can be used to draw waypoints, lines, or static overlays like
	 * text messages.
	 * 
	 * @param overlay
	 *            the map overlay to use
	 * @see org.jdesktop.swingx.painters.Painter
	 */
	public void setOverlayPainter(Painter overlay) {
		Painter old = getOverlayPainter();
		this.overlay = overlay;
		firePropertyChange("mapOverlay", old, getOverlayPainter());
		repaint();
	}

	/**
	 * Gets the current map overlay
	 * 
	 * @return the current map overlay
	 */
	public Painter getOverlayPainter() {
		return overlay;
	}


	/**
	 * Sets whether the map should recenter itself on mouse clicks (middle mouse
	 * clicks?)
	 * 
	 * @param b
	 *            if should recenter
	 */
	public void setRecenterOnClickEnabled(boolean b) {
		boolean old = isRecenterOnClickEnabled();
		recenterOnClickEnabled = b;
		firePropertyChange("recenterOnClickEnabled", old,
				isRecenterOnClickEnabled());
	}

	/**
	 * Indicates if the map should recenter itself on mouse clicks.
	 * 
	 * @return boolean indicating if the map should recenter itself
	 */
	public boolean isRecenterOnClickEnabled() {
		return recenterOnClickEnabled;
	}

	
	/**
	 * A property for an image which will be display when an image is still
	 * loading.
	 * 
	 * @return the current property value
	 */
	public Image getLoadingImage() {
		return loadingImage;
	}

	/**
	 * A property for an image which will be display when an image is still
	 * loading.
	 * 
	 * @param loadingImage
	 *            the new property value
	 */
	public void setLoadingImage(Image loadingImage) {
		this.loadingImage = loadingImage;
	}

	
	private final class TileLoadListener implements PropertyChangeListener {
		public void propertyChange(PropertyChangeEvent evt) {
			if (Tile.LOADED.equals(evt.getPropertyName()) && Boolean.TRUE.equals(evt.getNewValue())) {
				Object source = evt.getSource();
				if (source instanceof Tile) {
					Tile t = (Tile) evt.getSource();
					if (t.getZoom() == getZoom()) {
						repaint();
					}
				}
			}
		}
	}

	private void setZoomInternal(int zoom) {
		int oldZoom = abstractMapViewer.getZoom();
		abstractMapViewer.setZoom(zoom);
		setZoomFlag = true;
		firePropertyChange(ZOOM, oldZoom, zoom);
		setZoomFlag = false;
	}

	/**
	 * Set {@link TileFactory}
	 * 
	 * @param factory
	 */
	public void setTileFactory(TileFactory> factory) {
            
            TileFactory> oldTileFactory = abstractMapViewer.getTileFactory();
            if (oldTileFactory != null) {
                oldTileFactory.clearQueueAndStopLoading();
            }
                    
            abstractMapViewer.setTileFactory(factory);
		//TODO clean up tileMap and imageMap
		repaint();
	}

	/**
	 * Set zoom level
	 * 
	 * @param zoom
	 */
	public void setZoom(int zoom) {
		setZoomInternal(zoom);
	}

	/**
	 * Set {@link GeoPosition} address of the center of the map
	 * 
	 * @param addressLocation
	 */
	public void setAddressLocation(GeoPosition addressLocation) {
		abstractMapViewer.setAddressLocation(addressLocation);
	}

	@Override
	public TileFactory> getTileFactory() {
		return abstractMapViewer.getTileFactory();
	}

	@Override
	public int getZoom() {
		return abstractMapViewer.getZoom();
	}

	@Override
	public Rectangle getViewportBounds() {
		return abstractMapViewer.getViewportBounds();
	}

	/**
	 * Gets center of the map in pixel coordinates
	 * @return
	 */
	public Point2D getCenter() {
		return abstractMapViewer.getCenter();
	}

	/**
	 * Sets the new center of the map in pixel coordinates.
	 * 
	 * @param center  the new center of the map in pixel coordinates
	 */
	public void setCenter(Point2D center) {
		abstractMapViewer.setCenter(center);
	}

	/**
	 * Sets center position of the map
	 * 
	 * @param geoPosition center position of the map
	 */
	public void setCenterPosition(GeoPosition geoPosition) {
		abstractMapViewer.setCenterPosition(geoPosition);
	}

	/**
	 * Gets center position of the map
	 * @return
	 */
	public GeoPosition getCenterPosition() {
		return abstractMapViewer.getCenterPosition();
	}

	 /**
     * Gets the current address location of the map. This property does not change when the user
     * pans the map. This property is bound.
     * @return the current map location (address)
     */
	public GeoPosition getAddressLocation() {
		return abstractMapViewer.getAddressLocation();
	}

	public void setRestrictOutsidePanning(boolean restrict) {
		abstractMapViewer.setRestrictOutsidePanning(restrict);
	}

	public void setHorizontalWrapped(boolean wrapped) {
		abstractMapViewer.setHorizontalWrapped(wrapped);
	}

	/**
	 * Paints zoom selection area rectangle
	 * 
	 * @param startPoint
	 * @param endPoint
	 */
	public void markZoomArea(Point startPoint, Point endPoint) {
		
		Graphics2D g = (Graphics2D) getGraphics();
		
		if (oldStartPoint != null && oldEndPoint != null) {
			Rectangle rect = new Rectangle(Math.min(oldStartPoint.x, oldEndPoint.x)-1, Math.min(oldStartPoint.y, oldEndPoint.y)-1, Math.max(oldStartPoint.x, oldEndPoint.x), Math.max(oldStartPoint.y, oldEndPoint.y));
			if (rect.x < 0) {
				rect.x = 0;
			}
			if (rect.y < 0) {
				rect.y = 0;
			}
			
			//TODO zoom components blink and hide part of selection rectangle when those intersect - should be fixed by using different layout manager
			g.setClip(rect.x, rect.y, rect.width, rect.height);
			doPaintComponent(g);
		}
		
		abstractMapViewer.fireRepaintCallbacks();
		
		g.setColor(Color.RED); //TODO LAF
		g.setStroke(new BasicStroke(3));
		g.drawRect(Math.min(startPoint.x, endPoint.x), Math.min(startPoint.y, endPoint.y), Math.abs(endPoint.x - startPoint.x), Math.abs(endPoint.y - startPoint.y));
		
		oldStartPoint = startPoint;
		oldEndPoint = endPoint;
	}

	/**
	 * Zoom the map to specified area
	 * 
	 * @param start
	 * @param end
	 */
	public void zoomToArea(Point start, Point end) {
		abstractMapViewer.zoomToCoordinates(start.x, start.y, end.x, end.y);
	}
	
	public void setZoomEnabled(boolean zoomEnabled) {
		if (this.zoomEnabled != zoomEnabled) {
			this.zoomEnabled= zoomEnabled;
			if (zoomEnabled) {
				enableZoom();
			} else {
				disableZoom();
			}
		}
	}
	
	public boolean isZoomEnabled() {
		return zoomEnabled;
	}
	
	protected void enableZoom() {
		zoomPanel = new ZoomPanel(this);
		zoomPanel.attach();
		
		GridBagConstraints gc = new GridBagConstraints();
		gc.gridx = 1;
		gc.gridy = 1;
		gc.weightx = 0;
		
		rightPanel.add(zoomPanel, gc);
	}
	
	protected void disableZoom() {
		zoomPanel.detach();
		rightPanel.remove(zoomPanel);
	}
	
	boolean isSetZoomFlag() {
		return setZoomFlag;
	}
	
	
	public void setTypeSelectionEnabled(boolean typeSelectionEnabled, TileFactoryInfo...tileFactoryInfos) {
		if (this.typeSelectionEnabled != typeSelectionEnabled) {
			this.typeSelectionEnabled= typeSelectionEnabled;
			if (typeSelectionEnabled) {
				enableTypeSelection(tileFactoryInfos);
			} else {
				disableTypeSelection();
			}
		}
	}
	
	private void disableTypeSelection() {
		rightPanel.remove(typeSelectionPanel);
	}

	private void enableTypeSelection(TileFactoryInfo...tileFactoryInfos) {
		typeSelectionPanel = new MapTypeSelectionPanelSwing();
		typeSelectionPanel.setItems(tileFactoryInfos);
		
		GridBagConstraints gc = new GridBagConstraints();
		gc.gridx = 0;
		gc.gridy = 0;
		gc.gridwidth = 2;
		
		rightPanel.add(typeSelectionPanel, gc);
		
		typeSelectionPanel.setOnSelectionChange(new TileFactoryInfoSelectionEventHandler() {
			
			@Override
			public void itemSelected(TileFactoryInfo tileFactoryInfo) {
				setTileFactory(new OfflineTileFactorySwing(tileFactoryInfo));
			}
			
		});
		typeSelectionPanel.setActiveTileFactoryInfo(getTileFactory().getInfo());
	}

	public boolean isTypeSelectionEnabled() {
		return typeSelectionEnabled;
	}
        
        public void setZoomFromGeoPosition(GeoPosition start, GeoPosition end) {
		abstractMapViewer.setZoomFromGeoPosition(start, end);
	}
        
        public void setDoubleClickZoomEnabled(boolean enabled) {
            centerMapListener.setEnabled(enabled);
        }
        
        public boolean isDoubleClickZoomEnabled() {
            return centerMapListener.isEnabled();
        }
        
        private void drawScale(Graphics g) {
    		long scaleValue = abstractMapViewer.getScaleValue(); 
    		int width = (int) (scaleValue * abstractMapViewer.getPixelToMeter() * ScaleUtil.getUnitToMetersRatio());
    		int xBase = 30;
    		int yBase = 30;
    		
    		UnitSystem unitSystem = ScaleUtil.getUnitSystem();
    		
    		String title = null;
    		
    		if (UnitSystem.METRIC.equals(unitSystem)) {
    			if (scaleValue >= 1000) {
    				title = String.format("%dkm", scaleValue / 1000);
    			} else {
    				title = String.format("%dm", scaleValue);
    			}
    		} else if (UnitSystem.IMPERIAL.equals(unitSystem)) {
    			if (scaleValue >= 5280) {
    				title = String.format("%dmi", scaleValue / 5280);
    			} else {
    				title = String.format("%dft", scaleValue);
    			}
    		}
    		
    		int h = getHeight();

    		g.setColor(Color.WHITE);
    		g.drawLine(xBase + 4, h-yBase, xBase - 4 + width, h-yBase);
    		g.drawLine(xBase, h-yBase + 3, xBase + width, h-yBase + 3);
    		
    		g.drawLine(xBase, h-yBase + 2, xBase, h-yBase - 5);
    		g.drawLine(xBase, h-yBase - 5, xBase + 3, h- yBase - 5);
    		g.drawLine(xBase + 3, h-yBase-5, xBase + 3, h-yBase);
    		
    		g.drawLine(xBase - 3 + width, h-yBase, xBase - 3 + width, h- yBase - 5);
    		g.drawLine(xBase - 3 + width, h- yBase -5, xBase + width, h- yBase - 5);
    		g.drawLine(xBase + width, h- yBase -5, xBase + width, h-yBase + 2);
    		
    		g.setColor(Color.BLACK);
    		g.drawLine(xBase + 1, h-yBase + 1, xBase - 1 + width, h- yBase + 1);
    		g.drawLine(xBase + 1, h-yBase + 2, xBase - 1 + width, h-yBase + 2);
    		
    		g.drawLine(xBase + 1, h-yBase + 1, xBase + 1, h-yBase - 4);
    		g.drawLine(xBase + 2, h-yBase + 1, xBase + 2, h-yBase-4);
    		g.drawLine(xBase - 1 + width, h-yBase+1, xBase - 1 + width, h-yBase-4);
    		g.drawLine(xBase - 2 + width, h-yBase+1, xBase - 2 + width, h-yBase-4);
    		
    		
    		g.setColor(Color.BLACK);
    		g.drawString(title, xBase + 10 , h-yBase-5);
    	}
        
    public GeoPosition[] getScreenCoordinates() {
        int width = getWidth();
        int height = getHeight();
        
        Point2D.Double point = new Point2D.Double(0, 0);
        GeoPosition positionNW = abstractMapViewer.convertPointToGeoPosition(point);
        
        point = new Point2D.Double(width, 0);
        GeoPosition positionNE = abstractMapViewer.convertPointToGeoPosition(point);
        
        point = new Point2D.Double(width, height);
        GeoPosition positionSE = abstractMapViewer.convertPointToGeoPosition(point);
        
        point = new Point2D.Double(0, height);
        GeoPosition positionSW = abstractMapViewer.convertPointToGeoPosition(point);
        
        GeoPosition[] cornerPositions = new GeoPosition[]{positionNW, positionNE, positionSE, positionSW, positionNW}; 
        return cornerPositions;
    }    
        
    @Override
    public void addNotify() {
        super.addNotify(); 
        MapViewerLookup.getInstance().addObject(this);
    }

    @Override
    public void removeNotify() {
        super.removeNotify(); 
        MapViewerLookup.getInstance().removeObject(this);
    }
        
    public Point2D convertGeoPositionToPoint(GeoPosition pos) {
        return abstractMapViewer.convertGeoPositionToPoint(pos);
    }

    public GeoPosition convertPointToGeoPosition(Point2D pt) {
        return abstractMapViewer.convertPointToGeoPosition(pt);
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy