/*
* 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.vector;
import co.foldingmap.map.Layer;
import co.foldingmap.map.MapView;
import co.foldingmap.map.ObjectNotWithinBoundsException;
import co.foldingmap.map.themes.ColorStyle;
import co.foldingmap.xml.XmlOutput;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
/**
*
* @author Alec
*/
public class MultiGeometry extends VectorObject {
protected VectorObjectList<VectorObject> mapObjects;
/**
* Creates a new MultiGeometry with the supplied name and no objects.
*
* @param name
*/
public MultiGeometry(String name) {
this.objectName = name;
this.mapObjects = new VectorObjectList<VectorObject>();
}
/**
* Creates a new MultiGeometry with the supplied name and objects.
*
* @param name
* @param mapObjects
*/
public MultiGeometry(String name, VectorObjectList<VectorObject> mapObjects) {
try {
this.mapObjects = new VectorObjectList<VectorObject>();
this.mapObjects.addAll(mapObjects.getPolygons());
this.mapObjects.addAll(mapObjects.getLineStrings());
this.mapObjects.addAll(mapObjects.getMapPoints());
this.objectName = name;
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentObject = mapObjects.get(i);
currentObject.setParentLayer(this.getParentLayer());
}
} catch (Exception e) {
System.err.println("Error in MultiGeometry.constructor(Stirng, VectorObjectList) - " + e);
}
}
/**
* Adds an object to this MultiGeometry.
*
* @param newObject
*/
public void addObject(VectorObject newObject) {
this.mapObjects.add(newObject);
newObject.setParentLayer(this.getParentLayer());
generateBoundingBox();
}
@Override
public VectorObject copy() {
VectorObjectList mapObjectsCopy = new VectorObjectList<VectorObject>();
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
mapObjectsCopy.add(currentMapObject.copy());
}
return new MultiGeometry(objectName, mapObjectsCopy);
}
/**
* Draws all objects in this MultiGeometry.
*
* @param g2
* @param mapView
* @param colorStyle
*/
@Override
public void drawObject(Graphics2D g2, MapView mapView, ColorStyle colorStyle) {
boolean drawObject;
try {
//check to see if we need to draw the object
if (mapView.displayAll()) {
drawObject = true;
} else {
drawObject = this.isVisible(mapView);
}
if (drawObject) {
//Draw Outlines
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
if (currentMapObject instanceof Polygon) {
currentMapObject.drawObject(g2, mapView, null);
currentMapObject.drawOutline(g2, mapView, true);
} else if (currentMapObject instanceof LineString) {
currentMapObject.drawOutline(g2, mapView, true);
} else if (currentMapObject instanceof MultiGeometry) {
currentMapObject.drawOutline(g2, mapView, true);
}
}
for (int i = 0; i < mapObjects.size(); i++) {
LineString currentLineString;
VectorObject currentMapObject = mapObjects.get(i);
Polygon currentPolygon;
if (currentMapObject != null) {
if (currentMapObject instanceof LineString) {
currentLineString = (LineString) currentMapObject;
currentLineString.drawOutline(g2, mapView, true);
} else if (currentMapObject instanceof Polygon) {
currentPolygon = (Polygon) currentMapObject;
currentPolygon.drawOutline(g2, mapView, true);
}
currentMapObject.drawObject(g2, mapView, colorStyle);
}
}
}
} catch (Exception e) {
System.err.println("Error in MultiGeometry.drawObject(Graphics2D, MapView) - " + e);
}
}
/**
* Draws the outline for this LineString.
*
* @param g2
* @param mapView
*/
@Override
public void drawOutline(Graphics2D g2, MapView mapView, boolean inMultiGeometry) {
if (this.isVisible(mapView)) {
for (VectorObject obj: this.mapObjects) {
obj.drawOutline(g2, mapView, true);
}
}
}
/**
* Draws the points that make up this object.
*
* @param g2
* @param mapView
*/
@Override
public void drawPoints(Graphics2D g2, MapView mapView) {
for (VectorObject object: mapObjects) {
object.drawPoints(g2, mapView);
}
}
/**
* Returns if this MultiGeometry is equal to another object.
*
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (o instanceof MultiGeometry) {
MultiGeometry mg = (MultiGeometry) o;
return (this.hashCode() == mg.hashCode());
} else {
return false;
}
}
/**
* Creates a hask code for this Object.
* @return
*/
@Override
public int hashCode() {
int hash = 3;
for (VectorObject object: this.mapObjects)
hash += (object != null ? object.hashCode() : 0);
return hash;
}
/**
* Fits all objects in this MultiGeometry to a boundary.
*
* @param boundry
* @return
* @throws ObjectNotWithinBoundsException
*/
@Override
public VectorObject fitToBoundry(LatLonAltBox boundry) throws ObjectNotWithinBoundsException {
VectorObjectList<VectorObject> fittedObjects;
MultiGeometry fittedObject;
fittedObject = new MultiGeometry(this.getName());
fittedObjects = new VectorObjectList<VectorObject>();
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
fittedObjects.add(currentMapObject.fitToBoundry(boundry));
}
mapObjects = fittedObjects;
return fittedObject;
}
/**
* Creates a box that gives the bounds of the coordinates of this object.
*/
@Override
public void generateBoundingBox() {
float north, south, east, west, minAltitude, maxAltitude;
LatLonAltBox currentBox;
north = -90;
south = 90;
east = -180;
west = 180;
minAltitude = Float.MAX_VALUE;
maxAltitude = Float.MIN_VALUE;
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentObject = mapObjects.get(i);
currentBox = currentObject.getBoundingBox();
if (currentBox.getNorth() > north)
north = currentBox.getNorth();
if (currentBox.getSouth() < south)
south = currentBox.getSouth();
if (currentBox.getEast() > east)
east = currentBox.getEast();
if (currentBox.getWest() < west)
west = currentBox.getWest();
if (currentBox.getMinAltitude() < minAltitude)
minAltitude = currentBox.getMinAltitude();
if (currentBox.getMaxAltitude() > maxAltitude)
maxAltitude = currentBox.getMaxAltitude();
}
this.boundingBox = new LatLonAltBox(north, south, east, west, minAltitude, maxAltitude);
}
/**
* Returns a component object at the given index.
*
* @param i
* @return
*/
public VectorObject getComponentObject(int i) {
return mapObjects.get(i);
}
/**
* Returns all the Component objects.
*
* @return
*/
public VectorObjectList<VectorObject> getComponentObjects() {
return mapObjects;
}
/**
* Returns all the coordinates of all contained objects.
*
* @return
*/
@Override
public CoordinateList<Coordinate> getCoordinateList() {
CoordinateList<Coordinate> allCoordinates;
allCoordinates = new CoordinateList<Coordinate>();
for (VectorObject object: mapObjects) {
allCoordinates.addAll(object.getCoordinateList());
}
return allCoordinates;
}
/**
* Returns a coordinate within the range.
*
* @param range
* @return
*/
@Override
public Coordinate getCoordinateWithinRectangle(Rectangle2D range) {
Coordinate returnCoordinate = null;
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
returnCoordinate = currentMapObject.getCoordinateWithinRectangle(range);
}
return returnCoordinate;
}
/**
* Returns if this object only contains one Polygon and one MapPoint.
*
* @return
*/
public boolean isIconPolygon() {
boolean hasMapPoint, hasPolygon, result;
hasMapPoint = false;
hasPolygon = false;
result = false;
if (mapObjects.size() == 2) {
if ((mapObjects.get(0) instanceof MapPoint) || (mapObjects.get(1) instanceof MapPoint))
hasMapPoint = true;
if ((mapObjects.get(0) instanceof Polygon) || (mapObjects.get(1) instanceof Polygon))
hasPolygon = true;
if (hasPolygon && hasMapPoint)
result = true;
}
return result;
}
/**
* Returns if the object is within a given range.
*
* @param range
* @return
*/
@Override
public boolean isObjectWithinRectangle(Rectangle2D range) {
boolean result = false;
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
result = currentMapObject.isObjectWithinRectangle(range);
if (result == true)
break;
}
return result;
}
/**
* Sets all Component objects as highlighted.
*
* @param h
*/
@Override
public void setHighlighted(boolean h) {
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
currentMapObject.setHighlighted(h);
}
}
/**
* Sets the name for all the Component map objects.
*
* @param name
*/
@Override
public void setName(String name) {
super.setName(name);
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
currentMapObject.setName(name);
}
}
/**
* Sets this Object's Parent Layer. This is usually called when adding a
* VectorObject to a Layer Object.
*
* @param parentLayer
*/
@Override
public void setParentLayer(Layer parentLayer) {
this.parentLayer = parentLayer;
//Set the parent layer for all sub object to the same parent layer.
for (VectorObject vo: this.mapObjects) {
vo.setParentLayer(parentLayer);
}
}
/**
* Writes this object to XML.
*
* @param xmlWriter
*/
@Override
public void toXML(XmlOutput xmlWriter) {
try {
xmlWriter.openTag ("MultiGeometry id=\"" + getName() + "\"");
xmlWriter.writeTag("Ref", Long.toString(getReference()));
xmlWriter.writeTag("Name", this.objectName);
if (this.visibility != null)
visibility.toXML(xmlWriter);
if (hasDisplayableText(getDescription()) && !getDescription().equalsIgnoreCase("null"))
xmlWriter.writeTag("description", "<![CDATA[" + getDescription() + "]]>");
xmlWriter.openTag("elements");
for (int i = 0; i < mapObjects.size(); i++) {
VectorObject currentMapObject = mapObjects.get(i);
currentMapObject.toXML(xmlWriter);
}
xmlWriter.closeTag("elements");
writeCustomDataFieldsAsXML(xmlWriter);
xmlWriter.closeTag("MultiGeometry");
} catch (Exception e) {
System.err.println("Error in MultiGeometry.toXML(KmlWriter) Object: " + this.objectName + " - " + e);
}
}
}