/*
* Copyright (C) 2015 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.themes.PolygonStyle;
import co.foldingmap.map.themes.MapTheme;
import co.foldingmap.map.themes.ThemeConstants;
import co.foldingmap.map.themes.LabelStyle;
import co.foldingmap.map.themes.ColorStyle;
import co.foldingmap.map.themes.OutlineStyle;
import co.foldingmap.Logger;
import co.foldingmap.dataStructures.ListOperations;
import co.foldingmap.dataStructures.StringCountList;
import co.foldingmap.map.MapView;
import co.foldingmap.map.ObjectNotWithinBoundsException;
import co.foldingmap.map.labeling.PolygonLabel;
import co.foldingmap.xml.XmlOutput;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* Handles the FmXML object: Polygon.
*
* @author Alec
*/
public class Polygon extends VectorObject {
private BasicStroke stroke;
private boolean pLeftInit, pCenterInit, pRightInit;
private boolean segmentsGenerated;
private GeneralPath pLeft, pCenter, pRight;
private ArrayList<InnerBoundary> innerBoundaries;
private ArrayList<OutlineSegment> outlineSegments;
/**
* Constructor for Polygon.
*
* @param name
* @param type
* @param coordinates
*/
public Polygon(String name, String type, CoordinateList coordinates) {
super();
commonConstructor(name, type);
this.coordinates.addAll(coordinates);
}
/**
* Constructor using String Coordinates.
*
* @param name
* @param type
* @param lineCoordinates
*/
public Polygon(String name, String type, String lineCoordinates) {
super();
Coordinate newCoordinate;
String currentCoordinate;
StringTokenizer st = new StringTokenizer(lineCoordinates);
commonConstructor(name, type);
//get coordinates
while (st.hasMoreTokens())
{
currentCoordinate = st.nextToken();
newCoordinate = new Coordinate(currentCoordinate);
coordinates.add(newCoordinate);
}
}
/**
* A Constructor for shared init elements between constructors.
*
* @param name
* @param objectClass
*/
public final void commonConstructor(String name, String objectClass) {
try {
this.coordinates = new CoordinateList<Coordinate>();
this.innerBoundaries = new ArrayList<InnerBoundary>(1);
this.objectName = name;
this.objectClass = objectClass;
this.pLeftInit = false;
this.pCenterInit = false;
this.pRightInit = false;
this.pLeft = new GeneralPath();
this.pRight = new GeneralPath();
this.pCenter = new GeneralPath();
this.segmentsGenerated = false;
this.stroke = new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.commonConstructor(String, String) - " + e);
}
}
/**
* Adds an Inner Boundary to this polygon.
*
* @param ib
*/
public void addInnerBoundary(InnerBoundary ib) {
this.innerBoundaries.add(ib);
}
/**
* Adds an OutlineSegment to this Polygon.
* This should be called directly rather than adding to the outlineSegments
* ArrayList directly.
*
* @param segment
*/
public void addOutlineSegment(OutlineSegment segment) {
try {
if (outlineSegments.size() > 0 && segment.getColor() != null) {
OutlineSegment lastSegment = outlineSegments.get(outlineSegments.size() - 1);
//Check to see if the outline styles are the same.
if (segment.getColor().equals(lastSegment.getColor()) &&
segment.getStrokeType() == lastSegment.getStrokeType()) {
//If they are the same combine segments
lastSegment.appendSegment(segment);
} else {
//If they are not the same then add a new segment.
outlineSegments.add(segment);
}
} else {
//No segment to check against
outlineSegments.add(segment);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.addOutlineSegment(OutlineSegment) - " + e);
}
}
public void convertCoordinatesToLines(MapView mapView) {
Coordinate currentCoordinate;
float x1, x2, x3, y1;
Point2D.Float tempPoint;
//clear out general path
pCenter.reset();
pLeftInit = false;
pCenterInit = false;
pRightInit = false;
try {
//prime the loop
currentCoordinate = (Coordinate) coordinates.get(0);
pCenterInit = true;
tempPoint = currentCoordinate.getCenterPoint();
pCenter.moveTo(tempPoint.getX(), tempPoint.getY());
//is left wrapping needed
if (mapView.getMapProjection().isLeftShown()) {
tempPoint = currentCoordinate.getLeftPoint();
pLeftInit = true;
pLeft.reset();
pLeft.moveTo(tempPoint.getX(), tempPoint.getY());
}
//right wrapping is needed
if (mapView.getMapProjection().isRightShown()) {
tempPoint = currentCoordinate.getRightPoint();
pRightInit = true;
pRight.reset();
pRight.moveTo(tempPoint.getX(), tempPoint.getY());
}
for (int i = 1; i < coordinates.size(); i++) {
currentCoordinate = (Coordinate) coordinates.get(i);
tempPoint = currentCoordinate.getCenterPoint();
pCenter.lineTo(tempPoint.getX(), tempPoint.getY());
if (pLeftInit) {
tempPoint = currentCoordinate.getLeftPoint();
pLeft.lineTo(tempPoint.getX(), tempPoint.getY());
}
if (pRightInit) {
tempPoint = currentCoordinate.getRightPoint();
pRight.lineTo(tempPoint.getX(), tempPoint.getY());
}
}
if (pLeftInit) pLeft.closePath();
if (pCenterInit) pCenter.closePath();
if (pRightInit) pRight.closePath();
if (this.innerBoundaries.size() > 0) {
if (pLeftInit) pLeft.setWindingRule( GeneralPath.WIND_EVEN_ODD);
if (pCenterInit) pCenter.setWindingRule(GeneralPath.WIND_EVEN_ODD);
if (pRightInit) pRight.setWindingRule( GeneralPath.WIND_EVEN_ODD);
for (InnerBoundary ib: innerBoundaries) {
currentCoordinate = ib.getCoordinateList().get(0);
y1 = mapView.getY(currentCoordinate);
if (pLeftInit) {
x2 = mapView.getX(currentCoordinate, MapView.WRAP_LEFT);
pLeft.moveTo(x2, y1);
}
if (pCenterInit) {
x1 = mapView.getX(currentCoordinate, MapView.NO_WRAP);
pCenter.moveTo(x1, y1);
}
if (pRightInit) {
x3 = mapView.getX(currentCoordinate, MapView.WRAP_RIGHT);
pRight.moveTo(x3, y1);
}
for (int i = 1; i < coordinates.size(); i++) {
currentCoordinate = (Coordinate) coordinates.get(i);
y1 = mapView.getY(currentCoordinate);
if (pLeftInit) {
x2 = mapView.getX(currentCoordinate, MapView.WRAP_LEFT);
pLeft.lineTo(x2, y1);
}
if (pCenterInit) {
x1 = mapView.getX(currentCoordinate, MapView.NO_WRAP);
pCenter.lineTo(x1, y1);
}
if (pRightInit) {
x3 = mapView.getX(currentCoordinate, MapView.WRAP_RIGHT);
pRight.lineTo(x3, y1);
}
}
if (pLeftInit) pLeft.closePath();
if (pCenterInit) pCenter.closePath();
if (pRightInit) pRight.closePath();
}
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.convertCoordinatesToLines(MapView) - " + e);
}
}
@Override
public VectorObject copy() {
Polygon newCopy = new Polygon(this.objectName, this.getObjectClass(), this.getCoordinateList());
newCopy.setDescription(this.getDescription());
newCopy.setParentLayer(this.getParentLayer());
newCopy.setCustomDataFields(this.customDataFields);
return newCopy;
}
/**
* Creates a Label for this Polygon.
*
* @param g2
* @param mapView
*/
public void createLabel(Graphics2D g2, MapView mapView) {
Coordinate center = this.boundingBox.getCenter();
FontMetrics fontMetrics = g2.getFontMetrics();
int labelWidth = fontMetrics.stringWidth(objectName);
float x = mapView.getX(center, MapView.NO_WRAP);
float y = mapView.getY(center);
x = x - (labelWidth / 2f);
mapView.getLabelManager().addLabel(g2, new PolygonLabel(objectName, new LabelStyle(Color.WHITE), (int) x, (int) y));
}
/**
* Draws this Polygon on the map.
*
* @param g2
* @param mapView
* @param colorStyle
*/
@Override
public void drawObject(Graphics2D g2, MapView mapView, ColorStyle colorStyle) {
boolean drawObject, fillPoly;
PolygonStyle polygonStyle;
try {
//check to see if we need to draw the object
if (mapView.displayAll()) {
drawObject = true;
} else {
drawObject = this.isVisible(mapView);
}
if (drawObject) {
convertCoordinatesToLines(mapView);
if (colorStyle == null) {
polygonStyle = (PolygonStyle) mapView.getMapTheme().getStyle(this, mapView.getZoomLevel());
if (polygonStyle == null)
polygonStyle = mapView.getMapTheme().getPolygonStyle("(Unspecified Polygon)");
} else {
polygonStyle = (PolygonStyle) colorStyle;
}
fillPoly = polygonStyle.isFilled();
g2.setStroke(this.stroke);
if (highlighted) {
g2.setColor(polygonStyle.getSelectedFillColor());
fillPoly = true; //Force fill is selected
} else {
g2.setColor(polygonStyle.getFillColor());
}
if (fillPoly) {
Paint cPaint = g2.getPaint();
if (polygonStyle.isGradientFilled() && !highlighted) {
Rectangle2D bounds = pCenter.getBounds2D();
float x1 = (float) bounds.getMinX();
float x2 = (float) bounds.getMaxX();
float y1 = (float) 0;
float y2 = (float) 20;
GradientPaint gp = new GradientPaint(x1, y1, polygonStyle.getGradient1(), x2, y2, polygonStyle.getGradient2());
g2.setPaint(gp);
g2.fill(pCenter);
g2.setPaint(cPaint);
} else {
if (pLeftInit) g2.fill(pLeft);
if (pCenterInit) g2.fill(pCenter);
if (pRightInit) g2.fill(pRight);
if (polygonStyle.isImagedFilled()) {
BufferedImage bi = polygonStyle.getObjectImage();
Rectangle2D bounds = new Rectangle2D.Float(0, 0, bi.getWidth(), bi.getHeight());
TexturePaint tp = new TexturePaint(bi, bounds);
g2.setPaint(tp);
if (pLeftInit) g2.fill(pLeft);
if (pCenterInit) g2.fill(pCenter);
if (pRightInit) g2.fill(pRight);
g2.setPaint(cPaint);
} else {
//if not outlined, draw the outline with the polygon's color
if (!polygonStyle.isOutlined()) {
if (pLeftInit) g2.draw(pLeft);
if (pCenterInit) g2.draw(pCenter);
if (pRightInit) g2.draw(pRight);
}
}//Image Filled Check
} // Gradient Check
} // Filled Check
// createLabel(g2, mapView);
} //end drawObject check
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.drawObjet(Graphics2D, MapView) " + this.objectName + " - " + e);
}
}
/**
* Draws the outline of this polygon.
*
* @param g2
* @param mapView
* @param inMultiGeometry
*/
@Override
public void drawOutline(Graphics2D g2, MapView mapView, boolean inMultiGeometry) {
boolean drawObject;
ColorStyle polygonStyle;
try {
//update segment outlines, if needed
if (!segmentsGenerated) {
this.updateOutlines(mapView.getMapTheme());
}
polygonStyle = mapView.getMapTheme().getPolygonStyle(this.getObjectClass());
if (polygonStyle != null) {
drawObject = this.isVisible(mapView);
if (drawObject && polygonStyle.isOutlined()) {
if (highlighted) {
g2.setColor(polygonStyle.getSelectedOutlineColor());
} else {
g2.setColor(polygonStyle.getOutlineColor());
}
for (OutlineSegment seg: this.outlineSegments) {
if (outlineSegments.size() == 1)
seg.closeSegment();
seg.draw(g2, mapView);
}
}
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.drawOutline(Graphics2D, MapView) - " + e);
}
}
/**
* Draws the points that make up this object.
*
* @param g2
* @param mapView
*/
@Override
public void drawPoints(Graphics2D g2, MapView mapView) {
Shape leftShape, centerShape, rightShape;
leftShape = null;
rightShape = null;
if (selectedCoordinate == null)
selectedCoordinate = getCoordinateList().getCoordinateClosestTo(mapView.getLastMouseClickCoordinate());
//change the draw style
g2.setStroke(new BasicStroke(2f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
for (Coordinate c: this.coordinates) {
centerShape = getPointShape(c, MapView.NO_WRAP);
g2.setColor(mapView.getMapTheme().getPointColor());
g2.draw(centerShape);
if (mapView.getMapProjection().isLeftShown()) {
leftShape = getPointShape(c, MapView.WRAP_LEFT);
g2.draw(leftShape);
}
if (mapView.getMapProjection().isRightShown()) {
rightShape = getPointShape(c, MapView.WRAP_RIGHT);
g2.draw(rightShape);
}
if (selectedCoordinate.equals(c))
g2.setColor(Color.WHITE);
g2.fill(centerShape);
if (mapView.getMapProjection().isRightShown())
g2.fill(rightShape);
if (mapView.getMapProjection().isLeftShown())
g2.fill(leftShape);
}
}
/**
* Fits this object to a given boundary.
*
* @param boundry
* @return
* @throws ObjectNotWithinBoundsException
*/
@Override
public VectorObject fitToBoundry(LatLonAltBox boundry) throws ObjectNotWithinBoundsException {
Coordinate northEastCoordinate, northWestCoordinate, southEastCoordinate, southWestCoordinate;
Coordinate cutOffCoordinate;
Polygon fittedObject;
fittedObject= this;
fittedObject.getCoordinateList().clear();
cutOffCoordinate = Coordinate.UNKNOWN_COORDINATE;
northEastCoordinate = boundry.getNorthEastCoordinate();
northWestCoordinate = boundry.getNorthWestCoordinate();
southEastCoordinate = boundry.getSouthEastCoordinate();
southWestCoordinate = boundry.getSouthWestCoordinate();
for (int i = 0; i < this.coordinates.size(); i++) {
Coordinate currentCoordinate = this.coordinates.get(i);
if (boundry.contains(currentCoordinate)) {
if (boundry.contains(this.coordinates.get(i-1))) {
fittedObject.appendCoordinate(currentCoordinate);
} else {
if (currentCoordinate.isNorthOf(northWestCoordinate)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, northWestCoordinate, northEastCoordinate);
} else if (currentCoordinate.isSouthOf(southEastCoordinate)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, southWestCoordinate, southEastCoordinate);
} else if (currentCoordinate.isWestOf(northWestCoordinate, 90)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, northWestCoordinate, southWestCoordinate);
} else if (currentCoordinate.isEastOf(southEastCoordinate)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, northEastCoordinate, southEastCoordinate);
}
fittedObject.appendCoordinate(cutOffCoordinate);
}
} else {
//coordinate is not within the area
if (boundry.contains(this.coordinates.get(i-1))) {
//last object was in the boundry
if (currentCoordinate.isNorthOf(northWestCoordinate)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, northWestCoordinate, northEastCoordinate);
} else if (currentCoordinate.isSouthOf(southEastCoordinate)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, southWestCoordinate, southEastCoordinate);
} else if (currentCoordinate.isWestOf(northWestCoordinate, 90)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, northWestCoordinate, southWestCoordinate);
} else if (currentCoordinate.isEastOf(southEastCoordinate)) {
cutOffCoordinate = Intersection.getIntersection(coordinates.get(i-1), currentCoordinate, northEastCoordinate, southEastCoordinate);
}
fittedObject.appendCoordinate(cutOffCoordinate);
}
}
} //end for loop
fittedObject = this;
return fittedObject;
}
/**
* Gets a coordinate with in a given Rectangle2D range.
*
* @param range
* @return
*/
@Override
public Coordinate getCoordinateWithinRectangle(Rectangle2D range) {
Coordinate returnCoordinate = null;
for (Coordinate c: this.coordinates) {
if (range.contains(c.getCenterPoint())) {
returnCoordinate = c;
break;
}
if ((c.getLeftPoint() != null) && range.contains(c.getLeftPoint())) {
returnCoordinate = c;
break;
}
if ((c.getRightPoint() != null) && range.contains(c.getRightPoint())) {
returnCoordinate = c;
break;
}
}
return returnCoordinate;
}
/**
* Returns the inner boundaries of this polygon.
*
* @return
*/
public ArrayList<InnerBoundary> getInnerBoundaries() {
return innerBoundaries;
}
/**
* Specifies if the selected range is within this polygon.
*
* TODO: If the polygon is filing the screen don't select it.
*
* @param range
* @return
*/
@Override
public boolean isObjectWithinRectangle(Rectangle2D range) {
boolean returnValue = false;
try {
if (pLeft != null) {
if (pLeft.contains(range))
returnValue = true;
}
if (pCenter != null) {
if (pCenter.contains(range))
returnValue = true;
}
if (pRight != null) {
if (pRight.contains(range))
returnValue = true;
}
//TODO: this is slow and needs to be refactored
if (!returnValue) {
for (Coordinate c: this.coordinates) {
if (range.contains(c.getCenterPoint())) {
returnValue = true;
break;
}
if ((c.getLeftPoint() != null) && range.contains(c.getLeftPoint())) {
returnValue = true;
break;
}
if ((c.getRightPoint() != null) && range.contains(c.getRightPoint())) {
returnValue = true;
break;
}
}
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.isObjectWithinRectangle(Rectangle2D) - " + e);
isObjectWithinRectangle(range);
}
return returnValue;
}
@Override
public String toString() {
return "Polygon - " + objectName + " - " + objectClass;
}
/**
* Writes out this object in XML form so it can be copied or written to a
* file.
*
* @param xmlWriter
*/
@Override
public void toXML(XmlOutput xmlWriter) {
try {
xmlWriter.openTag ("Polygon class=\"" + getObjectClass() + "\" id=\"" + getName() + "\"");
if (this.visibility != null)
visibility.toXML(xmlWriter);
if (hasDisplayableText(getDescription()) && !getDescription().equalsIgnoreCase("null"))
xmlWriter.writeTag("description", "<![CDATA[" + getDescription() + "]]>");
xmlWriter.writeTag("Ref", Long.toString(getReference()));
xmlWriter.openTag ("outerBoundary");
xmlWriter.writeTag("coordinates", getCoordinateString());
xmlWriter.closeTag("outerBoundary");
for (InnerBoundary ib: innerBoundaries)
ib.toXML(xmlWriter);
writeCustomDataFieldsAsXML(xmlWriter);
xmlWriter.closeTag("Polygon");
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.toXML(KmlWriter) Object: " + this.objectName + " - " + e);
}
}
private void buildSegments(MapTheme theme) {
ArrayList<Coordinate> segCoordinates;
ArrayList<VectorObject> commonObjects;
ArrayList<VectorObject> currentParentObjects, lastParentObjects;
boolean addSegment, dateline, divideSegment;
Color outlineColor, ColorToUse;
Coordinate c;
double longitudeFirst, longitudeLast;
OutlineSegment currentOutlineSegment;
PolygonStyle polyStyle;
VectorObject gulfedObject;
try {
//init
ColorToUse = null;
dateline = false;
lastParentObjects = null;
currentParentObjects = null;
currentOutlineSegment = new OutlineSegment();
outlineSegments = new ArrayList<OutlineSegment>();
polyStyle = theme.getPolygonStyle(this.getObjectClass());
outlineColor = polyStyle.getOutlineColor();
for (int i = 0; i < coordinates.size(); i++) {
addSegment = true;
divideSegment = false;
gulfedObject = null;
c = coordinates.get(i);
currentParentObjects = c.getParentVectorObjects();
currentOutlineSegment.addCoordinate(c);
if (lastParentObjects == null) {
lastParentObjects = currentParentObjects;
} else {
if (ListOperations.listsContainSameObjects(currentParentObjects, lastParentObjects)) {
lastParentObjects = currentParentObjects;
//gulf detection
if (currentOutlineSegment.size() == 2) {
Coordinate c1, c2;
int lastIndex, thisIndex;
c1 = currentOutlineSegment.getCoordinateList().get(0);
c2 = currentOutlineSegment.getCoordinateList().get(1);
commonObjects = ListOperations.getCommonObjects(c1.getParentVectorObjects(), c2.getParentVectorObjects());
for (VectorObject vecObj: commonObjects) {
thisIndex = vecObj.getCoordinateList().indexOf(c1);
lastIndex = vecObj.getCoordinateList().indexOf(c2);
if (lastIndex > 0 && thisIndex > 0) {
if (Math.abs(lastIndex - thisIndex) > 1) {
//is a gulf
divideSegment = true;
gulfedObject = vecObj;
break;
}
}
}
}
//International Dateline Detection
if (divideSegment == false) {
if (c.getLongitude() == 180 || c.getLongitude() == -180) {
divideSegment = true;
dateline = true;
}
}
} else {
divideSegment = true;
}
}
if (divideSegment == true) {
if (currentOutlineSegment.getCoordinateList().size() > 1) {
//Lists have diffent parent objects, split into a new outline segment.
commonObjects = ListOperations.getCommonObjects(currentParentObjects, lastParentObjects);
segCoordinates = currentOutlineSegment.getCoordinateList();
longitudeFirst = segCoordinates.get(0).getLongitude();
longitudeLast = segCoordinates.get(segCoordinates.size() - 1).getLongitude();
//If this segment bridges a gulf, remove the gulfed object
if (gulfedObject != null) commonObjects.remove(gulfedObject);
//Check to see if the segment lies on the IDL
if (dateline == true &&
(longitudeFirst == 180 || longitudeFirst == -180) &&
(longitudeLast == 180 || longitudeLast == -180)) {
ColorToUse = polyStyle.getOutlineStyleByCondition("None").getColor();
currentOutlineSegment.setColor(ColorToUse);
} else if (commonObjects.size() == 2) {
commonObjects.remove(this);
if (commonObjects.size() > 0)
ColorToUse = getBorderConditionColor(theme, polyStyle, commonObjects.get(0));
if (ColorToUse != null) {
currentOutlineSegment.setColor(ColorToUse);
} else {
addSegment = false;
}
} else if (commonObjects.size() < 2) {
if (currentParentObjects.size() == 2) {
if (currentParentObjects.get(0) == this) {
ColorToUse = getBorderConditionColor(theme, polyStyle, currentParentObjects.get(1));
} else {
ColorToUse = getBorderConditionColor(theme, polyStyle, currentParentObjects.get(0));
}
currentOutlineSegment.setColor(ColorToUse);
} else {
lastParentObjects.removeAll(currentParentObjects);
if (lastParentObjects.size() > 0) {
ColorToUse = getBorderConditionColor(theme, polyStyle, lastParentObjects.get(0));
currentOutlineSegment.setColor(ColorToUse);
}
}
} else {
//More than 2 common object, common with gulfs
System.out.println("--> More than 2 common Objects");
currentOutlineSegment.setColor(Color.RED);
}
if (addSegment == true) addOutlineSegment(currentOutlineSegment);
currentOutlineSegment = new OutlineSegment();
currentOutlineSegment.addCoordinate(c);
lastParentObjects = currentParentObjects;
}
} // If for deviding segment
}
//Last Segment
if (currentOutlineSegment.getColor() == null) {
commonObjects = ListOperations.getCommonObjects(currentParentObjects, lastParentObjects);
if (commonObjects.size() == 2) {
//Check to see if the segment lies on the IDL
//test for implementing OutlineStyles
commonObjects.remove(this);
ColorToUse = getBorderConditionColor(theme, polyStyle, commonObjects.get(0));
if (ColorToUse != null) {
currentOutlineSegment.setColor(ColorToUse);
}
} else if (commonObjects.size() == 1) {
//no border check for ANY or NONE
for (OutlineStyle os: polyStyle.getOutlineStyles()) {
if (os != null) {
if (os.getBorderCondition().equalsIgnoreCase(ThemeConstants.NONE) ||
os.getBorderCondition().equalsIgnoreCase(ThemeConstants.ANY)) {
//Matching condition found
if (os.getColor() != null) {
currentOutlineSegment.setColor(os.getColor());
break;
}
}
} //end null check
}
} else {
StringCountList countList = new StringCountList();
for (VectorObject commObj: commonObjects)
countList.addIncrement(commObj.getObjectClass());
if (countList.getMostOccurringString("").equalsIgnoreCase("Ocean")) {
currentOutlineSegment.setColor(polyStyle.getFillColor());
} else {
currentOutlineSegment.setColor(outlineColor);
}
}
}
addOutlineSegment(currentOutlineSegment);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.buildSegments(MapView) - " + e);
}
}
/**
* Updates how outlines are drawn. A polygon may have OutlineSegments with
* different characteristics. I.e. different color, depending on certain
* conditions like what other objects the outline will be shared with.
*/
@Override
public void updateOutlines(MapTheme theme) {
ArrayList<OutlineStyle> outlineStyles;
boolean buildSegments;
PolygonStyle polyStyle;
try {
polyStyle = theme.getPolygonStyle(objectClass);
if (polyStyle != null) {
outlineStyles = theme.getPolygonStyle(objectClass).getOutlineStyles();
//Decide if the outline segments need to be built
if (outlineStyles.size() > 1) {
buildSegments = true;
} else if (outlineStyles.size() == 1) {
buildSegments = !outlineStyles.get(0).getBorderCondition().equalsIgnoreCase(OutlineStyle.ANY);
} else {
buildSegments = false;
}
if (buildSegments) {
buildSegments(theme);
} else {
//This Polygon has no shared segments
OutlineSegment newSegment = new OutlineSegment(1);
OutlineStyle styleToUse;
PolygonStyle style;
//Create the OutlineSegment ArrayList with a size of 1, because there is only one outline.
this.outlineSegments = new ArrayList<OutlineSegment>(1);
//add all the coordinates from this Polygon into a new segment.
for (Coordinate c: this.coordinates)
newSegment.addCoordinate(c);
newSegment.closeSegment();
style = theme.getPolygonStyle(objectClass);
if (style != null) {
//First check to see if there is a 'None' style
styleToUse = ListOperations.getOutlineStyleFromCondition(style.getOutlineStyles(), "None");
//If no 'None' style, look for an 'Any'
if (styleToUse == null)
styleToUse = ListOperations.getOutlineStyleFromCondition(style.getOutlineStyles(), "Any");
if (styleToUse != null) {
newSegment.setColor(styleToUse.getColor());
} else {
//no outline style, do not outline
}
addOutlineSegment(newSegment);
} else {
Logger.log(Logger.WARN, "Polygon.updateOutlines(MapTheme) Style: " + objectClass + " Not found in this theme.");
}
}
}
this.segmentsGenerated = true;
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in Polygon.updateOutlines(MapTheme) - " + e);
}
}
/**
* Returns the Color to be used for the border from the given polyStyle and
* the given borderingObject.
*
* @param theme
* @param polyStyle
* @param borderingObject
* @return
*/
public static Color getBorderConditionColor(MapTheme theme, PolygonStyle polyStyle, VectorObject borderingObject) {
ColorStyle borderingStyle;
for (OutlineStyle os: polyStyle.getOutlineStyles()) {
if (os != null) {
borderingStyle = theme.getStyle(borderingObject, 0);
if (os.getBorderCondition().equalsIgnoreCase(borderingStyle.getFeatureType()) ||
os.getBorderCondition().equalsIgnoreCase(borderingObject.getObjectClass()) ||
os.getBorderCondition().equalsIgnoreCase(ThemeConstants.ANY)) {
//Matching condition found
if (os.getColor() != null) {
return os.getColor();
}
}
} //end null check
}
return null;
}
}