package com.esri.geoevent.solutions.processor.trackidle;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.esri.core.geometry.Geometry.Type;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.MapGeometry;
import com.esri.core.geometry.Point;
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.DefaultGeoEventDefinition;
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.GeoEventCreator;
import com.esri.ges.messaging.Messaging;
import com.esri.ges.messaging.MessagingException;
import com.esri.ges.processor.GeoEventProcessorBase;
import com.esri.ges.processor.GeoEventProcessorDefinition;
import com.esri.ges.util.Converter;
import com.esri.ges.util.Validator;
public class TrackIdleProcessor extends GeoEventProcessorBase {
private static final BundleLogger LOGGER = BundleLoggerFactory
.getLogger(TrackIdleProcessor.class);
private TrackIdleProcessorNotificationMode notificationMode;
private long idleLimit;
private GeoEventCreator geoEventCreator;
private GeoEventDefinitionManager manager;
private String outDefName;
private long tolerance;
private Boolean keepFields;
private List<FieldDefinition> fds;
private Boolean createDef = false;
private GeoEventDefinition ged;
private final Map<String, TrackIdleProcessorStart> trackIdles = new ConcurrentHashMap<String, TrackIdleProcessorStart>();
protected TrackIdleProcessor(GeoEventProcessorDefinition definition)
throws ComponentException {
super(definition);
}
public void afterPropertiesSet() {
notificationMode = Validator.valueOfIgnoreCase(
TrackIdleProcessorNotificationMode.class,
getProperty("notificationMode").getValueAsString(),
TrackIdleProcessorNotificationMode.OnChange);
idleLimit = Converter.convertToInteger(getProperty("idleLimit")
.getValueAsString(), 300);
tolerance = Converter.convertToLong(getProperty("tolerance")
.getValueAsString(), 50l);
keepFields = (Boolean) getProperty("keepfields").getValue();
outDefName = getProperty("outdefname").getValueAsString();
fds = new ArrayList<FieldDefinition>();
try {
//fds.add(new DefaultFieldDefinition("trackId", FieldType.String,
//"TRACK_ID"));
fds.add(new DefaultFieldDefinition("idle", FieldType.Boolean));
fds.add(new DefaultFieldDefinition("idleDuration", FieldType.Double));
fds.add(new DefaultFieldDefinition("idleStart", FieldType.Date));
//fds.add(new DefaultFieldDefinition("geometry", FieldType.Geometry));
if ((ged = manager.searchGeoEventDefinition(outDefName, definition.getUri().toString())) == null)
{
createDef = true;
}
} catch (ConfigurationException e) {
}
// geoEventDefinitions.put(ged.getName(), ged);
}
@Override
public GeoEvent process(GeoEvent geoEvent) throws Exception {
GeoEvent msg = null;
if (createDef) {
createGeoEventDefinition(geoEvent, keepFields);
createDef=false;
}
if (geoEvent.getTrackId() == null || geoEvent.getGeometry() == null) {
LOGGER.warn("NULL_ERROR");
return null;
}
if (trackIdles == null) {
LOGGER.warn("TRACK_IDLES_NULL");
return null;
}
try {
String cacheKey = buildCacheKey(geoEvent);
TrackIdleProcessorStart idleStart = trackIdles.get(cacheKey);
Date startTime = (Date)geoEvent.getField("TIME_START");
long currentStartTime = startTime.getTime();
if (idleStart != null && idleStart.getGeometry() != null) {
if (!hasGeometryMoved(geoEvent.getGeometry(),
idleStart.getGeometry(), tolerance)) {
double idleDuration = (currentStartTime - idleStart
.getStartTime().getTime()) / 1000.0;
idleDuration = idleDuration >= 0 ? idleDuration
: -idleDuration;
idleDuration = Math.round(idleDuration * 10.0) / 10.0;
if (idleDuration >= idleLimit) {
idleStart.setIdleDuration(idleDuration);
if (notificationMode == TrackIdleProcessorNotificationMode.Continuous)
msg = createTrackIdleGeoEvent(idleStart, true,
geoEvent, ged);
else if (!idleStart.isIdling())
msg = createTrackIdleGeoEvent(idleStart, true,
geoEvent, ged);
idleStart.setIdling(true);
}
}
else
{
if (idleStart.isIdling())
{
msg = createTrackIdleGeoEvent(idleStart, false, geoEvent, ged);
}
idleStart.setGeometry(geoEvent.getGeometry());
idleStart.setStartTime(geoEvent.getStartTime());
idleStart.setIdling(false);
}
} else {
trackIdles.put(
cacheKey,
new TrackIdleProcessorStart(geoEvent.getTrackId(), startTime, geoEvent.getGeometry()));
}
} catch (Exception error) {
LOGGER.error(error.getMessage(), error);
}
return msg;
}
@Override
public void validate() throws ValidationException {
super.validate();
List<String> errors = new ArrayList<String>();
if (idleLimit <= 0)
errors.add(LOGGER.translate("VALIDATION_GAP_DURATION_INVALID",
definition.getName()));
if (errors.size() > 0) {
StringBuffer sb = new StringBuffer();
for (String message : errors)
sb.append(message).append("\n");
throw new ValidationException(LOGGER.translate("VALIDATION_ERROR",
this.getClass().getName(), sb.toString()));
}
}
private GeoEvent processGeoEvent(GeoEvent geoEvent)
throws GeoEventDefinitionManagerException {
GeoEvent geoevent = null;
if (createDef) {
createGeoEventDefinition(geoEvent, keepFields);
createDef=false;
}
if (geoEvent.getTrackId() == null || geoEvent.getGeometry() == null) {
LOGGER.warn("NULL_ERROR");
return null;
}
if (trackIdles == null) {
LOGGER.warn("TRACK_IDLES_NULL");
return null;
}
try {
String cacheKey = buildCacheKey(geoEvent);
TrackIdleProcessorStart idleStart = trackIdles.get(cacheKey);
Date startTime = (Date)geoEvent.getField("TIME_START");
long currentStartTime = startTime.getTime();
if (idleStart != null && idleStart.getGeometry() != null) {
if (!hasGeometryMoved(geoEvent.getGeometry(),
idleStart.getGeometry(), tolerance)) {
double idleDuration = (currentStartTime - idleStart
.getStartTime().getTime()) / 1000.0;
idleDuration = idleDuration >= 0 ? idleDuration
: -idleDuration;
idleDuration = Math.round(idleDuration * 10.0) / 10.0;
if (idleDuration >= idleLimit) {
idleStart.setIdleDuration(idleDuration);
if (notificationMode == TrackIdleProcessorNotificationMode.Continuous)
geoevent = createTrackIdleGeoEvent(idleStart, true,
geoEvent, ged);
else if (!idleStart.isIdling())
geoevent = createTrackIdleGeoEvent(idleStart, true,
geoEvent, ged);
idleStart.setIdling(true);
}
}
else
{
if (idleStart.isIdling())
{
geoevent = createTrackIdleGeoEvent(idleStart, false, geoEvent, ged);
}
idleStart.setGeometry(geoEvent.getGeometry());
idleStart.setStartTime(geoEvent.getStartTime());
idleStart.setIdling(false);
}
} else {
trackIdles.put(
cacheKey,
new TrackIdleProcessorStart(geoEvent.getTrackId(), startTime, geoEvent.getGeometry()));
}
} catch (Exception error) {
LOGGER.error(error.getMessage(), error);
}
return geoevent;
}
private void createGeoEventDefinition(GeoEvent event, Boolean retainFlds)
{
if (keepFields) {
GeoEventDefinition eventDef = event.getGeoEventDefinition();
try {
ged = eventDef.augment(fds);
} catch (ConfigurationException e) {
LOGGER.error(e.getLocalizedMessage());
}
} else {
ged = new DefaultGeoEventDefinition();
FieldDefinition trackidFD = event.getGeoEventDefinition()
.getFieldDefinition("TRACK_ID");
fds.add(trackidFD);
FieldDefinition geoFD = event.getGeoEventDefinition()
.getFieldDefinition("GEOMETRY");
fds.add(geoFD);
ged.setFieldDefinitions(fds);
}
ged.setName(outDefName);
ged.setOwner(definition.getUri().toString());
try {
manager.addGeoEventDefinition(ged);
} catch (GeoEventDefinitionManagerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private boolean hasGeometryMoved(MapGeometry geom1, MapGeometry geom2,
double tolerance) {
if (geom1 != null && geom1.getGeometry() != null
&& geom1.getGeometry().getType() == Type.Point && geom2 != null
&& geom2.getGeometry() != null
&& geom2.getGeometry().getType() == Type.Point) {
Point corePt1 = (Point) geom1.getGeometry();
Point corePt2 = (Point) geom2.getGeometry();
double meters = 0.0;
try {
meters = GeometryEngine.geodesicDistanceOnWGS84(corePt1,
corePt2);
} catch (Throwable error) {
LOGGER.error(error.getMessage());
}
double feet = meter2feet(meters);
if (feet >= tolerance)
return true;
else
return false;
} else {
throw new RuntimeException(
LOGGER.translate("INVALID_GEOMETRY_TYPE"));
}
}
private double meter2feet(double meter) {
return meter * 3.28084;
}
private GeoEvent createTrackIdleGeoEvent(TrackIdleProcessorStart idleStart,
boolean isIdle, GeoEvent oEvent, GeoEventDefinition ged)
throws MessagingException {
GeoEvent idleEvent = null;
if (geoEventCreator != null) {
try {
idleEvent = geoEventCreator.create(outDefName, definition.getUri().toString());
// idleEvent.setField("trackId", idleStart.getTrackId());
idleEvent.setField("idle", isIdle);
idleEvent.setField("idleDuration", idleStart.getIdleDuration());
idleEvent.setField("idleStart", idleStart.getStartTime());
// idleEvent.setField("GEOMETRY", idleStart.getGeometry());
if (keepFields) {
for (FieldDefinition fd : oEvent.getGeoEventDefinition()
.getFieldDefinitions()) {
idleEvent.setField(fd.getName(),
oEvent.getField(fd.getName()));
}
} else {
for (FieldDefinition fd : oEvent.getGeoEventDefinition()
.getFieldDefinitions()) {
if (fd.getTags().contains("TRACK_ID")
|| fd.getTags().contains("GEOMETRY")) {
idleEvent.setField(fd.getName(),
oEvent.getField(fd.getName()));
}
}
}
idleEvent.setProperty(GeoEventPropertyName.TYPE, "event");
idleEvent.setProperty(GeoEventPropertyName.OWNER_ID, getId());
idleEvent.setProperty(GeoEventPropertyName.OWNER_URI,
definition.getUri());
} catch (FieldException error) {
idleEvent = null;
LOGGER.error("GEOEVENT_CREATION_ERROR", error.getMessage());
LOGGER.info(error.getMessage(), error);
}
}
return idleEvent;
}
private String buildCacheKey(GeoEvent geoEvent) {
if (geoEvent != null && geoEvent.getTrackId() != null) {
GeoEventDefinition definition = geoEvent.getGeoEventDefinition();
return definition.getOwner() + "/" + definition.getName() + "/"
+ geoEvent.getTrackId();
}
return null;
}
public void setMessaging(Messaging messaging) {
geoEventCreator = messaging.createGeoEventCreator();
}
public void setManager(GeoEventDefinitionManager manager) {
this.manager = manager;
}
@Override
public void shutdown()
{
super.shutdown();
//GeoEvent Extension is shutting down
}
@Override
public void onServiceStart()
{
//GeoEvent Service is starting
}
@Override
public void onServiceStop(){
//GeoEvent Service is stopping
}
@Override
public boolean isGeoEventMutator()
{
//Must return true if processor is going to
//Modify the GeoEvent passed in.
return false;
}
}