/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License 3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
******************************************************************************/
package com.opendoorlogistics.core.geometry.operations;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import com.opendoorlogistics.api.geometry.ODLGeom;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.api.tables.ODLTableReadOnly;
import com.opendoorlogistics.core.cache.ApplicationCache;
import com.opendoorlogistics.core.cache.RecentlyUsedCache;
import com.opendoorlogistics.core.geometry.ODLGeomImpl;
import com.opendoorlogistics.core.geometry.ODLLoadedGeometry;
import com.opendoorlogistics.core.tables.ColumnValueProcessor;
import com.opendoorlogistics.core.utils.Pair;
import com.opendoorlogistics.core.utils.strings.Strings;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
public class GeomWeightedCentroid {
private static class CacheKey {
final HashSet<Pair<ODLGeom, Double>> items;
final String epsg;
public CacheKey(Collection<Pair<ODLGeom, Double>> items, String espg) {
super();
this.items = new HashSet<>(items);
this.epsg = espg;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((epsg == null) ? 0 : epsg.hashCode());
result = prime * result + ((items == null) ? 0 : items.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CacheKey other = (CacheKey) obj;
if (epsg == null) {
if (other.epsg != null)
return false;
} else if (!epsg.equals(other.epsg))
return false;
if (items == null) {
if (other.items != null)
return false;
} else if (!items.equals(other.items))
return false;
return true;
}
}
/**
* Calculate weighted centre using the input table and row ids.
* @param table
* @param rowIds
* @param geomColIndx
* @param weightColIndx Can be -1 if not using a column (each weight is 1)
* @param ESPGCode
* @return
*/
public ODLGeom calculate(ODLTableReadOnly table, long[] rowIds, int geomColIndx, int weightColIndx, String ESPGCode) {
if (rowIds == null || rowIds.length == 0) {
return null;
}
// put input values into a collection
ArrayList<Pair<ODLGeom, Double>> geoms = new ArrayList<>(rowIds.length);
for (long id : rowIds) {
// get geometry
Object geomVal = table.getValueById(id, geomColIndx);
if (geomVal == null) {
return null;
}
ODLGeom geom = (ODLGeom) ColumnValueProcessor.convertToMe(ODLColumnType.GEOM, geomVal);
if (geom == null) {
return null;
}
// get weight
Double d = 1.0;
if(weightColIndx>=0){
Object weightVal = table.getValueById(id, weightColIndx);
if (weightVal != null) {
d = (Double) ColumnValueProcessor.convertToMe(ODLColumnType.DOUBLE, weightVal);
if (d == null) {
return null;
}
}
}
geoms.add(new Pair<ODLGeom, Double>(geom, d));
}
return calculate(geoms, ESPGCode);
}
public ODLGeom calculate(Collection<Pair<ODLGeom, Double>> geoms, String ESPGCode) {
// check cache
CacheKey record = new CacheKey(geoms, ESPGCode);
RecentlyUsedCache cache = ApplicationCache.singleton().get(ApplicationCache.GEOM_CENTROID_CACHE);
ODLGeom ret = (ODLGeom) cache.get(record);
if (ret == null) {
GridTransforms transforms =Strings.isEmpty(ESPGCode)?null: new GridTransforms(ESPGCode);
// transform all geoms and get their individual centroids
double weightSum = 0;
double xSum = 0;
double ySum = 0;
for (Pair<ODLGeom, Double> pair : geoms) {
Geometry g = ((ODLGeomImpl) pair.getFirst()).getJTSGeometry();
if (g == null) {
return null;
}
g = transforms!=null? transforms.wgs84ToGrid(g):g;
Point pnt = g.getCentroid();
double w = pair.getSecond();
weightSum += w;
xSum += pnt.getX() * w;
ySum += pnt.getY() * w;
}
if (weightSum > 0) {
Point pGrid = new GeometryFactory().createPoint(new Coordinate(xSum / weightSum, ySum / weightSum));
Point pTrans = (Point)( transforms!=null?transforms.gridToWGS84(pGrid): pGrid);
ret = new ODLLoadedGeometry(pTrans);
// estimate size as size of keys as these will be the most expensive..
long nbBytes = 0;
for (Pair<ODLGeom, Double> pair : geoms) {
nbBytes += ((ODLGeomImpl) pair.getFirst()).getEstimatedSizeInBytes();
}
nbBytes += 100; // add some extra for hashset, cache record etc
cache.put(record, ret, nbBytes);
}
}
// if(ret!=null){
// System.out.println(ret);
// }
return ret;
}
}