/*
* Copyright (C) 2014 Alec Dhuse
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.foldingmap.map.raster;
import co.foldingmap.map.Overlay;
import co.foldingmap.map.MapView;
import co.foldingmap.map.MapObject;
import co.foldingmap.map.Visibility;
import co.foldingmap.map.Layer;
import co.foldingmap.map.vector.LatLonBox;
import co.foldingmap.map.vector.LatLonAltBox;
import co.foldingmap.map.vector.CoordinateList;
import co.foldingmap.map.vector.Coordinate;
import co.foldingmap.map.vector.MapIcon;
import co.foldingmap.map.themes.ColorStyle;
import co.foldingmap.xml.XmlOutput;
import java.awt.*;
import java.awt.Polygon;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.ImageIcon;
/**
*
* @author Alec
*/
public class ImageOverlay extends Overlay implements MapObject {
protected BasicStroke points, centerStroke, centerOutline;
protected boolean highlighted;
protected Coordinate center, ne, nw, se, sw;
protected Coordinate centerB, neB, nwB, seB, swB;
protected Coordinate selectedCoordinate;
protected double rotation;
protected float centerX, centerY;
protected Layer parentLayer;
protected MapIcon mapIcon;
protected MapView lastMapView;
protected int height, width;
protected int[] pointsX, pointsY;
protected Polygon screenBounds;
/**
* Constructor for 2D ImageOverlay.
*
* @param id
* @param mapIcon
* @param bounds
*/
public ImageOverlay(String id, MapIcon mapIcon, LatLonBox bounds) {
this.overlayID = id;
this.mapIcon = mapIcon;
this.rotation = 0;
this.pointsX = new int[4];
this.pointsY = new int[4];
points = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
centerStroke = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
centerOutline = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
setBoundary(new LatLonAltBox(bounds));
}
/**
* Constructor for 3D ImageOverlay.
*
* @param id
* @param mapIcon
* @param bounds
*/
public ImageOverlay(String id, MapIcon mapIcon, LatLonAltBox bounds) {
this.overlayID = id;
this.mapIcon = mapIcon;
this.rotation = 0;
this.pointsX = new int[4];
this.pointsY = new int[4];
points = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
centerStroke = new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
centerOutline = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
setBoundary(bounds);
}
/**
* Converts the objects latitude and longitude coordinates screen x,y points.
*
* @param mapView
*/
protected void convertCoordinatesToLines(MapView mapView) {
float heightWidthRatio;
float latDiff, lonDiff;
float tempHeight, tempWidth;
int x, y;
if (!center.equals(centerB)) {
}
if (!nw.equals(nwB)) {
x = (int) mapView.getX(nwB, MapView.NO_WRAP);
y = (int) mapView.getY(nwB);
tempHeight = (pointsY[3] - y) * mapIcon.getHeightWidthRatio();
tempWidth = (pointsX[1] - x) / mapIcon.getHeightWidthRatio();
heightWidthRatio = tempHeight / tempWidth;
nwB = nw.copy();
ne.setLatitude((float) nw.getLatitude());
sw.setLongitude((float) nw.getLongitude());
center = getCenter();
centerB = center.copy();
}
if (!ne.equals(neB)) {
neB = ne.copy();
nw.setLatitude((float) ne.getLatitude());
se.setLongitude((float) ne.getLongitude());
center = getCenter();
centerB = center.copy();
}
if (!se.equals(seB)) {
seB = se.copy();
sw.setLatitude((float) se.getLatitude());
ne.setLongitude((float) se.getLongitude());
center = getCenter();
centerB = center.copy();
}
if (!sw.equals(swB)) {
swB = sw.copy();
se.setLatitude((float) sw.getLatitude());
nw.setLongitude((float) sw.getLongitude());
center = getCenter();
centerB = center.copy();
}
//accomidate for changes in the center coordinate.
if (!center.equals(centerB)) {
latDiff = (float) (centerB.getLatitude() - center.getLatitude());
lonDiff = (float) (centerB.getLongitude() - center.getLongitude());
nw.setLatitude((float) nw.getLatitude() - latDiff);
ne.setLatitude((float) ne.getLatitude() - latDiff);
se.setLatitude((float) se.getLatitude() - latDiff);
sw.setLatitude((float) sw.getLatitude() - latDiff);
nw.setLongitude((float) nw.getLongitude() - lonDiff);
ne.setLongitude((float) ne.getLongitude() - lonDiff);
se.setLongitude((float) se.getLongitude() - lonDiff);
sw.setLongitude((float) sw.getLongitude() - lonDiff);
centerB = center.copy();
swB = sw.copy();
seB = se.copy();
neB = ne.copy();
nwB = nw.copy();
}
pointsX[0] = (int) mapView.getX(nw, MapView.NO_WRAP);
pointsX[1] = (int) mapView.getX(ne, MapView.NO_WRAP);
pointsX[2] = (int) mapView.getX(se, MapView.NO_WRAP);
pointsX[3] = (int) mapView.getX(sw, MapView.NO_WRAP);
centerX = mapView.getX(center, MapView.NO_WRAP);
pointsY[0] = (int) mapView.getY(nw);
pointsY[1] = (int) mapView.getY(nw);
pointsY[2] = (int) mapView.getY(se);
pointsY[3] = (int) mapView.getY(sw);
centerY = mapView.getY(center);
height = Math.abs(pointsY[3] - pointsY[0]);
width = Math.abs(pointsX[1] - pointsX[0]);
if (width > 0) {
heightWidthRatio = (height / (float) width);
} else {
heightWidthRatio = 0;
}
//make sure to keep the images height-width ratio
if (heightWidthRatio != mapIcon.getHeightWidthRatio()) {
}
}
/**
* Returns a generic MapObject copy of this ImageOverlay.
*
* @return
*/
@Override
public MapObject copy() {
MapObject object;
object = new ImageOverlay(overlayID, mapIcon.copy(), getBounds());
return object;
}
/**
* Returns a copy of this ImageOverlay
*
* @return
*/
@Override
public Overlay copyOverlay() {
ImageOverlay groundOverlay;
groundOverlay = new ImageOverlay(overlayID, mapIcon.copy(), getBounds());
return groundOverlay;
}
/**
* Draws this object.
*
* @param g2
* @param mapView
*/
@Override
public void drawObject(Graphics2D g2, MapView mapView) {
lastMapView = mapView;
drawObject(g2, mapView, null);
}
/**
* Draws this overlay object on the given Graphics class using the
* given MapView.
*
* @param g2
* @param mapView
*/
@Override
public void drawObject(Graphics2D g2, MapView mapView, ColorStyle colorStyle) {
ImageIcon imageIcon;
lastMapView = mapView;
if (rotation != 0) {
}
convertCoordinatesToLines(mapView);
screenBounds = new Polygon(pointsX, pointsY, 4);
imageIcon = mapIcon.getImageIcon();
if (imageIcon != null)
g2.drawImage(imageIcon.getImage(), pointsX[0], pointsY[1], width, height, imageIcon.getImageObserver());
}
/**
* Draws the control points for this ImageOverlay.
*
* @param g2
* @param mapView
*/
@Override
public void drawPoints(Graphics2D g2, MapView mapView) {
Ellipse2D centerPoint;
Rectangle2D rect1, rect2, rect3, rect4;
convertCoordinatesToLines(mapView);
rect1 = new Rectangle2D.Double((pointsX[0] - 0), (pointsY[0] - 0), 4, 4);
rect2 = new Rectangle2D.Double((pointsX[1] - 4), (pointsY[1] - 0), 4, 4);
rect3 = new Rectangle2D.Double((pointsX[2] - 4), (pointsY[2] - 4), 4, 4);
rect4 = new Rectangle2D.Double((pointsX[3] - 0), (pointsY[3] - 4), 4, 4);
centerPoint = new Ellipse2D.Float(centerX - 10, centerY - 10, 20, 20);
g2.setStroke(points);
//draw outlines
g2.setColor(Color.BLACK);
g2.draw(rect1);
g2.draw(rect2);
g2.draw(rect3);
g2.draw(rect4);
g2.draw(centerPoint);
if (selectedCoordinate.equals(nw)) {
g2.fill(rect2);
g2.fill(rect3);
g2.fill(rect4);
g2.setColor(Color.WHITE);
g2.fill(rect1);
} else if (selectedCoordinate.equals(ne)) {
g2.fill(rect1);
g2.fill(rect3);
g2.fill(rect4);
g2.setColor(Color.WHITE);
g2.fill(rect2);
} else if (selectedCoordinate.equals(se)) {
g2.fill(rect1);
g2.fill(rect2);
g2.fill(rect4);
g2.setColor(Color.WHITE);
g2.fill(rect3);
} else if (selectedCoordinate.equals(sw)) {
g2.fill(rect1);
g2.fill(rect2);
g2.fill(rect3);
g2.setColor(Color.WHITE);
g2.fill(rect4);
} else if (selectedCoordinate.equals(center)) {
g2.fill(rect1);
g2.fill(rect2);
g2.fill(rect3);
g2.fill(rect4);
g2.setStroke(centerOutline);
g2.setColor(new Color(255,255,255,128));
g2.draw(centerPoint);
g2.setStroke(centerStroke);
g2.setColor(new Color(68,68,68,128));
g2.draw(centerPoint);
}
}
/**
* Returns if this ImageOverlay is equal to another object.
*
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (o instanceof ImageOverlay) {
ImageOverlay io = (ImageOverlay) o;
return (this.hashCode() == io.hashCode());
} else {
return false;
}
}
/**
* Generates a hashcode for this ImageOverlay.
*
* @return
*/
@Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + (this.ne != null ? this.ne.hashCode() : 0);
hash = 89 * hash + (this.nw != null ? this.nw.hashCode() : 0);
hash = 89 * hash + (this.se != null ? this.se.hashCode() : 0);
hash = 89 * hash + (this.sw != null ? this.sw.hashCode() : 0);
hash = 89 * hash + (this.mapIcon != null ? this.mapIcon.hashCode() : 0);
return hash;
}
/**
* Returns the boundary for this ImagEOverlay.
*
* @return
*/
public LatLonAltBox getBounds() {
return new LatLonAltBox((float) nw.getLatitude(), (float) sw.getLatitude(), (float) ne.getLongitude(), (float) sw.getLongitude(), -1, -1);
}
/**
* Creates a CoordinateList with the four corners of this object as the Coordinates.
*
* @return
*/
@Override
public CoordinateList<Coordinate> getCoordinateList() {
CoordinateList<Coordinate> cList = new CoordinateList<Coordinate>();
cList.add(nw);
cList.add(ne);
cList.add(se);
cList.add(sw);
cList.add(center);
return cList;
}
/**
* Returns the center of this ImageOverlay as a Coordinate.
*
* @return
*/
protected Coordinate getCenter() {
Coordinate c;
float centerLat, centerLon;
try {
centerLat = (float) sw.getLatitude() + (getHeight() / 2.0f);
centerLon = (float) sw.getLongitude() + (getWidth() / 2.0f);
return new Coordinate(0, centerLat, centerLon);
} catch (Exception e) {
System.err.println("Error in ImageOverlay.getCenter() - " + e);
return Coordinate.UNKNOWN_COORDINATE;
}
}
/**
* Returns the Description of this object. For Image Overlay a blank string is always returned.
* @return
*/
@Override
public String getDescription() {
return "";
}
/**
* Returns the height in Latitude degrees of this Overlay.
*
* @return
*/
private float getHeight() {
if ((nw.getLatitude() > 0) && (sw.getLatitude() >= 0)) {
return (float) nw.getLatitude() - (float) sw.getLatitude();
} else if ((nw.getLatitude() > 0) && (sw.getLatitude() < 0)) {
return (float) nw.getLatitude() + Math.abs((float) sw.getLatitude());
} else if ((nw.getLatitude() < 0) && (sw.getLatitude() < 0)) {
return Math.abs((float) sw.getLatitude()) - Math.abs((float) nw.getLatitude());
} else {
return 0;
}
}
/**
* Returns the MapIcon being used by this ImageOverlay.
*
* @return
*/
public MapIcon getMapIcon() {
return this.mapIcon;
}
/**
* Returns the name/ID for htis ImageOverlay.
*
* @return
*/
@Override
public String getName() {
return this.overlayID;
}
/**
* Returns the parent Layer that contains this ImageOverlay.
*
* @return
*/
@Override
public final Layer getParentLayer() {
return parentLayer;
}
/**
* Returns which Coordinate is selected.
*
* @return
*/
@Override
public Coordinate getSelectedCoordinate() {
return this.selectedCoordinate;
}
/**
* Returns the Visibility for this object.
*
* @return
*/
@Override
public Visibility getVisibility() {
return null;
//TODO implement Visibility in ImageOverlay.
}
/**
* Returns the width in Longitude Degrees of this Overlay.
*
* @return
*/
public float getWidth() {
if ((nw.getLongitude() >= 0) && (ne.getLongitude() >= 0)) {
return (float) ne.getLongitude() - (float) nw.getLongitude();
} else if ((nw.getLongitude() < 0) && (ne.getLongitude() > 0)) {
return Math.abs((float) nw.getLongitude()) + (float) ne.getLongitude();
} else if ((nw.getLongitude() > 0) && (ne.getLongitude() < 0)) {
return (180 - (float) nw.getLongitude()) + (180 - Math.abs((float) ne.getLongitude()));
} else if ((nw.getLongitude() < 0) && (ne.getLongitude() < 0)) {
return Math.abs((float) ne.getLongitude() - (float) nw.getLongitude());
} else {
return 0;
}
}
/**
* Returns if this object is Highlighted.
*
* @return
*/
@Override
public boolean isHighlighted() {
return highlighted;
}
/**
* Returns if this object is contained by, or overlaps another range.
*
* @param range
* @return
*/
@Override
public boolean isObjectWithinRectangle(Rectangle2D range) {
if (screenBounds != null) {
return screenBounds.contains(range);
} else {
return false;
}
}
/**
* Sets the boundary of this ImageOverlay.
*
* @param bounds
*/
public final void setBoundary(LatLonAltBox bounds) {
if (bounds != null) {
center = bounds.getCenter();
nw = bounds.getNorthWestCoordinate();
ne = bounds.getNorthEastCoordinate();
sw = bounds.getSouthWestCoordinate();
se = bounds.getSouthEastCoordinate();
//set backups to detect coordinate change
centerB = center.copy();
nwB = nw.copy();
neB = ne.copy();
swB = sw.copy();
seB = se.copy();
}
}
/**
* Sets the description for this object. However for ImageOverlay this is ignored.
*
* @param description
*/
@Override
public void setDescription(String description) {
}
public void setDimension(Dimension d) {
if (lastMapView != null) {
} else {
System.err.println("Error in ImageOverlay.setDimension(Dimension) - MapView not set, cannot change dimensions.");
}
}
/**
* Sets if this object is highlighted or not.
*
* @param h
*/
@Override
public void setHighlighted(boolean h) {
this.highlighted = h;
}
/**
* Sets the last MapView used to draw this object.
*
* @param mapView
*/
public void setLastMapView(MapView mapView) {
this.lastMapView = mapView;
}
/**
* Sets the Icon to use when drawing this object.
*
* @param mapIcon
*/
public void setMapIcon(MapIcon mapIcon) {
this.mapIcon = mapIcon;
}
/**
* Sets the Name/ID of this object.
*
* @param name
*/
@Override
public void setName(String name) {
this.overlayID = name;
}
/**
* Sets the ParentLayer containing this object.
*
* @param parentLayer
*/
@Override
public void setParentLayer(Layer parentLayer) {
this.parentLayer = parentLayer;
}
/**
* Sets the selected Coordinate of this Object.
*
* @param selectedCoordinate
*/
@Override
public void setSelectedCoordinate(Coordinate selectedCoordinate) {
this.selectedCoordinate = selectedCoordinate;
}
/**
* Sets the visibility for this ImageOverlay.
*
* @param v
*/
public void setVisibility(Visibility v) {
//TODO implement this.
}
/**
* Exports this ImageOverlay to FmXML.
*
* @param kmlWriter
*/
@Override
public void toXML(XmlOutput xmlWriter) {
LatLonAltBox bounds = getBounds();
xmlWriter.openTag("GroundOverlay id=\"" + overlayID + "\"");
mapIcon.toXML(xmlWriter);
xmlWriter.openTag("LatLonBox");
xmlWriter.writeTag("north", Float.toString(bounds.getNorth()));
xmlWriter.writeTag("south", Float.toString(bounds.getSouth()));
xmlWriter.writeTag("east", Float.toString(bounds.getEast()));
xmlWriter.writeTag("west", Float.toString(bounds.getWest()));
xmlWriter.writeTag("rotation", "0");
xmlWriter.closeTag("LatLonBox");
xmlWriter.closeTag("GroundOverlay");
}
/**
* Unselects the selected Coordinate.
*
*/
@Override
public void unselectCoordinate() {
this.selectedCoordinate = Coordinate.UNKNOWN_COORDINATE;
}
}