/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2014, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. */ package org.geotools.process.spatialstatistics.pattern; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.process.spatialstatistics.core.DistanceFactory; import org.geotools.process.spatialstatistics.core.FormatUtils; import org.geotools.process.spatialstatistics.core.SSUtils; import org.geotools.process.spatialstatistics.core.SSUtils.StatEnum; import org.geotools.process.spatialstatistics.core.SpatialEvent; import org.geotools.process.spatialstatistics.enumeration.DistanceMethod; import org.geotools.process.spatialstatistics.operations.GeneralOperation; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import com.vividsolutions.jts.algorithm.ConvexHull; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.index.strtree.ItemBoundable; import com.vividsolutions.jts.index.strtree.ItemDistance; import com.vividsolutions.jts.index.strtree.STRtree; /** * Calculates a nearest neighbor index based on the average distance from each feature to its nearest neighboring feature. * * @author Minpa Lee, MangoSystem * * @source $URL$ */ public class NNIOperation extends GeneralOperation { protected static final Logger LOGGER = Logging.getLogger(NNIOperation.class); private final DistanceFactory factory = DistanceFactory.newInstance(); private DistanceMethod distanceMethod = DistanceMethod.Euclidean; private int featureCount = 0; private double studyArea = 0d; private double observedMeanDist = 0d; private String typeName = "Average Nearest Neighbor Ratio"; public void setDistanceType(DistanceMethod distanceMethod) { this.distanceMethod = distanceMethod; } public double getConvexHullArea(List<SpatialEvent> srcEvents) { Coordinate[] coordinates = new Coordinate[srcEvents.size()]; for (int k = 0; k < srcEvents.size(); k++) { SpatialEvent srcEvent = srcEvents.get(k); coordinates[k] = srcEvent.getCoordinate(); } ConvexHull cbxBuidler = new ConvexHull(coordinates, new GeometryFactory()); Geometry convexHull = cbxBuidler.getConvexHull(); return convexHull.getArea(); } public NearestNeighborResult execute(SimpleFeatureCollection features) { return execute(features, 0d); } public NearestNeighborResult execute(SimpleFeatureCollection features, double studyArea) { typeName = features.getSchema().getTypeName(); factory.setDistanceType(distanceMethod); // build spatial index final List<SpatialEvent> events = new ArrayList<SpatialEvent>(); final STRtree spatialIndex = new STRtree(); SimpleFeatureIterator featureIter = features.features(); try { while (featureIter.hasNext()) { SimpleFeature feature = featureIter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Coordinate centroid = geometry.getCentroid().getCoordinate(); SpatialEvent event = new SpatialEvent(feature.getID(), centroid); events.add(event); spatialIndex.insert(new Envelope(centroid), event); } } finally { featureIter.close(); } // calculate area featureCount = events.size(); if (studyArea == 0) { this.studyArea = getConvexHullArea(events); } else { this.studyArea = studyArea; } // calculate nearest neighbor index double distanceSum = 0.0; for (SpatialEvent source : events) { SpatialEvent nearest = (SpatialEvent) spatialIndex.nearestNeighbour(new Envelope( source.coordinate), source, new ItemDistance() { @Override public double distance(ItemBoundable item1, ItemBoundable item2) { SpatialEvent s1 = (SpatialEvent) item1.getItem(); SpatialEvent s2 = (SpatialEvent) item2.getItem(); if (s1.id.equals(s2.id)) { return Double.MAX_VALUE; } return s1.distance(s2); } }); distanceSum += factory.getDistance(source, nearest); } observedMeanDist = distanceSum / featureCount; return buildResult(); } private NearestNeighborResult buildResult() { double nearestNeighborIndex = 0; double expectedMeanDist = 0; double standardError = 0; double zScore = 0; double pValue = 0; if (studyArea <= 0) { expectedMeanDist = 0.0; nearestNeighborIndex = 0.0; standardError = 0.0; zScore = 0.0; pValue = 0.0; } else { expectedMeanDist = 0.5 * Math.sqrt(studyArea / featureCount); nearestNeighborIndex = observedMeanDist / expectedMeanDist; standardError = Math.sqrt(((4 - Math.PI) * studyArea) / (4 * Math.PI * featureCount * featureCount)); zScore = (observedMeanDist - expectedMeanDist) / standardError; pValue = SSUtils.zProb(zScore, StatEnum.BOTH); } NearestNeighborResult nni = new NearestNeighborResult(typeName); nni.setObserved_Point_Count(featureCount); nni.setStudy_Area(FormatUtils.round(studyArea)); nni.setObserved_Mean_Distance(FormatUtils.round(observedMeanDist)); nni.setExpected_Mean_Distance(FormatUtils.round(expectedMeanDist)); nni.setNearest_Neighbor_Ratio(FormatUtils.round(nearestNeighborIndex)); nni.setZ_Score(FormatUtils.round(zScore)); nni.setP_Value(FormatUtils.round(pValue)); nni.setStandard_Error(FormatUtils.round(standardError)); return nni; } public static final class NearestNeighborResult { String typeName; int observed_Point_Count = 0; double study_Area = 0; double observed_Mean_Distance = 0; double expected_Mean_Distance = 0; double nearest_Neighbor_Ratio = 0; double z_Score = 0; double p_Value = 0; double standard_Error = 0; public String getTypeName() { return typeName; } public void setTypeName(String typeName) { this.typeName = typeName; } public int getObserved_Point_Count() { return observed_Point_Count; } public void setObserved_Point_Count(int observed_Point_Count) { this.observed_Point_Count = observed_Point_Count; } public double getStudy_Area() { return study_Area; } public void setStudy_Area(double study_Area) { this.study_Area = study_Area; } public double getObserved_Mean_Distance() { return observed_Mean_Distance; } public void setObserved_Mean_Distance(double observed_Mean_Distance) { this.observed_Mean_Distance = observed_Mean_Distance; } public double getExpected_Mean_Distance() { return expected_Mean_Distance; } public void setExpected_Mean_Distance(double expected_Mean_Distance) { this.expected_Mean_Distance = expected_Mean_Distance; } public double getNearest_Neighbor_Ratio() { return nearest_Neighbor_Ratio; } public void setNearest_Neighbor_Ratio(double nearest_Neighbor_Ratio) { this.nearest_Neighbor_Ratio = nearest_Neighbor_Ratio; } public double getZ_Score() { return z_Score; } public void setZ_Score(double z_Score) { this.z_Score = z_Score; } public double getP_Value() { return p_Value; } public void setP_Value(double p_Value) { this.p_Value = p_Value; } public double getStandard_Error() { return standard_Error; } public void setStandard_Error(double standard_Error) { this.standard_Error = standard_Error; } public NearestNeighborResult(String typeName) { this.typeName = typeName; } @Override public String toString() { final String separator = System.getProperty("line.separator"); final DecimalFormat df = new DecimalFormat("##.######"); StringBuffer sb = new StringBuffer(); sb.append("|| Average Nearest Neighbor Summary").append(separator); sb.append("|| Observed Point Count: ").append(df.format(getObserved_Point_Count())) .append(separator); sb.append("|| Study Area: ").append(df.format(getStudy_Area())).append(separator); sb.append("|| Observed Mean Distance: ").append(df.format(getObserved_Mean_Distance())) .append(separator); sb.append("|| Expected Mean Distance: ").append(df.format(getExpected_Mean_Distance())) .append(separator); sb.append("|| Nearest Neighbor Ratio: ").append(df.format(getNearest_Neighbor_Ratio())) .append(separator); sb.append("|| Z-Score: ").append(df.format(getZ_Score())).append(separator); sb.append("|| p-value: ").append(df.format(getP_Value())).append(separator); sb.append("|| Standard Error: ").append(df.format(getStandard_Error())) .append(separator); return sb.toString(); } } }