package com.esri.geoevent.solutions.processor.incrementalPoint;
/*
* #%L
* Esri :: AGES :: Solutions :: Processor :: Geometry
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2013 - 2014 Esri
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.LinearUnit;
import com.esri.core.geometry.MapGeometry;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polyline;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.geometry.Unit;
import com.esri.ges.core.ConfigurationException;
import com.esri.ges.core.component.ComponentException;
import com.esri.ges.core.geoevent.DefaultFieldDefinition;
import com.esri.ges.core.geoevent.FieldDefinition;
import com.esri.ges.core.geoevent.FieldException;
import com.esri.ges.core.geoevent.FieldType;
import com.esri.ges.core.geoevent.GeoEvent;
import com.esri.ges.core.geoevent.GeoEventDefinition;
import com.esri.ges.core.geoevent.GeoEventPropertyName;
import com.esri.ges.core.validation.ValidationException;
import com.esri.ges.framework.i18n.BundleLogger;
import com.esri.ges.framework.i18n.BundleLoggerFactory;
import com.esri.ges.manager.geoeventdefinition.GeoEventDefinitionManager;
import com.esri.ges.manager.geoeventdefinition.GeoEventDefinitionManagerException;
import com.esri.ges.messaging.EventDestination;
import com.esri.ges.messaging.EventUpdatable;
import com.esri.ges.messaging.GeoEventCreator;
import com.esri.ges.messaging.GeoEventProducer;
import com.esri.ges.messaging.Messaging;
import com.esri.ges.messaging.MessagingException;
import com.esri.ges.processor.GeoEventProcessorBase;
import com.esri.ges.processor.GeoEventProcessorDefinition;
public class IncrementalPointProcessor extends GeoEventProcessorBase implements
GeoEventProducer, EventUpdatable {
class IncrementPoint
{
private Point point;
Integer nextVertexIndex;
IncrementPoint(Point p, Integer i)
{
this.point = p;
this.nextVertexIndex=i;
}
public Point getPoint()
{
return this.point;
}
public Integer getNextVertexIndex()
{
return this.nextVertexIndex;
}
}
private int processWkid;
private String outDef;
private List<FieldDefinition> fds;
private Boolean createDef = false;
private GeoEventDefinition ged;
private GeoEventCreator geoEventCreator;
private GeoEventDefinitionManager manager;
private Messaging messaging;
private GeoEventProducer geoEventProducer;
private SpatialReference processSr;
private SpatialReference outSr;
private String intervalType;
private Double distInterval;
private long timeInterval;
private Boolean usingTime;
private Boolean usingVertex;
private static final BundleLogger LOGGER = BundleLoggerFactory
.getLogger(IncrementalPointProcessor.class);
public IncrementalPointProcessor(GeoEventProcessorDefinition definition) throws ComponentException {
super(definition);
//spatial = s;
geoEventMutator = true;
}
@Override
public void send(GeoEvent geoEvent) throws MessagingException {
if (geoEventProducer != null && geoEvent != null)
geoEventProducer.send(geoEvent);
}
@Override
public void setId(String id) {
super.setId(id);
geoEventProducer = messaging
.createGeoEventProducer(new EventDestination(id + ":event"));
}
@Override
public void afterPropertiesSet() {
intervalType = properties.get("intervalType").getValueAsString();
if(intervalType.equals("time"))
{
timeInterval = (long)properties.get("timeinterval").getValue();
usingTime = true;
usingVertex=false;
}
else if(intervalType.equals("distance"))
{
distInterval = (Double)properties.get("distanceinterval").getValue();
usingTime = false;
usingVertex=false;
}
else if (intervalType.equals("vertex"))
{
usingTime=false;
usingVertex=true;
}
processWkid = (Integer)properties.get("wkid").getValue();
outDef = properties.get("outdefname").getValueAsString();
fds = new ArrayList<FieldDefinition>();
processSr = SpatialReference.create(processWkid);
try {
fds.add(new DefaultFieldDefinition("locationtimestamp", FieldType.Date, "TIMESTAMP"));
fds.add(new DefaultFieldDefinition("timefromstart", FieldType.Double, "TIME_FROM_START"));
fds.add(new DefaultFieldDefinition("distanceonline", FieldType.Double, "DISTANCE_ON_LINE"));
Collection<GeoEventDefinition>eventDefs = manager.searchGeoEventDefinitionByName(outDef);
Iterator<GeoEventDefinition>eventDefIt = eventDefs.iterator();
while(eventDefIt.hasNext())
{
GeoEventDefinition currentDef = eventDefIt.next();
manager.deleteGeoEventDefinition(currentDef.getGuid());
}
createDef = true;
}
catch(ConfigurationException e)
{
LOGGER.error(e.getMessage());
} catch (GeoEventDefinitionManagerException e) {
LOGGER.error(e.getMessage());
}
}
@Override
public synchronized void validate() throws ValidationException {
// Validation Phase ...
super.validate();
}
@Override
public GeoEvent process(GeoEvent ge) throws Exception {
if(ge.getGeometry().getGeometry().getType()!=Geometry.Type.Polyline)
return null;
if (createDef) {
createGeoEventDefinition(ge);
createDef=false;
}
Date timeStart = (Date)ge.getField("TIME_START");
Date timeEnd = (Date)ge.getField("TIME_END");
if(timeStart== null)
return null;
if(timeEnd==null)
return null;
long start = timeStart.getTime();
long end = timeEnd.getTime();
if(start >= end)
return null;
MapGeometry mg = ge.getGeometry();
MapGeometry mapGeo = null;
Boolean projected=false;
SpatialReference inSr = mg.getSpatialReference();
if (processWkid != mg.getSpatialReference().getID()) {
projected=true;
processSr = SpatialReference.create(processWkid);
Geometry g = GeometryEngine.project(mg.getGeometry(), inSr,
processSr);
mapGeo = new MapGeometry(g, processSr);
outSr = inSr;
} else {
mapGeo = mg;
outSr = mapGeo.getSpatialReference();
}
Geometry geo = mapGeo.getGeometry();
Polyline polyln = (Polyline)geo;
Point startPt = polyln.getPoint(0);
Unit unit = LinearUnit.create(9001);
LinearUnit lu = (LinearUnit) unit;
double distTotal = GeometryEngine.geodesicLength(polyln, processSr, lu);
if(usingVertex)
{
processVertices(ge, polyln, distTotal, start, end, lu, projected);
}
else
{
processIncrements(ge, polyln, startPt, distTotal, start, end, projected);
}
/*int numHops = 0;
long timeTotal = end - start;
if(usingTime)
{
distInterval = distTotal/(timeTotal/timeInterval);
numHops = (int) Math.floor(distTotal/distInterval);
}
else
{
numHops = (int) Math.floor(distTotal/distInterval);
timeInterval = timeTotal/numHops;
}
Integer ptIndex = 0;
GeoEvent msg = null;
//Geometry outGeo = null;
//Geometry projGeo = null;
for(int i = 0; i < numHops; ++i)
{
IncrementPoint ip = this.getNextPoint(polyln, startPt, ptIndex, distInterval);
Geometry outGeo = null;
Geometry projGeo = ip.getPoint();
if(projected)
{
outGeo = GeometryEngine.project(projGeo, processSr, outSr);
}
else
{
outGeo=projGeo;
}
MapGeometry outMapGeo = new MapGeometry(outGeo, mapGeo.getSpatialReference());
msg = createIncrementalPointGeoevent(ge, outMapGeo, timeStart, i);
send(msg);
startPt = (Point)projGeo;
ptIndex = ip.getNextVertexIndex();
}*/
return null;
}
private void processIncrements(GeoEvent ge, Polyline polyln, Point startPt, double distTotal, long start, long end, Boolean projected) throws MessagingException, FieldException
{
int numHops = 0;
long timeTotal = end - start;
Date timeStart = new Date(start);
if(usingTime)
{
distInterval = distTotal/(timeTotal/timeInterval);
numHops = (int) Math.floor(distTotal/distInterval);
}
else
{
numHops = (int) Math.floor(distTotal/distInterval);
timeInterval = timeTotal/numHops;
}
Integer ptIndex = 0;
GeoEvent msg = null;
//Geometry outGeo = null;
//Geometry projGeo = null;
for(int i = 0; i < numHops; ++i)
{
IncrementPoint ip = this.getNextPoint(polyln, startPt, ptIndex, distInterval);
Geometry outGeo = null;
Geometry projGeo = ip.getPoint();
if(projected)
{
outGeo = GeometryEngine.project(projGeo, processSr, outSr);
}
else
{
outGeo=projGeo;
}
MapGeometry outMapGeo = new MapGeometry(outGeo, outSr);
msg = createIncrementalPointGeoevent(ge, outMapGeo, timeStart, i);
send(msg);
startPt = (Point)projGeo;
ptIndex = ip.getNextVertexIndex();
}
}
private void processVertices(GeoEvent ge, Polyline polyln, double distTotal, long start, long end, LinearUnit lu, Boolean projected) throws MessagingException, FieldException
{
int count = polyln.getPointCount();
double currentDist = 0;
long currentTime = start;
long totalTime = end - start;
Geometry outGeo = null;
Point projGeo = null;
Point lastPoint = null;
for(int i = 0; i < count; ++i)
{
projGeo = polyln.getPoint(i);
if(i!=0)
{
Polyline seg = new Polyline();
seg.startPath(lastPoint);
seg.lineTo(projGeo);
double segDist = GeometryEngine.geodesicLength(seg, processSr, lu);
currentDist += segDist;
double percent = currentDist/distTotal;
currentTime = (long) Math.floor((start + (totalTime*percent)));
}
if(projected)
{
outGeo = GeometryEngine.project(projGeo, processSr, outSr);
}
else
{
outGeo=projGeo;
}
MapGeometry outMapGeo = new MapGeometry(outGeo, outSr);
double minutesFromStart = (currentTime - start)/60000;
GeoEvent msg = createVertexGeoevent(ge, outMapGeo, currentDist, currentTime, minutesFromStart, i);
send(msg);
lastPoint = projGeo;
}
}
private GeoEvent createVertexGeoevent(GeoEvent event, MapGeometry outGeo, double dist, long time, double timeFromStartMinutes, Integer increment) throws MessagingException, FieldException
{
Date ts = new Date(time);
Double distOnLine = dist;
GeoEvent msg = geoEventCreator.create(outDef, definition.getUri().toString());
for(FieldDefinition fd: event.getGeoEventDefinition().getFieldDefinitions())
{
if(fd.getTags().contains("GEOMETRY"))
{
msg.setGeometry(outGeo);
}
else if(fd.getTags().contains("TRACK_ID"))
{
String trackid = event.getTrackId() + "_" + increment.toString();
msg.setField("TRACK_ID", trackid);
}
else
{
msg.setField(fd.getName(), event.getField(fd.getName()));
}
}
msg.setField("TIMESTAMP", ts);
msg.setField("TIME_FROM_START", timeFromStartMinutes);
msg.setField("DISTANCE_ON_LINE", distOnLine);
msg.setProperty(GeoEventPropertyName.TYPE, "event");
msg.setProperty(GeoEventPropertyName.OWNER_ID, getId());
msg.setProperty(GeoEventPropertyName.OWNER_URI,
definition.getUri());
return msg;
}
private GeoEvent createIncrementalPointGeoevent(GeoEvent event, MapGeometry outGeo, Date timestart, Integer increment) throws MessagingException, FieldException
{
long multiplier = increment+1;
long timeFromStart = timeInterval*multiplier;
long incrementTime = timestart.getTime() + (timeFromStart);
double timeFromStartMinutes = timeFromStart/60000.0;
Double distOnLine = distInterval*multiplier;
Date ts = new Date(incrementTime);
GeoEvent msg = geoEventCreator.create(outDef, definition.getUri().toString());
for(FieldDefinition fd: event.getGeoEventDefinition().getFieldDefinitions())
{
if(fd.getTags().contains("GEOMETRY"))
{
msg.setGeometry(outGeo);
}
else if(fd.getTags().contains("TRACK_ID"))
{
String trackid = event.getTrackId() + "_" + increment.toString();
msg.setField("TRACK_ID", trackid);
}
else
{
msg.setField(fd.getName(), event.getField(fd.getName()));
}
}
msg.setField("TIMESTAMP", ts);
msg.setField("TIME_FROM_START", timeFromStartMinutes);
msg.setField("DISTANCE_ON_LINE", distOnLine);
msg.setProperty(GeoEventPropertyName.TYPE, "event");
msg.setProperty(GeoEventPropertyName.OWNER_ID, getId());
msg.setProperty(GeoEventPropertyName.OWNER_URI,
definition.getUri());
return msg;
}
private void createGeoEventDefinition(GeoEvent event)
{
GeoEventDefinition eventDef = event.getGeoEventDefinition();
try {
ged = eventDef.augment(fds);
} catch (ConfigurationException e) {
LOGGER.error(e.getLocalizedMessage());
}
ged.setName(outDef);
ged.setOwner(definition.getUri().toString());
try {
manager.addGeoEventDefinition(ged);
} catch (GeoEventDefinitionManagerException e) {
LOGGER.error(e.getMessage());
}
}
@Override
public void shutdown() {
// Destruction Phase
super.shutdown();
}
@Override
public boolean isGeoEventMutator() {
return true;
}
@Override
public EventDestination getEventDestination() {
return (geoEventProducer != null) ? geoEventProducer
.getEventDestination() : null;
}
@Override
public List<EventDestination> getEventDestinations() {
return (geoEventProducer != null) ? Arrays.asList(geoEventProducer
.getEventDestination()) : new ArrayList<EventDestination>();
}
@Override
public void disconnect() {
if (geoEventProducer != null)
geoEventProducer.disconnect();
}
@Override
public boolean isConnected() {
return (geoEventProducer != null) ? geoEventProducer.isConnected()
: false;
}
@Override
public String getStatusDetails() {
return (geoEventProducer != null) ? geoEventProducer.getStatusDetails()
: "";
}
@Override
public void setup() throws MessagingException {
;
}
@Override
public void init() throws MessagingException {
;
}
@Override
public void update(Observable o, Object arg) {
;
}
@Override
public void onServiceStart() {
// Service Start Phase
}
@Override
public void onServiceStop() {
// Service Stop Phase
}
private IncrementPoint getNextPoint(Polyline polyln, Point startPt, Integer i, Double dist)
{
Point startVertex = polyln.getPoint(i);
Double currentDist = GeometryEngine.distance(startPt, startVertex, processSr);
Point segStart = null;
Point segEnd = null;
Boolean multipleVertices = true;
if(currentDist > dist)
{
segStart = startPt;
segEnd = startVertex;
multipleVertices = false;
}
while(currentDist < dist)
{
Point start = polyln.getPoint(i);
Point end = polyln.getPoint(i+1);
currentDist += GeometryEngine.distance(start, end, processSr);
++i;
}
if(multipleVertices)
{
segStart = polyln.getPoint(i-1);
segEnd = polyln.getPoint(i);
}
Double segLen = GeometryEngine.distance(segStart, segEnd, processSr);
Double distOver = currentDist - dist;
Double distOnSeg = segLen - distOver;
Point p = findPtOnSegment(segStart, segEnd, distOnSeg);
IncrementPoint ip = new IncrementPoint(p, i);
return ip;
}
private Point findPtOnSegment(Point segStart, Point segEnd, Double d)
{
Point pt = null;
Double x1, y1, x2, y2;
x1 = segStart.getX();
y1 = segStart.getY();
x2 = segEnd.getX();
y2 = segEnd.getY();
Double diffXsquare = Math.pow((x2-x1), 2);
Double diffYsquare = Math.pow((y2-y1), 2);
Double x = x1 + d*(x2-x1)/Math.sqrt(diffXsquare+diffYsquare);
Double y = y1 + d*(y2-y1)/Math.sqrt(diffXsquare + diffYsquare);
pt = new Point(x,y);
return pt;
}
public void setManager(GeoEventDefinitionManager manager)
{
this.manager = manager;
}
public void setMessaging(Messaging messaging)
{
this.messaging = messaging;
this.geoEventCreator = messaging.createGeoEventCreator();
}
}