/*******************************************************************************
* 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.functions;
import java.util.LinkedList;
import java.util.List;
import com.opendoorlogistics.api.geometry.ODLGeom;
import com.opendoorlogistics.api.tables.ODLColumnType;
import com.opendoorlogistics.core.cache.ApplicationCache;
import com.opendoorlogistics.core.cache.RecentlyUsedCache;
import com.opendoorlogistics.core.formulae.Function;
import com.opendoorlogistics.core.formulae.FunctionImpl;
import com.opendoorlogistics.core.formulae.FunctionParameters;
import com.opendoorlogistics.core.formulae.Functions;
import com.opendoorlogistics.core.geometry.ODLGeomImpl;
import com.opendoorlogistics.core.geometry.ODLLoadedGeometry;
import com.opendoorlogistics.core.tables.ColumnValueProcessor;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Polygon;
public class FmGeomBorder extends FunctionImpl {
public FmGeomBorder(Function geometry, Function includeHoles) {
super(geometry, includeHoles);
}
private Object createCacheKey(ODLGeom geom, boolean includeHoles){
class CacheKey{
ODLGeom geom;
boolean includeHoles;
public CacheKey(ODLGeom geom, boolean includeHoles) {
super();
this.geom = geom;
this.includeHoles = includeHoles;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((geom == null) ? 0 : geom.hashCode());
result = prime * result + (includeHoles ? 1231 : 1237);
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 (geom == null) {
if (other.geom != null)
return false;
} else if (!geom.equals(other.geom))
return false;
if (includeHoles != other.includeHoles)
return false;
return true;
}
}
Object cacheKey = new CacheKey(geom, includeHoles);
return cacheKey;
}
@Override
public Object execute(FunctionParameters parameters) {
// get input parameters
Object[] childEx = executeChildFormulae(parameters, false);
for(Object o :childEx){
if(o==null){
return null;
}
}
Object converted0 = ColumnValueProcessor.convertToMe(ODLColumnType.GEOM, childEx[0]);
Object converted1 = ColumnValueProcessor.convertToMe(ODLColumnType.LONG, childEx[1]);
if (converted0 == null || converted1 == null) {
return Functions.EXECUTION_ERROR;
}
ODLGeom geom = (ODLGeom) converted0;
boolean includeHoles = ((Long) converted1) == 1;
// check for cached result
RecentlyUsedCache cache = ApplicationCache.singleton().get(ApplicationCache.GEOMETRY_BORDER_CACHE);
Object cacheKey = createCacheKey(geom, includeHoles);
ODLGeom ret = (ODLGeom)cache.get(cacheKey);
if(ret!=null){
return ret;
}
Geometry g = ((ODLGeomImpl) geom).getJTSGeometry();
if (g == null) {
return null;
}
LinkedList<LineString> lineStrings = new LinkedList<>();
recurseFetchLineStrings(g, includeHoles, lineStrings);
if(lineStrings.size()==0){
// return null rather than an empty linestring as empty linestrings create problems
return null;
}
// create return object and cache it
Geometry gBorders = new GeometryFactory().createMultiLineString(lineStrings.toArray(new LineString[lineStrings.size()]));
ret = new ODLLoadedGeometry(gBorders);
cache.put(cacheKey, ret, ((ODLGeomImpl)ret).getEstimatedSizeInBytes() + 24);
return ret;
}
private static void recurseFetchLineStrings(Geometry g, boolean includeHoles, List<LineString> ret) {
if (g == null) {
return;
}
if (GeometryCollection.class.isInstance(g)) {
int nGeoms = g.getNumGeometries();
for (int i = 0; i < nGeoms; i++) {
recurseFetchLineStrings(g.getGeometryN(i), includeHoles, ret);
}
} else if (LineString.class.isInstance(g)) {
ret.add((LineString) g);
} else if (Polygon.class.isInstance(g)) {
Polygon poly = (Polygon) g;
ret.add(poly.getExteriorRing());
if (includeHoles) {
int nHoles = poly.getNumInteriorRing();
for (int i = 0; i < nHoles; i++) {
ret.add(poly.getInteriorRingN(i));
}
}
}
}
@Override
public Function deepCopy() {
return new FmGeomBorder(child(0).deepCopy(), child(1).deepCopy());
}
}