package com.revolsys.gis.parallel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.jexl.Expression;
import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.context.HashMapContext;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.LineCap;
import com.revolsys.geometry.model.LineJoin;
import com.revolsys.geometry.simplify.DouglasPeuckerSimplifier;
import com.revolsys.io.PathName;
import com.revolsys.parallel.channel.Channel;
import com.revolsys.parallel.process.BaseInOutProcess;
import com.revolsys.record.ArrayRecord;
import com.revolsys.record.Record;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.record.schema.RecordDefinitionImpl;
import com.revolsys.util.JexlUtil;
public class CreateObjectsWithinDistanceOfGeometry extends BaseInOutProcess<Record, Record> {
private Map<String, Object> attributes = new HashMap<>();
private double distance;
private Channel<Record> geometryIn;
private List<Record> geometryObjects = new ArrayList<>();
private Map<RecordDefinition, Map<RecordDefinition, Geometry>> recordDefinitionGeometryMap = new HashMap<>();
private String typePathTemplate;
private Expression typePathTemplateExpression;
private boolean writeOriginal;
@Override
protected void destroy() {
super.destroy();
if (this.geometryIn != null) {
this.geometryIn.readDisconnect();
this.geometryIn = null;
}
this.attributes = null;
this.geometryObjects = null;
this.recordDefinitionGeometryMap = null;
}
public double getDistance() {
return this.distance;
}
public Map<String, Object> getFields() {
return this.attributes;
}
public Channel<Record> getGeometryIn() {
if (this.geometryIn == null) {
setGeometryIn(new Channel<Record>());
}
return this.geometryIn;
}
public List<Record> getGeometryObjects() {
return this.geometryObjects;
}
private final Map<RecordDefinition, Geometry> getRecordDefinitionGeometries(
final RecordDefinition recordDefinition) {
Map<RecordDefinition, Geometry> recordDefinitionGeometries = this.recordDefinitionGeometryMap
.get(recordDefinition);
if (recordDefinitionGeometries == null) {
recordDefinitionGeometries = new LinkedHashMap<>();
RecordDefinition newRecordDefinition;
Geometry preparedGeometry;
for (final Record record : this.geometryObjects) {
Geometry geometry = record.getGeometry();
if (geometry != null) {
final JexlContext context = new HashMapContext();
final Map<String, Object> vars = new HashMap<>(this.attributes);
vars.putAll(record);
vars.put("typePath", recordDefinition.getPath());
context.setVars(vars);
final String typePath = (String)JexlUtil.evaluateExpression(context,
this.typePathTemplateExpression);
newRecordDefinition = new RecordDefinitionImpl(PathName.newPathName(typePath),
recordDefinition.getFields());
if (this.distance > 0) {
geometry = geometry.buffer(this.distance, 1, LineCap.SQUARE, LineJoin.MITER, 1.0D);
}
geometry = DouglasPeuckerSimplifier.simplify(geometry, 2D);
preparedGeometry = geometry.prepare();
recordDefinitionGeometries.put(newRecordDefinition, preparedGeometry);
}
}
this.recordDefinitionGeometryMap.put(recordDefinition, recordDefinitionGeometries);
}
return recordDefinitionGeometries;
}
public String getTypeNameTemplate() {
return this.typePathTemplate;
}
private void initializeGeometries(final Channel<Record> geometryIn) {
if (geometryIn != null) {
for (final Record object : geometryIn) {
this.geometryObjects.add(object);
}
}
}
public boolean isWriteOriginal() {
return this.writeOriginal;
}
@Override
protected void preRun(final Channel<Record> in, final Channel<Record> out) {
initializeGeometries(this.geometryIn);
}
@Override
protected void process(final Channel<Record> in, final Channel<Record> out, final Record object) {
if (this.writeOriginal) {
out.write(object);
}
final RecordDefinition recordDefinition = object.getRecordDefinition();
final Geometry geometryValue = object.getGeometry();
final Map<RecordDefinition, Geometry> recordDefinitionGeometries = getRecordDefinitionGeometries(
recordDefinition);
for (final Entry<RecordDefinition, Geometry> recordDefinitionGeometry : recordDefinitionGeometries
.entrySet()) {
final RecordDefinition newRecordDefinition = recordDefinitionGeometry.getKey();
final Geometry intersectsGeometry = recordDefinitionGeometry.getValue();
if (intersectsGeometry.intersects(geometryValue)) {
final Record newRecord = new ArrayRecord(newRecordDefinition, object);
out.write(newRecord);
}
}
}
public void setAttributes(final Map<String, Object> attributes) {
this.attributes = attributes;
}
public void setDistance(final double distance) {
this.distance = distance;
}
public void setGeometryIn(final Channel<Record> geometryIn) {
this.geometryIn = geometryIn;
geometryIn.readConnect();
}
public void setGeometryObjects(final List<Record> geometryObjects) {
this.geometryObjects = geometryObjects;
}
public void setTypeNameTemplate(final String typePathTemplate) {
this.typePathTemplate = typePathTemplate;
try {
this.typePathTemplateExpression = JexlUtil.newExpression(typePathTemplate,
"%\\{([^\\}]+)\\}");
} catch (final Exception e) {
throw new IllegalArgumentException(new StringBuilder().append("Invalid type name template: ")
.append(typePathTemplate)
.toString(), e);
}
}
public void setWriteOriginal(final boolean writeOriginal) {
this.writeOriginal = writeOriginal;
}
}