Java tutorial
/* Copyright 1995-2015 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. For additional information, contact: Environmental Systems Research Institute, Inc. Attn: Contracts Dept 380 New York Street Redlands, California, USA 92373 email: contracts@esri.com */ package com.esri.geoevent.processor.serviceareacalculator; import java.io.ByteArrayOutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.http.client.methods.HttpPost; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; import com.esri.core.geometry.MapGeometry; 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.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.http.GeoEventHttpClient; import com.esri.ges.core.http.GeoEventHttpClientService; import com.esri.ges.core.http.KeyValue; import com.esri.ges.core.validation.ValidationException; import com.esri.ges.manager.datastore.agsconnection.ArcGISServerConnection; import com.esri.ges.framework.i18n.BundleLogger; import com.esri.ges.framework.i18n.BundleLoggerFactory; import com.esri.ges.manager.datastore.agsconnection.ArcGISServerConnectionManager; 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.MessagingException; import com.esri.ges.processor.GeoEventProcessorBase; import com.esri.ges.processor.GeoEventProcessorDefinition; import com.esri.ges.util.GeometryUtil; import com.esri.ges.util.Validator; public class ServiceAreaCalculator extends GeoEventProcessorBase { final private static BundleLogger LOGGER = BundleLoggerFactory.getLogger(ServiceAreaCalculator.class); final static Object[] wkidPath = new Object[] { "spatialReference", "wkid" }; final static Object[] geometryPath = new Object[] { "geometry" }; private String serviceAreaSolverPath; private int driveTime; private GeoEventHttpClientService httpService; private String inputGeometryField; private String outputGeometryField; private boolean overrideInputField; private String outputGEDName; private GeoEventCreator geoEventCreator; private GeoEventDefinitionManager geoEventDefinitionManager; private ArcGISServerConnectionManager agsConnectionManager; private Map<String, String> edMapper = new ConcurrentHashMap<String, String>(); private String naConnectionName; private String outputPolygonType; protected ServiceAreaCalculator(GeoEventProcessorDefinition definition, GeoEventHttpClientService httpService, GeoEventCreator geoEventCreator, GeoEventDefinitionManager geoEventDefinitionManager, ArcGISServerConnectionManager agsConnectionManager) throws ComponentException { super(definition); this.httpService = httpService; this.geoEventCreator = geoEventCreator; this.geoEventDefinitionManager = geoEventDefinitionManager; this.agsConnectionManager = agsConnectionManager; } @Override public GeoEvent process(GeoEvent geoEvent) throws Exception { try { if (geoEvent.getField(inputGeometryField) instanceof MapGeometry) { if (geoEvent != null && geoEventDefinitionManager != null) { Object obj = processGeometry(geoEvent, inputGeometryField); if (overrideInputField) geoEvent.setField(inputGeometryField, obj); else { FieldDefinition fd = geoEvent.getGeoEventDefinition() .getFieldDefinition(outputGeometryField); if (fd != null && fd.getType() != FieldType.Geometry) throw new ValidationException(LOGGER.translate("PROCESSOR_OUTPUTFIELD_ERROR")); if (fd != null) { if (Validator.isEmpty(outputGEDName)) { geoEvent.setField(outputGeometryField, obj); } else { GeoEventDefinition edOut = update(geoEvent.getGeoEventDefinition()); geoEvent = populateGeoEvent(geoEvent, edOut, null); geoEvent.setField(outputGeometryField, obj); } } else { GeoEventDefinition edOut = update(geoEvent.getGeoEventDefinition()); geoEvent = populateGeoEvent(geoEvent, edOut, obj); } } return geoEvent; } } else throw new ValidationException(LOGGER.translate("PROCESSOR_INPUTFIELD_ERROR")); } catch (Exception e) { LOGGER.error("PROCESSOR_UNABLE_ERROR", e.getMessage()); LOGGER.info(e.getMessage(), e); } return null; } synchronized private void clearGeoEventDefinitionMapper() { if (!edMapper.isEmpty()) { for (String guid : edMapper.values()) { try { geoEventDefinitionManager.deleteGeoEventDefinition(guid); } catch (GeoEventDefinitionManagerException e) { LOGGER.warn("PROCESSOR_FAILED_TO_DELETE_GED", guid, e.getMessage()); } } edMapper.clear(); } } private GeoEvent populateGeoEvent(GeoEvent geoEvent, GeoEventDefinition edOut, Object outputGeometry) throws MessagingException { GeoEvent outGeoEvent; if (outputGeometry == null) outGeoEvent = geoEventCreator.create(edOut.getGuid(), geoEvent.getAllFields()); else outGeoEvent = geoEventCreator.create(edOut.getGuid(), new Object[] { geoEvent.getAllFields(), outputGeometry }); outGeoEvent.setProperty(GeoEventPropertyName.TYPE, geoEvent.getProperty(GeoEventPropertyName.TYPE)); outGeoEvent.setProperty(GeoEventPropertyName.OWNER_ID, geoEvent.getProperty(GeoEventPropertyName.OWNER_ID)); outGeoEvent.setProperty(GeoEventPropertyName.OWNER_URI, geoEvent.getProperty(GeoEventPropertyName.OWNER_URI)); for (Map.Entry<GeoEventPropertyName, Object> property : geoEvent.getProperties()) { if (!outGeoEvent.hasProperty(property.getKey())) outGeoEvent.setProperty(property.getKey(), property.getValue()); } return outGeoEvent; } synchronized private GeoEventDefinition update(GeoEventDefinition edIn) throws Exception { FieldDefinition fd = edIn.getFieldDefinition(outputGeometryField); GeoEventDefinition edOut = edMapper.containsKey(edIn.getGuid()) ? geoEventDefinitionManager.getGeoEventDefinition(edMapper.get(edIn.getGuid())) : null; if (edOut == null) { if (fd == null) { FieldDefinition newfd = new DefaultFieldDefinition(outputGeometryField, FieldType.Geometry); edOut = edIn.augment(Arrays.asList(newfd)); } else { edOut = (GeoEventDefinition) edIn.clone(); } edOut.setOwner(getId()); if (!outputGEDName.isEmpty()) { edOut.setName(outputGEDName); geoEventDefinitionManager.addTemporaryGeoEventDefinition(edOut, false); } else { geoEventDefinitionManager.addTemporaryGeoEventDefinition(edOut, true); } edMapper.put(edIn.getGuid(), edOut.getGuid()); } return edOut; } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); inputGeometryField = hasProperty("inputGeometryField") ? getProperty("inputGeometryField").getValueAsString().trim() : ""; overrideInputField = Boolean.parseBoolean(getProperty("replaceGeometry").getValueAsString()); if (overrideInputField) { geoEventMutator = true; outputGeometryField = inputGeometryField; } else { outputGeometryField = getProperty("outputGeometryField").getValueAsString().trim(); outputGEDName = getProperty("outputGEDName").getValueAsString().trim(); if (outputGEDName.isEmpty()) geoEventMutator = true; else geoEventMutator = false; } naConnectionName = getProperty(ServiceAreaCalculatorDefinition.NA_CONNECTION_PROPERTY).getValueAsString(); serviceAreaSolverPath = getProperty(ServiceAreaCalculatorDefinition.NA_PATH_PROPERTY).getValueAsString(); outputPolygonType = getProperty(ServiceAreaCalculatorDefinition.OUTPUT_POLYGON_TYPE_PROPERTY) .getValueAsString(); driveTime = Integer .parseInt(getProperty(ServiceAreaCalculatorDefinition.DRIVE_TIME_PROPERTY).getValueAsString()); } protected Object processGeometry(GeoEvent geoevent, String geometryField) throws Exception { MapGeometry geomout = null; try { MapGeometry geom = (MapGeometry) geoevent.getField(geometryField); geomout = getAreaAroundPoint(geom); } catch (Exception e) { if (geoevent.getTrackId() == null) throw new Exception(LOGGER.translate("SERVICE_AREA_UNABLE_ERROR1", e.getMessage()), e); else throw new Exception( LOGGER.translate("SERVICE_AREA_UNABLE_ERROR2", geoevent.getTrackId(), e.getMessage()), e); } return geomout; } public MapGeometry getAreaAroundPoint(MapGeometry point) { ArcGISServerConnection agsConnection = agsConnectionManager.getArcGISServerConnection(naConnectionName); Collection<KeyValue> params = new ArrayList<KeyValue>(); params.addAll(agsConnection.getDefaultParamsForRequest()); params.add(new KeyValue("facilities", generateFacilitiesJson(point))); params.add(new KeyValue("defaultBreaks", (new Integer(driveTime)).toString())); params.add(new KeyValue("travelDirection", "esriNATravelDirectionFromFacility")); if (!Validator.isEmpty(outputPolygonType)) params.add(new KeyValue("outputPolygons", outputPolygonType)); try (GeoEventHttpClient http = httpService.createNewClient()) { StringBuffer urlString = new StringBuffer(); urlString.append(agsConnection.getUrl().toExternalForm()); urlString.append(serviceAreaSolverPath); URL url = new URL(urlString.toString()); HttpPost postRequest = http.createPostRequest(url, params); postRequest.addHeader("Referer", agsConnection.getReferer()); String responseString = http.executeAndReturnBody(postRequest, GeoEventHttpClient.DEFAULT_TIMEOUT); return parseAreaSolverReply(responseString); } catch (Exception e) { LOGGER.debug("SERVICE_AREA_UNABLE_ERROR1", e.getMessage()); LOGGER.info(e.getMessage(), e); } return null; } private MapGeometry parseAreaSolverReply(String reply) { try { JsonNode response = (new ObjectMapper()).readTree(reply); JsonNode saPolygon = response.get("saPolygons"); if (saPolygon == null) { LOGGER.error("SERVICE_AREA_NO_SAPOLYGON", reply); return null; } List<MapGeometry> polygons = getGeometriesFromNAReply(saPolygon); if (polygons.size() > 1) { LOGGER.info("SERVICE_AREA_MULTIPLE_RESULT ", reply); } if (polygons.size() == 0) { LOGGER.error("SERVICE_AREA_NO_GEOMETRY_IN_RESULT", reply); return null; } return polygons.get(0); } catch (Exception e) { throw new RuntimeException(LOGGER.translate("SERVICE_AREA_UNABLE_ERROR1", e.getMessage()), e); } } private List<MapGeometry> getGeometriesFromNAReply(JsonNode jsonNode) throws Exception { if (jsonNode == null) { LOGGER.error("SERVICE_AREA_NO_ROUTES_IN_RESULT"); return null; } int wkid = getNodeFollowingPath(jsonNode, wkidPath).getIntValue(); String wkidStr = Integer.toString(wkid); String geometryString; List<MapGeometry> retList = new ArrayList<MapGeometry>(); MapGeometry mapGeometry; for (JsonNode feature : getNodeFollowingPath(jsonNode, new Object[] { "features" })) { geometryString = geometryStringFromJsonNode(getNodeFollowingPath(feature, geometryPath), wkidStr); mapGeometry = GeometryUtil.fromJson(geometryString); retList.add(mapGeometry); } return retList; } private JsonNode getNodeFollowingPath(JsonNode jsonNode, Object[] nodePath) { for (Object property : nodePath) { if (property instanceof String) { jsonNode = jsonNode.get((String) property); } else if (property instanceof Integer) { Integer index = (Integer) property; jsonNode = jsonNode.get(index); } if (jsonNode == null) { break; } } return jsonNode; } private String geometryStringFromJsonNode(JsonNode geometry, String outSR) { String geometryString = geometry.toString(); return geometryString.substring(0, geometryString.length() - 1) + ",\"spatialReference\":{\"wkid\":" + outSR + "}}"; } private String generateFacilitiesJson(MapGeometry point) { StringBuffer sb = new StringBuffer(); sb.append("{\"type\":\"features\",\"features\":[{\"geometry\":"); sb.append(removeZFromGeom(GeometryUtil.toJson(point))); sb.append("}]}"); return sb.toString(); } private String removeZFromGeom(String geomString) { geomString = new String(geomString); JsonFactory factory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper(factory); JsonParser parser; try { parser = factory.createJsonParser(geomString.getBytes()); TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() { }; HashMap<String, Object> o = mapper.readValue(parser, typeRef); if (o.containsKey("z")) { o.remove("z"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); mapper.writeValue(baos, o); geomString = baos.toString(); } } catch (Exception e) { throw new RuntimeException(LOGGER.translate("SERVICE_AREA_ERROR_REMOVING_Z", e.getMessage()), e); } return geomString; } @Override public void shutdown() { super.shutdown(); clearGeoEventDefinitionMapper(); } }