/* * 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.core; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.index.strtree.ItemBoundable; import com.vividsolutions.jts.index.strtree.ItemDistance; import com.vividsolutions.jts.index.strtree.STRtree; /** * SpatialWeightMatrix - Distance based weights - k-Nearest Neighbors. <br> * K Nearest Neighbors (KNN) is a distance-based definition of neighbors where "k" refers to the number of neighbors of a location. It is computed as * the distance between a point and the number (k) of nearest neighbor points (i.e. the distance between the central points of polygons). * * @author Minpa Lee, MangoSystem * * @source $URL$ */ public class WeightMatrixKNearestNeighbors extends AbstractWeightMatrix { protected static final Logger LOGGER = Logging.getLogger(WeightMatrixKNearestNeighbors.class); // default number of neighbors = 8, maximum = 24 private int numberOfNeighbors = 8; private STRtree spatialIndex; private int featureCount = 0; public WeightMatrixKNearestNeighbors() { } public int getNumberOfNeighbors() { return numberOfNeighbors; } public void setNumberOfNeighbors(int numberOfNeighbors) { if (numberOfNeighbors > 24) { numberOfNeighbors = 24; LOGGER.log(Level.WARNING, "Maximum number of neighbors is 24!"); } this.numberOfNeighbors = numberOfNeighbors; } @Override public WeightMatrix execute(SimpleFeatureCollection features, String uniqueField) { uniqueField = FeatureTypes.validateProperty(features.getSchema(), uniqueField); this.uniqueFieldIsFID = uniqueField == null || uniqueField.isEmpty(); WeightMatrix matrix = new WeightMatrix(SpatialWeightMatrixType.Distance); matrix.setupVariables(features.getSchema().getTypeName(), uniqueField); // 1. extract centroid and build spatial index this.buildSpatialIndex(features, uniqueField); if (numberOfNeighbors >= featureCount) { insertAll(matrix, features, uniqueField); } else { KnnSearch knnSearch = new KnnSearch(spatialIndex); SimpleFeatureIterator featureIter = features.features(); try { while (featureIter.hasNext()) { SimpleFeature feature = featureIter.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Coordinate coordinate = geometry.getCentroid().getCoordinate(); Object primaryID = getFeatureID(feature, uniqueField); SpatialEvent soruce = new SpatialEvent(primaryID, coordinate); Object[] knns = knnSearch.kNearestNeighbour(new Envelope(coordinate), soruce, new ItemDistance() { @Override public double distance(ItemBoundable item1, ItemBoundable item2) { SpatialEvent s1 = (SpatialEvent) item1.getItem(); SpatialEvent s2 = (SpatialEvent) item2.getItem(); if (!isSelfNeighbors() && s1.id.equals(s2.id)) { return Double.MAX_VALUE; } return s1.distance(s2); } }, numberOfNeighbors); // build weight matrix for (Object object : knns) { SpatialEvent current = (SpatialEvent) object; matrix.visit(primaryID, current.id, soruce.distance(current)); } } } finally { featureIter.close(); } } return matrix; } private void insertAll(WeightMatrix matrix, SimpleFeatureCollection features, String uniqueField) { SimpleFeatureIterator featureIter = features.features(); try { while (featureIter.hasNext()) { SimpleFeature feature = featureIter.next(); Object primaryID = getFeatureID(feature, uniqueField); Geometry geometry = (Geometry) feature.getDefaultGeometry(); Coordinate coordinate = geometry.getCentroid().getCoordinate(); SimpleFeatureIterator subIter = features.features(); try { while (subIter.hasNext()) { SimpleFeature secondaryFeature = subIter.next(); Object secondaryID = getFeatureID(secondaryFeature, uniqueField); if (!this.isSelfNeighbors() && primaryID.equals(secondaryID)) { continue; } Geometry secGeom = (Geometry) secondaryFeature.getDefaultGeometry(); Coordinate secCoord = secGeom.getCentroid().getCoordinate(); matrix.visit(primaryID, secondaryID, coordinate.distance(secCoord)); } } finally { subIter.close(); } } } finally { featureIter.close(); } } private void buildSpatialIndex(SimpleFeatureCollection features, String uniqueField) { 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(); Object primaryID = getFeatureID(feature, uniqueField); spatialIndex.insert(new Envelope(centroid), new SpatialEvent(primaryID, centroid)); featureCount++; } } finally { featureIter.close(); } } }