Java tutorial
/******************************************************************************* * Copyright 2009 OpenSHA.org in partnership with * the Southern California Earthquake Center (SCEC, http://www.scec.org) * at the University of Southern California and the UnitedStates Geological * Survey (USGS; http://www.usgs.gov) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package org.opensha.commons.geo; import java.awt.geom.Path2D; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.apache.commons.io.IOUtils; import org.dom4j.Element; import org.opensha.commons.metadata.XMLSaveable; /** * A customized <code>ArrayList</code> of <code>Location</code>s. In addition to * providing the functionality of the <code>List</code> interface, this class * provides several custom methods for querying and manipulating a collection of * <code>Location</code>s. * * @author Peter Powers * @author Steven W. Rock * @version $Id: LocationList.java 10721 2014-05-21 21:51:22Z pmpowers $ */ public class LocationList extends ArrayList<Location> implements XMLSaveable, Serializable { private static final long serialVersionUID = 1L; public static final String XML_METADATA_NAME = "LocationList"; /** * Convenience method to reverse the <code>Location</code>s in this list. * Simply calls <code>Collections.reverse()</code>. */ public void reverse() { Collections.reverse(this); } /** * Breaks this <code>LocationList</code> into multiple parts. If * <code>size</code> is less than or equal to the size of this list, a * <code>List<LocationList></code> containing only this * <code>LocationList</code> is returned. The last element in the * <code>List<LocationList></code> will be a <code>LocationList</code> * of <code>size</code> or fewer <code>Location</code>s. * * @param size of the smaller lists * @return a <code>List<LocationList></code> of smaller * <code>LocationList</code>s */ public List<LocationList> split(int size) { ArrayList<LocationList> lists = new ArrayList<LocationList>(); // quickly handle the trivial case if (size <= 0 || size() <= size) { lists.add(this); return lists; } LocationList cur = new LocationList(); for (int i = 0; i < size(); i++) { if (i % size == 0 && i > 0) { lists.add(cur); cur = new LocationList(); } cur.add(get(i)); } if (cur.size() > 0) lists.add(cur); return lists; } /** * Returns a closed, stright-line {@link Path2D} representation of this list * @return a path representation of {@code this} */ public Path2D toPath() { Path2D path = new Path2D.Double(Path2D.WIND_EVEN_ODD, size()); boolean starting = true; for (Location loc : this) { double lat = loc.getLatitude(); double lon = loc.getLongitude(); // if just starting, then moveTo if (starting) { path.moveTo(lon, lat); starting = false; continue; } path.lineTo(lon, lat); } path.closePath(); return path; } /** * Computes the horizontal surface distance (in km) to the closest point in * this list from the supplied <code>Location</code>. This method uses * {@link LocationUtils#horzDistanceFast(Location, Location)} to compute the * distance. * * @param loc <code>Location</code> of interest * @return the distance to the closest point in this * <code>LocationList</code> * @see LocationUtils#horzDistanceFast(Location, Location) */ public double minDistToLocation(Location loc) { double min = Double.MAX_VALUE; double dist = 0; for (Location p : this) { dist = LocationUtils.horzDistanceFast(loc, p); if (dist < min) min = dist; } return min; } /** * Computes the shortest horizontal distance (in km) from the supplied * <code>Location</code> to the line defined by connecting the points in * this <code>LocationList</code>. This method uses * {@link LocationUtils#distanceToLineSegmentFast(Location, Location, Location)} * and is inappropriate for for use at large separations (e.g. >200 km). * * @param loc <code>Location</code> of interest * @return the shortest distance to the line defined by this * <code>LocationList</code> */ public double minDistToLine(Location loc) { double min = Double.MAX_VALUE; double dist = 0; for (int i = 1; i < size(); i++) { dist = Math.abs(LocationUtils.distanceToLineSegmentFast(get(i - 1), get(i), loc)); if (dist < min) min = dist; } return min; } /** * Overriden to return a <code>LocationList</code> with a deep copy of the * <code>Location</code>s spanned by the requested range. * * @return a deep copy of the range of <code>Location</code>s specified */ @Override public LocationList subList(int fromIndex, int toIndex) { List<Location> source = super.subList(fromIndex, toIndex); LocationList subLocList = new LocationList(); for (Location loc : source) { subLocList.add(loc.clone()); } return subLocList; } /** * Overriden to return a deep copy of this <code>LocationList</code>. * * @return a deep copy of this list */ @Override public LocationList clone() { LocationList clone = new LocationList(); for (Location loc : this) { clone.add(loc.clone()); } return clone; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof LocationList)) return false; LocationList ll = (LocationList) obj; if (size() != ll.size()) return false; for (int i = 0; i < size(); i++) { if (!(get(i).equals(ll.get(i)))) return false; } return true; } @Override public int hashCode() { int v = 0; boolean add = true; for (Location loc : this) { // make each smaller to avoid possible overrun of int int locCode = loc.hashCode() / 1000; v = (add) ? v + locCode : v - locCode; add = !add; } return v; } @Override public String toString() { // @formatter:off StringBuffer b = new StringBuffer().append("List size: ").append(size()).append(IOUtils.LINE_SEPARATOR) .append("Locations: "); for (Location loc : this) { b.append(loc).append(IOUtils.LINE_SEPARATOR).append(" "); } return b.toString(); // @formatter:on } public Element toXMLMetadata(Element root) { return toXMLMetadata(root, LocationList.XML_METADATA_NAME); } public Element toXMLMetadata(Element root, String elemName) { Element locs = root.addElement(elemName); for (int i = 0; i < this.size(); i++) { Location loc = this.get(i); locs = loc.toXMLMetadata(locs); } return root; } public static LocationList fromXMLMetadata(Element locationElement) { LocationList locs = new LocationList(); Iterator<Element> it = locationElement.elementIterator(); while (it.hasNext()) { Element el = it.next(); if (el.getName().equals(Location.XML_METADATA_NAME)) { locs.add(Location.fromXMLMetadata(el)); } } return locs; } /** * Returns an unmodifiable view of this <code>LocationList</code>. Any calls * to methods that would result in a change to this list will throw an * <code>UnsupportedOperationException</code>. Clones of an unmodifiable * <code>LocationList</code> (deep-copies) are editable. * * @return an unmodifiable view of this list */ public LocationList unmodifiableList() { return new UnmodifiableLocationList(this); } private final static class UnmodifiableLocationList extends LocationList { private static final long serialVersionUID = 1L; final LocationList ll; UnmodifiableLocationList(LocationList ll) { this.ll = ll; } // Pass-through operations @Override public LocationList clone() { return ll.clone(); } @Override public boolean contains(Object o) { return ll.contains(o); } @Override public boolean containsAll(Collection<?> coll) { return ll.containsAll(coll); } @Override public boolean equals(Object o) { return ll.equals(o); } @Override public Location get(int index) { return ll.get(index); } @Override public int hashCode() { return ll.hashCode(); } @Override public int indexOf(Object o) { return ll.indexOf(o); } @Override public boolean isEmpty() { return ll.isEmpty(); } @Override public int lastIndexOf(Object o) { return ll.lastIndexOf(o); } @Override public int size() { return ll.size(); } @Override public Object[] toArray() { return ll.toArray(); } @Override public <T> T[] toArray(T[] a) { return ll.toArray(a); } @Override public String toString() { return ll.toString(); } @Override public LocationList subList(int fromIndex, int toIndex) { return ll.subList(fromIndex, toIndex); } // Unsupported operations @Override public boolean add(Location e) { throw new UnsupportedOperationException(); } @Override public void add(int index, Location element) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection<? extends Location> coll) { throw new UnsupportedOperationException(); } @Override public boolean addAll(int index, Collection<? extends Location> coll) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public Location remove(int index) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection<?> coll) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection<?> coll) { throw new UnsupportedOperationException(); } @Override public Location set(int index, Location element) { throw new UnsupportedOperationException(); } @Override public Iterator<Location> iterator() { return new Iterator<Location>() { Iterator<? extends Location> it = ll.iterator(); @Override public boolean hasNext() { return it.hasNext(); } @Override public Location next() { return it.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public ListIterator<Location> listIterator() { return listIterator(0); } @Override public ListIterator<Location> listIterator(final int index) { return new ListIterator<Location>() { ListIterator<? extends Location> it = ll.listIterator(index); @Override public boolean hasNext() { return it.hasNext(); } @Override public Location next() { return it.next(); } @Override public boolean hasPrevious() { return it.hasPrevious(); } @Override public Location previous() { return it.previous(); } @Override public int nextIndex() { return it.nextIndex(); } @Override public int previousIndex() { return it.previousIndex(); } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void set(Location e) { throw new UnsupportedOperationException(); } @Override public void add(Location e) { throw new UnsupportedOperationException(); } }; } } /** * @return the first location */ public Location first() { return get(0); } /** * @return the lats location */ public Location last() { return get(size() - 1); } /** * Computes the segment index that is closest to the supplied * {@code Location}. There are {@code LocationList.size() - 1} segment * indices. The endpoints of the returned segment index are {@code [n, n+1]}. * * @param loc {@code Location} of interest * @return the index of the closest segment */ public int minDistIndex(Location loc) { double min = Double.MAX_VALUE; int minIdx = -1; for (int i = 0; i < size() - 1; i++) { double dist = LocationUtils.distanceToLineSegmentFast(get(i), get(i + 1), loc); if (dist < min) { min = dist; minIdx = i; } } return minIdx; } /** * Returns the index of the {@code Location} in the list closest to the * supplied {@code Location}. * * @param loc {@code Location} of interest * @return the index of the closest point in the list */ public int closestPoint(Location loc) { double min = Double.MAX_VALUE; int minIdx = -1; for (int i = 0; i < size(); i++) { double dist = LocationUtils.horzDistanceFast(loc, get(i)); if (dist < min) { min = dist; minIdx = i; } } return minIdx; } }