/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.android.maps.overlay;
import org.mapsforge.core.model.GeoPoint;
import android.graphics.Paint;
import android.graphics.Point;
/**
* OverlayWay holds all parameters of a single way on a {@link WayOverlay}. All rendering parameters like color, stroke
* width, pattern and transparency can be configured via two {@link Paint} objects. Each way is drawn twice - once with
* each paint object - to allow for different outlines and fillings. The drawing quality can be improved by enabling
* {@link Paint#setAntiAlias(boolean) anti-aliasing}.
* <p>
* The way data is represented as a two-dimensional array in order to support multi-polygons. A multi-polygon consists
* of several polygons and can for example be used to draw a polygon with holes. Each array element on the first level
* stores on the second level the coordinates of one polygon.
*/
public class OverlayWay {
/**
* Checks whether the given arrays have the same lengths on each dimension.
*
* @param array1
* the first array to check.
* @param array2
* the second array to check.
* @return true if the arrays have the same length on each dimension, false otherwise.
*/
private static boolean arrayLengthsEqual(Object[][] array1, Object[][] array2) {
if (array1.length != array2.length) {
return false;
}
for (int i = array1.length - 1; i >= 0; --i) {
if (array1[i].length != array2[i].length) {
return false;
}
}
return true;
}
/**
* Checks the given way nodes for null elements.
*
* @param wayNodes
* the way nodes to check for null elements.
* @return true if the way nodes contain at least one null element, false otherwise.
*/
private static boolean containsNullElements(GeoPoint[][] wayNodes) {
for (int i = wayNodes.length - 1; i >= 0; --i) {
if (wayNodes[i] == null) {
return true;
}
for (int j = wayNodes[i].length - 1; j >= 0; --j) {
if (wayNodes[i][j] == null) {
return true;
}
}
}
return false;
}
/**
* Paint which will be used to fill the way.
*/
public Paint paintFill;
/**
* Paint which will be used to draw the way outline.
*/
public Paint paintOutline;
/**
* Geographical coordinates of the way nodes.
*/
public GeoPoint[][] wayNodes;
/**
* Cached way positions of the way nodes on the map.
*/
public Point[][] cachedWayPositions;
/**
* Zoom level of the cached way node positions.
*/
public byte cachedZoomLevel;
/**
* Flag to indicate if at least one paint is set for this way.
*/
public boolean hasPaint;
/**
* Constructs a new OverlayWay.
*/
public OverlayWay() {
this(null, null, null);
}
/**
* @param wayNodes
* the geographical coordinates of the way nodes, must not contain null elements.
* @throws IllegalArgumentException
* if the way nodes contain at least one null element.
*/
public OverlayWay(GeoPoint[][] wayNodes) {
this(wayNodes, null, null);
}
/**
* @param wayNodes
* the geographical coordinates of the way nodes, must not contain null elements.
* @param paintFill
* the paint which will be used to fill the way (may be null).
* @param paintOutline
* the paint which will be used to draw the way outline (may be null).
* @throws IllegalArgumentException
* if the way nodes contain at least one null element.
*/
public OverlayWay(GeoPoint[][] wayNodes, Paint paintFill, Paint paintOutline) {
this.cachedWayPositions = new Point[0][0];
this.cachedZoomLevel = Byte.MIN_VALUE;
setWayNodesInternal(wayNodes);
setPaintInternal(paintFill, paintOutline);
}
/**
* @param paintFill
* the paint which will be used to fill the way (may be null).
* @param paintOutline
* the paint which will be used to draw the way outline (may be null).
* @throws IllegalArgumentException
* if the way nodes contain at least one null element.
*/
public OverlayWay(Paint paintFill, Paint paintOutline) {
this(null, paintFill, paintOutline);
}
/**
* @return a copy of the way nodes of this way.
*/
public synchronized GeoPoint[][] getWayNodes() {
return this.wayNodes.clone();
}
/**
* Sets the paints which will be used to draw this way.
* <p>
* Changes might not become visible until {@link Overlay#requestRedraw()} is called.
*
* @param paintFill
* the paint which will be used to fill the way (may be null).
* @param paintOutline
* the paint which will be used to draw the way outline (may be null).
*/
public synchronized void setPaint(Paint paintFill, Paint paintOutline) {
setPaintInternal(paintFill, paintOutline);
}
/**
* Sets the way nodes of this way.
* <p>
* Changes might not become visible until {@link Overlay#requestRedraw()} is called.
*
* @param wayNodes
* the geographical coordinates of the way nodes, must not contain null elements.
* @throws IllegalArgumentException
* if the way nodes contain at least one null element.
*/
public synchronized void setWayNodes(GeoPoint[][] wayNodes) {
setWayNodesInternal(wayNodes);
}
private void setPaintInternal(Paint paintFill, Paint paintOutline) {
this.paintFill = paintFill;
this.paintOutline = paintOutline;
this.hasPaint = paintFill != null || paintOutline != null;
}
private void setWayNodesInternal(GeoPoint[][] wayNodes) {
if (wayNodes == null) {
this.wayNodes = null;
} else if (containsNullElements(wayNodes)) {
throw new IllegalArgumentException("way nodes must not contain null elements");
} else {
this.wayNodes = wayNodes.clone();
}
if (this.wayNodes == null) {
this.cachedWayPositions = new Point[0][0];
} else if (!arrayLengthsEqual(this.wayNodes, this.cachedWayPositions)) {
this.cachedWayPositions = new Point[this.wayNodes.length][];
for (int i = this.wayNodes.length - 1; i >= 0; --i) {
this.cachedWayPositions[i] = new Point[this.wayNodes[i].length];
}
}
this.cachedZoomLevel = Byte.MIN_VALUE;
}
}