/* * 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.Logger; import co.foldingmap.map.MapObjectList; import java.io.Serializable; import java.util.*; /** * This class is used to keep a collection of VectorObject and provide efficient * methods to store and retrieve them. * * @author alecdhuse */ public class VectorObjectList<VectorObject> extends AbstractList<VectorObject> implements List<VectorObject>, Cloneable, Serializable, RandomAccess { private transient int firstIndex; private transient int lastIndex; private transient VectorObject[] array; protected ArrayList<LineString> lineStrings; protected ArrayList<MapPoint> points; protected ArrayList<MultiGeometry> multiGeometries; protected ArrayList<Polygon> polygons; /** * Constructs a new instance of VectorObjectList with the an initial capacity * of 10. * */ public VectorObjectList() { this(10); } /** * Constructs a new instance of VectorObjectList with the specified capacity. * * @param capacity * the initial capacity of this VectorObjectList. */ public VectorObjectList(int capacity) { if (capacity < 0) { throw new IllegalArgumentException(); } firstIndex = lastIndex = 0; array = newElementArray(capacity); lineStrings = new ArrayList<LineString>(); multiGeometries = new ArrayList<MultiGeometry>(); points = new ArrayList<MapPoint>(); polygons = new ArrayList<Polygon>(); } /** * Creates a new VectorObjectList from another collection of VectorObjects. * * @param collection */ public VectorObjectList(Collection<? extends VectorObject> collection) { this(collection.size()); Object[] dumpArray = collection.toArray(); if (dumpArray.length != 0) { if (dumpArray.length > array.length - lastIndex) { growAtEnd(dumpArray.length); } System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length); lastIndex += dumpArray.length; modCount++; for (int i = 0; i < collection.size(); i++) { VectorObject object = (VectorObject) dumpArray[i]; if (object instanceof MapPoint) { points.add((MapPoint) object); } else if (object instanceof LineString) { lineStrings.add((LineString) object); } else if (object instanceof Polygon) { polygons.add((Polygon) object); } else if (object instanceof MultiGeometry) { multiGeometries.add((MultiGeometry) object); } } } } public VectorObjectList(MapObjectList list) { this(list.size()); Object[] dumpArray = list.toArray(); if (dumpArray.length != 0) { if (dumpArray.length > array.length - lastIndex) { growAtEnd(dumpArray.length); } System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length); lastIndex += dumpArray.length; modCount++; for (int i = 0; i < list.size(); i++) { VectorObject object = (VectorObject) dumpArray[i]; if (object instanceof MapPoint) { points.add((MapPoint) object); } else if (object instanceof LineString) { lineStrings.add((LineString) object); } else if (object instanceof Polygon) { polygons.add((Polygon) object); } else if (object instanceof MultiGeometry) { multiGeometries.add((MultiGeometry) object); } } } } /** * Inserts the specified object into this {@code VectorObjectList} at the * specified location. The object is inserted before any previous element * at the specified location. If the location is equal to the size of this * {@code ArrayList}, the object is added at the end. * * @param location * the index at which to insert the object. * @param object * the object to add. * @throws IndexOutOfBoundsException * when {@code location < 0 || > size()} */ @Override public void add(int location, VectorObject object) { int size = lastIndex - firstIndex; if (0 < location && location < size) { if (firstIndex == 0 && lastIndex == array.length) { growForInsert(location, 1); } else if ((location < size / 2 && firstIndex > 0) || lastIndex == array.length) { System.arraycopy(array, firstIndex, array, --firstIndex, location); } else { int index = location + firstIndex; System.arraycopy(array, index, array, index + 1, size - location); lastIndex++; } array[location + firstIndex] = object; } else if (location == 0) { if (firstIndex == 0) { growAtFront(1); } array[--firstIndex] = object; } else if (location == size) { if (lastIndex == array.length) { growAtEnd(1); } array[lastIndex++] = object; } else { throw new IndexOutOfBoundsException("Index: " + Integer.valueOf(location) + " Size: " + Integer.valueOf(lastIndex - firstIndex)); } if (object instanceof MapPoint) { points.add((MapPoint) object); } else if (object instanceof LineString) { lineStrings.add((LineString) object); } else if (object instanceof Polygon) { polygons.add((Polygon) object); } else if (object instanceof MultiGeometry) { multiGeometries.add((MultiGeometry) object); } modCount++; } /** * Adds the specified object at the end of this VectorObjectList unless an * instance already exists in the list. * * @param object * The VectorObject to add. * @return * If the object was added or not. */ @Override public boolean add(VectorObject newObject) { try { boolean instanceFound = false; if (firstIndex != lastIndex) { //check to see if the coordinate exists already for (int i = firstIndex; i < lastIndex; i++) { if (array[i].equals(newObject)) { instanceFound = true; break; } } //end for loop } if (instanceFound == false) { forceAdd(newObject); return true; } else { return false; } } catch (Exception e) { System.err.println("Error in VectorObjectList.add() - " + e); return false; } } /** * Adds the objects in the specified collection to this * {@code VectorObjectList}. * * @param collection * the collection of objects. * @return {@code true} if this {@code VectorObjectList} is modified, * {@code false} otherwise. */ @Override public boolean addAll(Collection<? extends VectorObject> collection) { Object[] dumpArray = collection.toArray(); if (dumpArray.length == 0) { return false; } if (dumpArray.length > array.length - lastIndex) { growAtEnd(dumpArray.length); } System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length); lastIndex += dumpArray.length; modCount++; for (int i = 0; i < collection.size(); i++) { VectorObject object = (VectorObject) dumpArray[i]; if (object instanceof MapPoint) { points.add((MapPoint) object); } else if (object instanceof LineString) { lineStrings.add((LineString) object); } else if (object instanceof Polygon) { polygons.add((Polygon) object); } else if (object instanceof MultiGeometry) { multiGeometries.add((MultiGeometry) object); } } return true; } /** * Returns a new {@code VectorObjectList} with the same elements, the same * size and the same capacity as this {@code VectorObjectList}. * * @return a shallow copy of this {@code VectorObjectList} * @see java.lang.Cloneable */ @Override @SuppressWarnings("unchecked") public VectorObjectList clone() { try { VectorObjectList<VectorObject> newList = (VectorObjectList<VectorObject>) super.clone(); newList.array = array.clone(); return newList; } catch (CloneNotSupportedException e) { return null; } } /** * Returns if the supplied VectorObject already exists in the list. * * @param object * The VectorObject to check. * * @return If the VectorObject exist in the list or not. */ public boolean contains(co.foldingmap.map.vector.VectorObject object) { boolean result = false; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; if (currentObject == object) { result = true; break; } } return result; } /** * Returns if this object is equal to another. * * @param obj * @return */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof VectorObjectList) { VectorObjectList vol = (VectorObjectList) obj; return (this.hashCode() == vol.hashCode()); } else { return false; } } /** * Removes objects that are not within the boundary and shapes objects that * are to be completely within it. * * @param boundry * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> fitObjectsToBoundary(LatLonBox boundary) { boolean includeObject, objectChanged; boolean northEast, northWest, southEast, southWest; Coordinate min, max, newCoordinate; Coordinate c1, c2, c3; CoordinateList<Coordinate> objectCoordinates, newCoordinates; co.foldingmap.map.vector.VectorObject currentObject; VectorObjectList<co.foldingmap.map.vector.VectorObject> fittedObjects; try { fittedObjects = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); objectChanged = false; includeObject = false; min = new Coordinate(0, boundary.getNorth(), boundary.getWest()); max = new Coordinate(0, boundary.getSouth(), boundary.getEast()); for (int i = firstIndex; i < lastIndex; i++) { objectChanged = false; includeObject = false; northEast = false; northWest = false; southEast = false; southWest = false; currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; objectCoordinates = currentObject.getCoordinateList(); newCoordinates = new CoordinateList<Coordinate>(); for (Coordinate c: objectCoordinates) { if (boundary.contains(c)) { newCoordinates.add(c); includeObject = true; } else { objectChanged = true; if ((c.isSouthOf(min)) && (c.isNorthOf(max))) { if ((c.isEastOf(max)) && c.isEastOf(min)) { //Create a new coordinate from the old with the longitude set to the East boundary. newCoordinate = new Coordinate(c.getAltitude(), c.getLatitude(), boundary.getEast()); if (boundary.contains(newCoordinate)) { newCoordinates.add(newCoordinate); if (newCoordinates.size() > 2) { c1 = newCoordinates.get(newCoordinates.size() - 2); c2 = newCoordinates.get(newCoordinates.size() - 3); if ((c1.longitude == c2.longitude) && (c2.longitude == newCoordinate.longitude)) { //The last three corrdinates lay on the same longitude remove the center one if (! c2.isShared()) newCoordinates.remove(c1); } } } } else if (c.isWestOf(min, 90) && c.isWestOf(max, 90)) { //Create a new coordinate from the old with the longitude set to the West boundary. newCoordinate = new Coordinate(c.getAltitude(), c.getLatitude(), boundary.getWest()); if (boundary.contains(newCoordinate)) { newCoordinates.add(newCoordinate); if (newCoordinates.size() > 2) { c1 = newCoordinates.get(newCoordinates.size() - 2); c2 = newCoordinates.get(newCoordinates.size() - 3); if ((c1.longitude == c2.longitude) && (c2.longitude == newCoordinate.longitude)) { //The last three corrdinates lay on the same longitude remove the center one if (! c2.isShared()) newCoordinates.remove(c1); } } } } } else if ((c.isEastOf(min)) && (c.isEastOf(max))) { if ((c.isSouthOf(max)) && (c.isSouthOf(min))) { //Create a new coordinate from the old with the latitude set to the South boundary. newCoordinate = new Coordinate(c.getAltitude(), (boundary.getSouth()), c.getLongitude()); if (boundary.contains(newCoordinate)) { newCoordinates.add(newCoordinate); if (newCoordinates.size() > 2) { c1 = newCoordinates.get(newCoordinates.size() - 2); c2 = newCoordinates.get(newCoordinates.size() - 3); if ((c1.latitude == c2.latitude) && (c2.latitude == newCoordinate.latitude)) { //The last three corrdinates lay on the same latitude remove the center one if (! c2.isShared()) newCoordinates.remove(c1); } } } } else if ((c.isNorthOf(min)) && (c.isNorthOf(max))) { //Create a new coordinate from the old with the latitude set to the North boundary. newCoordinate = new Coordinate(c.getAltitude(), boundary.getNorth(), c.getLongitude()); if (boundary.contains(newCoordinate)) { newCoordinates.add(newCoordinate); if (newCoordinates.size() > 2) { c1 = newCoordinates.get(newCoordinates.size() - 2); c2 = newCoordinates.get(newCoordinates.size() - 3); if ((c1.latitude == c2.latitude) && (c2.latitude == newCoordinate.latitude)) { //The last three corrdinates lay on the same latitude remove the center one if (! c2.isShared()) newCoordinates.remove(c1); } } } } } else if ((c.isEastOf(min)) && (c.isWestOf(max, 90))) { if (c.isSouthOf(max)) { //Create a new coordinate from the old with the latitude set to the South boundary. newCoordinate = new Coordinate(c.getAltitude(), boundary.getSouth(), c.getLongitude()); if (boundary.contains(newCoordinate)) { newCoordinates.add(newCoordinate); if (newCoordinates.size() > 2) { c1 = newCoordinates.get(newCoordinates.size() - 2); c2 = newCoordinates.get(newCoordinates.size() - 3); if ((c1.latitude == c2.latitude) && (c2.latitude == newCoordinate.latitude)) { //The last three corrdinates lay on the same latitude remove the center one if (! c2.isShared()) newCoordinates.remove(c1); } } } } else if (c.isNorthOf(min)) { //Create a new coordinate from the old with the latitude set to the North boundary. newCoordinate = new Coordinate(c.getAltitude(), boundary.getNorth(), c.getLongitude()); if (boundary.contains(newCoordinate)) { newCoordinates.add(newCoordinate); if (newCoordinates.size() > 2) { c1 = newCoordinates.get(newCoordinates.size() - 2); c2 = newCoordinates.get(newCoordinates.size() - 3); if ((c1.latitude == c2.latitude) && (c2.latitude == newCoordinate.latitude)) { //The last three corrdinates lay on the same longitude remove the center one if (! c2.isShared()) newCoordinates.remove(c1); } } } } } //Ensures a point at the South-East edge of the crop if (!southEast && includeObject) { if (c.isSouthOf(max) && c.isEastOf(max)) { southEast = true; newCoordinate = new Coordinate(c.getAltitude(), (boundary.getSouth()), boundary.getEast()); newCoordinates.add(newCoordinate); } } //Ensures a point at the South-West edge of the crop if (!southWest && includeObject) { if (c.isSouthOf(max) && c.isWestOf(min, 90)) { southWest = true; newCoordinate = new Coordinate(c.getAltitude(), (boundary.getSouth()), boundary.getWest()); newCoordinates.add(newCoordinate); } } //Ensures a point at the North-East edge of the crop if (!northEast && includeObject) { if (c.isNorthOf(max) && c.isEastOf(max)) { northEast = true; newCoordinate = new Coordinate(c.getAltitude(), boundary.getNorth(), boundary.getEast()); if (currentObject.boundingBox.contains(newCoordinate)) newCoordinates.add(newCoordinate); } } if (!northWest && includeObject) { if (c.isNorthOf(min) && c.isWestOf(min, 90)) { northWest = true; newCoordinate = new Coordinate(c.getAltitude(), boundary.getNorth(), boundary.getWest()); newCoordinates.add(newCoordinate); } } } } if (includeObject) { if (!objectChanged) { fittedObjects.add(currentObject); } else { if (newCoordinates.size() > 0) { co.foldingmap.map.vector.VectorObject newObject = (co.foldingmap.map.vector.VectorObject) currentObject.copy(); newObject.setCoordinateList(newCoordinates); fittedObjects.add(newObject); } } } } } catch (Exception e) { System.err.println("Error in VectorObjectCollection.fitObjectsToBoundary(LatLonAltBox) " + e); fittedObjects = null; } return fittedObjects; } /** * Adds the specified object at the end of this VectorObjectList even if an * instance already exists in the list. * * @param object * the VectorObject to add. * @return always true */ public boolean forceAdd(VectorObject object) { try { if (lastIndex == array.length) { growAtEnd(3); } array[lastIndex++] = object; modCount++; if (object instanceof MapPoint) { points.add((MapPoint) object); } else if (object instanceof LineString) { lineStrings.add((LineString) object); } else if (object instanceof Polygon) { polygons.add((Polygon) object); } else if (object instanceof MultiGeometry) { multiGeometries.add((MultiGeometry) object); } return true; } catch (Exception e) { System.err.println("Error in VectorObject.forceAdd(Coordinate) - " + e); return false; } } /** * Returns the VectorObject at the given list location. * * @param location * @return */ @Override public VectorObject get(int location) { if (0 <= location && location < (lastIndex - firstIndex)) { return array[firstIndex + location]; } throw new IndexOutOfBoundsException("Index: " + Integer.valueOf(location) + " List Size: " + Integer.valueOf(lastIndex - firstIndex)); } /** * Returns the array for this List. * * @return */ public VectorObject[] getArray() { return array; } /** * Gets all the custom data field names associated with this object. * * @return Vector<String> A Vector containing all of the Custom Field Names. */ public ArrayList<String> getAllCustomDataFields() { ArrayList<String> allFields, objectFields; boolean stringFound; co.foldingmap.map.vector.VectorObject currentObject; allFields = new ArrayList<String>(); for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; objectFields = currentObject.getAllCustomDataFields(); for (String currentField: objectFields) { stringFound = false; for (String currentAllField: allFields) { if (currentField.equals(currentAllField)) stringFound = true; } if (!stringFound) allFields.add(currentField); } //end fields for loop } // end objects for loop return allFields; } /** * Returns all objects within a given Range. * * @param range * @return */ public VectorObjectList<VectorObject> getAllWithinRange(LatLonAltBox range) { LatLonAltBox currentRange; co.foldingmap.map.vector.VectorObject currentObject; VectorObjectList<VectorObject> objectsInRange; objectsInRange = new VectorObjectList<VectorObject>(); for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; currentRange = currentObject.getBoundingBox(); if (currentRange.overlaps(range)) objectsInRange.add((VectorObject) currentObject); } return (objectsInRange); } /** * Returns a LatLonAltBox in which all the objects in this list reside. * It this list is Empty then a LatLonAltBox with all dimentions equal * to zero is returned. * * @return */ public LatLonAltBox getBoundary() { co.foldingmap.map.vector.VectorObject currentObject; LatLonAltBox objectRange, range; try { //set the range to the first object's boundary box. currentObject = (co.foldingmap.map.vector.VectorObject) array[firstIndex]; if (currentObject != null) { range = currentObject.getBoundingBox(); for (int i = (firstIndex + 1); i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; if (currentObject != null) { objectRange = currentObject.getBoundingBox(); range = LatLonAltBox.combine(range, objectRange); } } } else { range = new LatLonAltBox(0,0,0,0,0,0); } return range; } catch (Exception e) { Logger.log(Logger.ERR, "Error in getBoundary() - " + e); return new LatLonAltBox(0,0,0,0,-1,-1); } } /** * Returns an organized list of of coordinates generated from the objects in * this VectorObjectList. * * @return */ public CoordinateList<Coordinate> getCoordinatesForMerge() { CoordinateList<Coordinate> tempCoordinates, compareCoordinateList, currentCoordinateList; VectorObjectList<co.foldingmap.map.vector.VectorObject> baseSet; Coordinate firstCurrentCoordinate, lastCurrentCoordinate; Coordinate firstCompareCoordinate, lastCompareCoordinate; float closest, bestTest, test1, test2, test3, test4; co.foldingmap.map.vector.VectorObject currentObject; co.foldingmap.map.vector.VectorObject compareObject, bestMatchObject; String bestMatchType; currentCoordinateList = new CoordinateList<Coordinate>(); try { baseSet = this.clone(); bestMatchObject = (co.foldingmap.map.vector.VectorObject) baseSet.get(0); bestMatchType = ""; compareCoordinateList = new CoordinateList<Coordinate>(); currentObject = (co.foldingmap.map.vector.VectorObject) baseSet.remove(0); currentCoordinateList = currentObject.getCoordinateList(); while (baseSet.size() > 0) { firstCurrentCoordinate = currentCoordinateList.get(0); lastCurrentCoordinate = currentCoordinateList.get(currentCoordinateList.size() - 1); closest = Float.MAX_VALUE; for (int j = (baseSet.size() - 1); j >= 0; j--) { compareObject = (co.foldingmap.map.vector.VectorObject) baseSet.get(j); if (!currentObject.equals(compareObject)) { compareCoordinateList = compareObject.getCoordinateList(); firstCompareCoordinate = compareCoordinateList.get(0); lastCompareCoordinate = compareCoordinateList.lastCoordinate(); test1 = CoordinateMath.getDistance(firstCurrentCoordinate, firstCompareCoordinate); test2 = CoordinateMath.getDistance(firstCurrentCoordinate, lastCompareCoordinate); test3 = CoordinateMath.getDistance(lastCurrentCoordinate, firstCompareCoordinate); test4 = CoordinateMath.getDistance(lastCurrentCoordinate, lastCompareCoordinate); bestTest = Math.min(Math.min(Math.min(test1, test2), test3), test4); if (bestTest < closest) { closest = bestTest; bestMatchObject = compareObject; if (test1 == bestTest) { bestMatchType = "first-first"; } else if (test2 == bestTest) { bestMatchType = "first-last"; } else if (test3 == bestTest) { bestMatchType = "last-first"; } else if (test4 == bestTest) { bestMatchType = "last-last"; } } //end if (bestTest < closest) }//end (currentObject != compareObject) } //end for j loop tempCoordinates = new CoordinateList<Coordinate>(); if (bestMatchType.equals("first-first")) { compareCoordinateList.reverse(); tempCoordinates.addAll(compareCoordinateList); tempCoordinates.addAll(currentCoordinateList); } else if (bestMatchType.equals("first-last")) { tempCoordinates.addAll(compareCoordinateList); tempCoordinates.addAll(currentCoordinateList); } else if (bestMatchType.equals("last-first")) { tempCoordinates.addAll(currentCoordinateList); tempCoordinates.addAll(compareCoordinateList); } else if (bestMatchType.equals("last-last")) { tempCoordinates.addAll(currentCoordinateList); compareCoordinateList.reverse(); tempCoordinates.addAll(compareCoordinateList); } currentCoordinateList = tempCoordinates; baseSet.remove(bestMatchObject); } //end while loop } catch (Exception e) { System.err.println("Error in VectorObjectList.getCoordinatesForMerge(): " + e); } return currentCoordinateList; } /** * Returns all the values for a specified custom field name of objects * in this layer. * * @param String The field name for the associated value. * @return ArrayList<String> The value for the passed in fieldName. */ public ArrayList<String> getCustomDataFieldValue(String fieldName) { ArrayList<String> values; co.foldingmap.map.vector.VectorObject object; String currentValue; values = new ArrayList<String>(); for (int i = firstIndex; i < lastIndex; i++) { object = (co.foldingmap.map.vector.VectorObject) array[i]; currentValue = object.getCustomDataFieldValue(fieldName); if (currentValue != null && !values.contains(currentValue)) { values.add(currentValue); } } return values; } /** * Returns the Eastern Most Longitude of all the objects in this list. * * @return */ public float getEasternMostLongitude() { float compaireLongitude; float longitude = -180f; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; compaireLongitude = (float) currentObject.getCoordinateList().getEasternMostLongitude(); if (compaireLongitude > longitude) longitude = compaireLongitude; } return longitude; } /** * Returns a new MspObjectList with copies of each object in the original * list. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> getFullCopy() { co.foldingmap.map.vector.VectorObject object; VectorObjectList<co.foldingmap.map.vector.VectorObject> copy; copy = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); for (int i = firstIndex; i < lastIndex; i++) { object = (co.foldingmap.map.vector.VectorObject) array[i]; copy.add((co.foldingmap.map.vector.VectorObject) object.copy()); } return copy; } /** * Returns the Index of a given object. * Returns -1 if the object cannot be found. * * @param object * @return */ public int getIndexOf(co.foldingmap.map.vector.VectorObject object) { int result = -1; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; if (currentObject == object) { result = i; break; } } return result; } /** * Returns only the LineStrings in this List. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> getLineStrings() { return new VectorObjectList(this.lineStrings); } /** * Returns only the MapPoints in this List. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> getMapPoints() { return new VectorObjectList(this.points); } /** * Returns only the MultiGeometries in this list. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> getMultiGeometries() { return new VectorObjectList(multiGeometries); } /** * Returns the Northern Most Latitude of any Object in this List. * * @return */ public float getNorthernMostLatitude() { float compaireLatitude; float latitude = -90; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; compaireLatitude = (float) currentObject.getCoordinateList().getNorthernMostLatitude(); if (compaireLatitude > latitude) latitude = compaireLatitude; } return latitude; } /** * Returns the VectorObject closes to the given Coordinate, null is the * List is empty. * * @param c * @return */ public co.foldingmap.map.vector.VectorObject getObjectClosestTo(Coordinate c) { CoordinateList<Coordinate> currentObjectCoordinates; float currentDistance, closestDistance; co.foldingmap.map.vector.VectorObject currentObject; co.foldingmap.map.vector.VectorObject objectToReturn; objectToReturn = null; closestDistance = Float.MAX_VALUE; try { for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; if (currentObject != null) { currentObjectCoordinates = currentObject.getCoordinateList(); for (Coordinate currentCoordinate: currentObjectCoordinates) { currentDistance = CoordinateMath.getDistance(currentCoordinate, c); if (currentDistance < closestDistance) { closestDistance = currentDistance; objectToReturn = currentObject; } } // end coordinate loop } //end null check } // end object loop } catch (Exception e) { System.err.println("Error in VectorObjectCollection.getObjectClosestTo.(Coordinate) - " + e); } return objectToReturn; } /** * Returns only the polygons in this List. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> getPolygons() { return new VectorObjectList(this.polygons); } /** * Returns the Southern most Latitude of an object in this list. * * @return */ public float getSouthernMostLatitude() { float compaireLatitude; float latitude = 90; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; compaireLatitude = (float) currentObject.getCoordinateList().getSouthernMostLatitude(); if (compaireLatitude < latitude) latitude = compaireLatitude; } return latitude; } /** * Returns the Western most Longitude of any object in this List. * * @return */ public float getWesternMostLongitude() { float compaireLongitude; float longitude = 180; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; compaireLongitude = (float) currentObject.getCoordinateList().getWesternMostLongitude(); if (compaireLongitude < longitude) longitude = compaireLongitude; } return longitude; } private void growAtEnd(int required) { int size = lastIndex - firstIndex; if (firstIndex >= required - (array.length - lastIndex)) { int newLast = lastIndex - firstIndex; if (size > 0) { System.arraycopy(array, firstIndex, array, 0, size); int start = newLast < firstIndex ? firstIndex : newLast; Arrays.fill(array, start, array.length, null); } firstIndex = 0; lastIndex = newLast; } else { int increment = size / 2; if (required > increment) increment = required; if (increment < 12) increment = 12; VectorObject[] newArray = newElementArray(size + increment); if (size > 0) { System.arraycopy(array, firstIndex, newArray, 0, size); firstIndex = 0; lastIndex = size; } array = newArray; } } private void growAtFront(int required) { int size = lastIndex - firstIndex; if (array.length - lastIndex + firstIndex >= required) { int newFirst = array.length - size; if (size > 0) { System.arraycopy(array, firstIndex, array, newFirst, size); int length = firstIndex + size > newFirst ? newFirst : firstIndex + size; Arrays.fill(array, firstIndex, length, null); } firstIndex = newFirst; lastIndex = array.length; } else { int increment = size / 2; if (required > increment) { increment = required; } if (increment < 12) { increment = 12; } VectorObject[] newArray = newElementArray(size + increment); if (size > 0) { System.arraycopy(array, firstIndex, newArray, newArray.length - size, size); } firstIndex = newArray.length - size; lastIndex = newArray.length; array = newArray; } } private void growForInsert(int location, int required) { int size = lastIndex - firstIndex; int increment = size / 2; if (required > increment) { increment = required; } if (increment < 12) { increment = 12; } VectorObject[] newArray = newElementArray(size + increment); int newFirst = increment - required; // Copy elements after location to the new array skipping inserted elements System.arraycopy(array, location + firstIndex, newArray, newFirst + location + required, size - location); // Copy elements before location to the new array from firstIndex System.arraycopy(array, firstIndex, newArray, newFirst, location); firstIndex = newFirst; lastIndex = size + increment; array = newArray; } /** * Creates a hash code for this Object. * * @return */ @Override public int hashCode() { int hash = 3; co.foldingmap.map.vector.VectorObject currentObject; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; hash += (currentObject != null ? currentObject.hashCode() : 0); } return hash; } @SuppressWarnings("unchecked") private VectorObject[] newElementArray(int size) { return (VectorObject[]) new Object[size]; } /** * Returns the index in the list of a given VectorObject. * * @param object * @return */ public int indexOf(co.foldingmap.map.vector.VectorObject object) { co.foldingmap.map.vector.VectorObject currentObject; int index = -1; for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; if (currentObject == object) { index = i; break; } } return index; } /** * Returns the last element in this list; * @return */ public VectorObject lastElement() { return array[lastIndex - 1]; } /** * Removes the object at the specified location from this list. * * @param location * the index of the object to remove. * @return the removed object. * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} */ @Override public VectorObject remove(int location) { VectorObject result; int size = lastIndex - firstIndex; if (0 <= location && location < size) { if (location == size - 1) { result = array[--lastIndex]; array[lastIndex] = null; } else if (location == 0) { result = array[firstIndex]; array[firstIndex++] = null; } else { int elementIndex = firstIndex + location; result = array[elementIndex]; if (location < size / 2) { System.arraycopy(array, firstIndex, array, firstIndex + 1, location); array[firstIndex++] = null; } else { System.arraycopy(array, elementIndex + 1, array, elementIndex, size - location - 1); array[--lastIndex] = null; } } if (firstIndex == lastIndex) { firstIndex = lastIndex = 0; } } else { throw new IndexOutOfBoundsException(Integer.valueOf(location) + " List size: " + Integer.valueOf(lastIndex - firstIndex)); } if (result instanceof MapPoint) { points.remove((MapPoint) result); } else if (result instanceof LineString) { lineStrings.remove((LineString) result); } else if (result instanceof Polygon) { polygons.remove((Polygon) result); } else if (result instanceof MultiGeometry) { multiGeometries.remove((MultiGeometry) result); } modCount++; return result; } /** * Removes a VectorObject from this List. * * @param object * @return If the object was removed or not. */ @Override public boolean remove(Object object) { boolean removed; removed = super.remove(object); if (removed) { if (object instanceof MapPoint) { points.remove((MapPoint) object); } else if (object instanceof LineString) { lineStrings.remove((LineString) object); } else if (object instanceof Polygon) { polygons.remove((Polygon) object); } else if (object instanceof MultiGeometry) { multiGeometries.remove((MultiGeometry) object); } } return removed; } /** * Returns the number of elements in this VectorObjectList. * * @return the number of elements in this VectorObjectList. */ @Override public int size() { return lastIndex - firstIndex; } public void sortByLayer() { VectorObjectList<co.foldingmap.map.vector.VectorObject> sortedObjects; VectorObjectList<co.foldingmap.map.vector.VectorObject> lineStrings, multis, points, polygons; VectorObjectList<co.foldingmap.map.vector.VectorObject> cityRoads, others, primaryHighways, reefs, secondaryHighways; VectorObjectList<co.foldingmap.map.vector.VectorObject> buildings, otherPolys, polyAreas, tramWays; String lineType; buildings = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); cityRoads = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); others = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); otherPolys = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); polyAreas = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); primaryHighways = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); secondaryHighways = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); sortedObjects = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); tramWays = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); reefs = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); lineStrings = getLineStrings(); multis = getMultiGeometries(); points = getMapPoints(); polygons = getPolygons(); sortedObjects.addAll(multis); this.removeAll(multis); //sort out different Polygons for (int i = 0; i < polygons.size(); i++) { Polygon currentPoly = (Polygon) polygons.get(i); if (currentPoly.getObjectClass().equalsIgnoreCase("Building")) { buildings.add(currentPoly); this.remove(currentPoly); } else if (currentPoly.getObjectClass().equalsIgnoreCase("Commercial Area") || currentPoly.getObjectClass().equalsIgnoreCase("Industrial Area") || currentPoly.getObjectClass().equalsIgnoreCase("Protected Area") || currentPoly.getObjectClass().equalsIgnoreCase("Residential Area") || currentPoly.getObjectClass().equalsIgnoreCase("University") || currentPoly.getObjectClass().equalsIgnoreCase("Wetland")) { polyAreas.add(currentPoly); this.remove(currentPoly); } else if (currentPoly.getObjectClass().equalsIgnoreCase("Ocean")) { sortedObjects.add(currentPoly); this.remove(currentPoly); } else if (currentPoly.getObjectClass().equalsIgnoreCase("Reef")) { reefs.add(currentPoly); this.remove(currentPoly); } else { otherPolys.add(currentPoly); this.remove(currentPoly); } } sortedObjects.addAll(reefs); sortedObjects.addAll(polyAreas); sortedObjects.addAll(otherPolys); sortedObjects.addAll(buildings); //sort out different LineStyles for (int i = 0; i < lineStrings.size(); i++) { LineString currentLine = (LineString) lineStrings.get(i); lineType = currentLine.getObjectClass(); if (lineType.equals("Road - City Primary") || lineType.equals("Road - City Secondary") || lineType.equals("Road - City Tertiary")) { cityRoads.add((co.foldingmap.map.vector.VectorObject) currentLine); this.remove(currentLine); } else if (lineType.equals("Road - Primary Highway") || lineType.equals("Road - Primary Highway Link")) { primaryHighways.add((co.foldingmap.map.vector.VectorObject) currentLine); this.remove(currentLine); } else if (lineType.equals("Road - Secondary Highway")) { secondaryHighways.add((co.foldingmap.map.vector.VectorObject) currentLine); this.remove(currentLine); } else if (lineType.equals("Rail - Tram")) { tramWays.add((co.foldingmap.map.vector.VectorObject) currentLine); this.remove(currentLine); } else { others.add((co.foldingmap.map.vector.VectorObject) currentLine); this.remove(currentLine); } } sortedObjects.addAll(others); sortedObjects.addAll(cityRoads); sortedObjects.addAll(secondaryHighways); sortedObjects.addAll(tramWays); sortedObjects.addAll(primaryHighways); sortedObjects.addAll(points); //copy anythign left in the array VectorObject[] theRestObjects = this.array.clone(); //clear the array and add the newly sorted objects this.clear(); for (co.foldingmap.map.vector.VectorObject vo: sortedObjects) this.add((VectorObject) vo); //add anything the was left behind for (VectorObject vo: theRestObjects) this.add(vo); } /** * Sorts objects so that object indexes are in the same order as their * east to west sorted VectorObjects. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> sortEastToWest() { double bestMatchValue; LatLonAltBox currentObjectBounds; co.foldingmap.map.vector.VectorObject bestMatchObject; co.foldingmap.map.vector.VectorObject currentObject; VectorObjectList<co.foldingmap.map.vector.VectorObject> objectsCopy, sortedCollection; objectsCopy = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); sortedCollection = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); try { for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; objectsCopy.add((co.foldingmap.map.vector.VectorObject) currentObject.copy()); } while (objectsCopy.size() > 0) { bestMatchObject = null; bestMatchValue = 0; for (co.foldingmap.map.vector.VectorObject object: objectsCopy) { currentObjectBounds = object.getBoundingBox(); if (currentObjectBounds.getEast() > bestMatchValue) { bestMatchValue = currentObjectBounds.getEast(); bestMatchObject = object; } } if (bestMatchObject != null) { objectsCopy.remove(bestMatchObject); sortedCollection.add(bestMatchObject); } else { //something went wrong sortedCollection.addAll(objectsCopy); break; } } } catch (Exception e) { System.err.println("Error in VectorObjectCollection.sortEastToWest() - " + e); } return sortedCollection; } /** * Sorts objects so that object indexes are in the same order as their * north to south sorted VectorObjects. * * @return */ public VectorObjectList<co.foldingmap.map.vector.VectorObject> sortNorthToSouth() { double bestMatchValue; LatLonAltBox currentObjectBounds; co.foldingmap.map.vector.VectorObject bestMatchObject; co.foldingmap.map.vector.VectorObject currentObject; VectorObjectList<co.foldingmap.map.vector.VectorObject> objectsCopy, sortedCollection; objectsCopy = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); sortedCollection = new VectorObjectList<co.foldingmap.map.vector.VectorObject>(); try { for (int i = firstIndex; i < lastIndex; i++) { currentObject = (co.foldingmap.map.vector.VectorObject) array[i]; objectsCopy.add(currentObject); } while (objectsCopy.size() > 0) { bestMatchObject = null; bestMatchValue = -99; for (co.foldingmap.map.vector.VectorObject object: objectsCopy) { currentObjectBounds = object.getBoundingBox(); if (currentObjectBounds.getNorth() > bestMatchValue) { bestMatchValue = currentObjectBounds.getNorth(); bestMatchObject = object; } } if (bestMatchObject != null) { objectsCopy.remove(bestMatchObject); sortedCollection.add(bestMatchObject); } else { //something went wrong sortedCollection.addAll(objectsCopy); break; } } } catch (Exception e) { System.err.println("Error in VectorObjectCollection.sortNorthToSouth() - " + e); } return sortedCollection; } }