/*
* 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.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.logging.Logger;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.GeoTools;
import org.geotools.process.spatialstatistics.enumeration.ContiguityType;
import org.geotools.process.spatialstatistics.enumeration.DistanceMethod;
import org.geotools.process.spatialstatistics.enumeration.SpatialConcept;
import org.geotools.process.spatialstatistics.enumeration.StandardizationMethod;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
/**
* SpatialWeightMatrix
*
* @author Minpa Lee, MangoSystem
*
* @source $URL$
*/
public class WeightMatrixBuilder {
protected static final Logger LOGGER = Logging.getLogger(WeightMatrixBuilder.class);
public double sumX = 0;
public double sumX2 = 0;
public double sumX3 = 0;
public double sumX4 = 0;
public double sumY = 0;
public double sumY2 = 0;
public double sumY3 = 0;
public double sumY4 = 0;
private List<SpatialEvent> events;
private double exponent = 1.0; // 1 or 2
private Hashtable<Object, Double> rowSum = new Hashtable<Object, Double>();
private double distanceBandWidth = 0;
private SpatialConcept spatialConcept = SpatialConcept.InverseDistance;
private boolean isContiguity = false;
private WeightMatrix weightMatrix;
private StandardizationMethod standardizationMethod = StandardizationMethod.None;
private boolean selfNeighbors = false;
private DistanceFactory factory = DistanceFactory.newInstance();
public WeightMatrixBuilder() {
}
public WeightMatrixBuilder(SpatialConcept spatialConcept,
StandardizationMethod standardizationMethod) {
this.spatialConcept = spatialConcept;
this.standardizationMethod = standardizationMethod;
this.exponent = spatialConcept == SpatialConcept.InverseDistanceSquared ? 2.0 : 1.0;
this.isContiguity = spatialConcept == SpatialConcept.ContiguityEdgesNodes
|| spatialConcept == SpatialConcept.ContiguityEdgesOnly
|| spatialConcept == SpatialConcept.ContiguityNodesOnly;
}
public List<SpatialEvent> getEvents() {
return this.events;
}
public double getDistanceBandWidth() {
return distanceBandWidth;
}
public void setDistanceBandWidth(double distanceBandWidth) {
this.distanceBandWidth = distanceBandWidth;
}
public DistanceMethod getDistanceMethod() {
return factory.getDistanceType();
}
public void setDistanceMethod(DistanceMethod distanceMethod) {
factory.setDistanceType(distanceMethod);
}
public boolean isSelfNeighbors() {
return selfNeighbors;
}
public void setSelfNeighbors(boolean selfNeighbors) {
this.selfNeighbors = selfNeighbors;
}
public WeightMatrix getWeightMatrix() {
return weightMatrix;
}
public double getMeanX() {
return this.sumX / this.getEvents().size();
}
public double getMeanY() {
return this.sumY / this.getEvents().size();
}
public WeightMatrix buildWeightMatrix(SimpleFeatureCollection inputFeatures, String xField) {
return buildWeightMatrix(inputFeatures, xField, null);
}
public WeightMatrix buildWeightMatrix(SimpleFeatureCollection inputFeatures, Expression xField) {
return buildWeightMatrix(inputFeatures, xField, null);
}
public WeightMatrix buildWeightMatrix(SimpleFeatureCollection inputFeatures, String xField,
String yField) {
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
Expression xExpression = null;
if (xField != null && !xField.isEmpty()) {
xExpression = ff.property(xField);
}
Expression yExpression = null;
if (yField != null && !yField.isEmpty()) {
yExpression = ff.property(yField);
}
return buildWeightMatrix(inputFeatures, xExpression, yExpression);
}
public WeightMatrix buildWeightMatrix(SimpleFeatureCollection inputFeatures, Expression xField,
Expression yField) {
this.events = loadEvents(inputFeatures, xField, yField);
if (isContiguity) {
WeightMatrixContiguity contiguity = new WeightMatrixContiguity();
contiguity.setSelfNeighbors(isSelfNeighbors());
if (spatialConcept == SpatialConcept.ContiguityEdgesNodes) {
contiguity.setContiguityType(ContiguityType.Queen);
} else if (spatialConcept == SpatialConcept.ContiguityEdgesOnly) {
contiguity.setContiguityType(ContiguityType.Rook);
} else if (spatialConcept == SpatialConcept.ContiguityNodesOnly) {
contiguity.setContiguityType(ContiguityType.Bishops);
}
weightMatrix = contiguity.execute(inputFeatures, null);
} else {
if (spatialConcept == SpatialConcept.KNearestNeighbors) {
WeightMatrixKNearestNeighbors swmKnearest = new WeightMatrixKNearestNeighbors();
swmKnearest.setSelfNeighbors(isSelfNeighbors());
weightMatrix = swmKnearest.execute(inputFeatures, null);
} else {
if (distanceBandWidth == 0) {
distanceBandWidth = factory.getThresholDistance(inputFeatures);
}
WeightMatrixDistance wmsDist = new WeightMatrixDistance();
wmsDist.setDistanceMethod(getDistanceMethod());
wmsDist.setSpatialConcept(spatialConcept);
wmsDist.setStandardizationMethod(standardizationMethod);
wmsDist.setSelfNeighbors(isSelfNeighbors());
wmsDist.setThresholdDistance(distanceBandWidth);
weightMatrix = wmsDist.execute(inputFeatures, null);
}
}
if (standardizationMethod == StandardizationMethod.Row) {
calculateRowSum();
}
return weightMatrix;
}
public double getWeight(SpatialEvent source, SpatialEvent target) {
double weight = 0.0; // default
if (isContiguity) {
weight = weightMatrix.isNeighbor(source.id, target.id) ? 1.0 : 0.0;
} else {
double dist = factory.getDistance(source, target);
if (spatialConcept == SpatialConcept.InverseDistance) {
weight = dist <= 1.0 ? 1.0 : 1.0 / (Math.pow(dist, exponent)); // beta = 1
} else if (spatialConcept == SpatialConcept.InverseDistanceSquared) {
weight = dist <= 1.0 ? 1.0 : 1.0 / (Math.pow(dist, exponent)); // beta = 2
} else if (spatialConcept == SpatialConcept.FixedDistance) {
weight = dist <= distanceBandWidth ? 1.0 : 0.0;
} else if (spatialConcept == SpatialConcept.ZoneOfIndifference) {
weight = dist > distanceBandWidth ? 1.0 / ((dist - distanceBandWidth) + 1) : 1.0;
} else if (spatialConcept == SpatialConcept.KNearestNeighbors) {
weight = weightMatrix.isNeighbor(source.id, target.id) ? 1.0 : 0.0;
}
}
return weight;
}
public double standardizeWeight(SpatialEvent source, double weight) {
if (standardizationMethod == StandardizationMethod.Row) {
Double rowSum = this.rowSum.get(source.id);
return rowSum == 0 ? 0.0 : weight / rowSum;
}
return weight;
}
private void calculateRowSum() {
this.rowSum.clear();
for (SpatialEvent current : events) {
this.rowSum.put(current.id, getRowSum(current));
}
}
private double getRowSum(SpatialEvent source) {
double rowSum = 0.0;
for (SpatialEvent current : events) {
if (!selfNeighbors && source.id == current.id) {
continue;
}
rowSum += getWeight(source, current);
}
return rowSum;
}
private double getValue(SimpleFeature feature, Expression expression) {
Double value = expression.evaluate(feature, Double.class);
if (value == null) {
return Double.valueOf(1.0);
}
return value;
}
private List<SpatialEvent> loadEvents(SimpleFeatureCollection features, Expression xField,
Expression yField) {
List<SpatialEvent> eventList = new ArrayList<SpatialEvent>();
this.sumX = this.sumX2 = this.sumX3 = this.sumX4 = 0.0;
this.sumY = this.sumY2 = this.sumY3 = this.sumY4 = 0.0;
SimpleFeatureIterator featureIter = features.features();
try {
while (featureIter.hasNext()) {
SimpleFeature feature = featureIter.next();
Geometry geometry = (Geometry) feature.getDefaultGeometry();
Coordinate coordinate = geometry.getCentroid().getCoordinate();
SpatialEvent event = new SpatialEvent(feature.getID(), coordinate);
event.xVal = getValue(feature, xField);
sumX += event.xVal;
sumX2 += Math.pow(event.xVal, 2.0);
sumX3 += Math.pow(event.xVal, 3.0);
sumX4 += Math.pow(event.xVal, 4.0);
if (yField != null) {
event.yVal = getValue(feature, yField);
sumY += event.yVal;
sumY2 += Math.pow(event.yVal, 2.0);
sumY3 += Math.pow(event.yVal, 3.0);
sumY4 += Math.pow(event.yVal, 4.0);
}
eventList.add(event);
}
} finally {
featureIter.close();
}
return eventList;
}
}