/* * 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 java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.mapsforge.core.model.GeoPoint; import org.mapsforge.core.model.Point; import org.mapsforge.core.util.MercatorProjection; import android.graphics.Path; /** * A {@code PolygonalChain} is a connected series of line segments specified by a list of {@link GeoPoint GeoPoints}. */ public class PolygonalChain { private final List<GeoPoint> geoPoints; /** * @param geoPoints * the initial GeoPoints of this polygonal chain (may be null). */ public PolygonalChain(Collection<GeoPoint> geoPoints) { if (geoPoints == null) { this.geoPoints = Collections.synchronizedList(new ArrayList<GeoPoint>()); } else { this.geoPoints = Collections.synchronizedList(new ArrayList<GeoPoint>(geoPoints)); } } /** * @return a synchronized (thread-safe) list of all GeoPoints of this polygonal chain. Manual synchronization on * this list is necessary when iterating over it. */ public List<GeoPoint> getGeoPoints() { synchronized (this.geoPoints) { return this.geoPoints; } } /** * @return true if the first and the last GeoPoint of this polygonal chain are equal, false otherwise. */ public boolean isClosed() { synchronized (this.geoPoints) { int numberOfGeoPoints = this.geoPoints.size(); if (numberOfGeoPoints < 2) { return false; } GeoPoint geoPointFirst = this.geoPoints.get(0); GeoPoint geoPointLast = this.geoPoints.get(numberOfGeoPoints - 1); return geoPointFirst.equals(geoPointLast); } } /** * @param zoomLevel * the zoom level at which this {@code PolygonalChain} should draw itself. * @param canvasPosition * the top-left pixel position of the canvas on the world map at the given zoom level. * @param closeAutomatically * whether the generated path should always be closed. * @return a {@code Path} representing this {@code PolygonalChain} (may be null). */ protected Path draw(byte zoomLevel, Point canvasPosition, boolean closeAutomatically) { synchronized (this.geoPoints) { int numberOfGeoPoints = this.geoPoints.size(); if (numberOfGeoPoints < 2) { return null; } Path path = new Path(); for (int i = 0; i < numberOfGeoPoints; ++i) { GeoPoint geoPoint = this.geoPoints.get(i); double latitude = geoPoint.latitude; double longitude = geoPoint.longitude; float pixelX = (float) (MercatorProjection.longitudeToPixelX(longitude, zoomLevel) - canvasPosition.x); float pixelY = (float) (MercatorProjection.latitudeToPixelY(latitude, zoomLevel) - canvasPosition.y); if (i == 0) { path.moveTo(pixelX, pixelY); } else { path.lineTo(pixelX, pixelY); } } if (closeAutomatically && !isClosed()) { path.close(); } return path; } } }