package se.kodapan.osm.sweden.ext.se.posten.postnummer.local; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Polygon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import se.kodapan.osm.services.nominatim.Nominatim; import se.kodapan.osm.services.overpass.Overpass; import se.kodapan.osm.sweden.OsmSweden; import se.kodapan.osm.sweden.util.Scored; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.text.DecimalFormat; import java.util.Arrays; import java.util.Iterator; import java.util.List; /** * @author kalle * @since 2013-09-02 6:20 PM */ public class PostnummerClassifierServlet extends HttpServlet { private static Logger log = LoggerFactory.getLogger(PostnummerClassifierServlet.class); private PostnummerClassifier postnummerClassifier; private Overpass overpass; @Override public void init() throws ServletException { super.init(); try { overpass = new Overpass(); overpass.setUserAgent("http://osm.kodapan.se/postnummer/classify/ osm@kodapan.se"); overpass.open(); postnummerClassifier = new PostnummerClassifier(OsmSweden.getInstance().getLocalPosten(), overpass); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void destroy() { try { overpass.close(); } catch (Exception e) { throw new RuntimeException(e); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { double longitude = Double.valueOf(req.getParameter("longitude")); double latitude = Double.valueOf(req.getParameter("latitude")); int metersSearchArea = 200; String metersSearchAreaParameter = req.getParameter("metersSearchArea"); if (metersSearchAreaParameter != null) { metersSearchArea = Integer.valueOf(metersSearchAreaParameter); } if (metersSearchArea > 5000) { throw new RuntimeException("metersSearchArea must not be greater than 5000!"); } double metersSearchAreaIncrement = 1.7; String metersSearchAreaIncrementParameter = req.getParameter("metersSearchAreaIncrement"); if (metersSearchAreaIncrementParameter != null) { metersSearchAreaIncrement = Integer.valueOf(metersSearchAreaIncrementParameter); } // if (metersSearchAreaIncrement > 1000) { // throw new RuntimeException("metersSearchAreaIncrement must not be greater than 1000!"); // } // double interpolationMeters = 1; String interpolationMetersParameter = req.getParameter("interpolationMeters"); if (interpolationMetersParameter != null) { interpolationMeters = Double.valueOf(interpolationMetersParameter); } if (interpolationMeters < 1) { throw new RuntimeException("Minimum 1 meters interpolation!"); } Postort postort = null; String postortParameter = req.getParameter("postort"); if (postortParameter != null) { postort = OsmSweden.getInstance().getLocalPosten().getPostortByIdentity().get(postortParameter); if (postort == null) { throw new IllegalArgumentException("Parameter postort '" + postortParameter + "' is not a valid postort identity. Should be the upper cased name of a Swedish postal town, e.g. 'MALMÖ'."); } } PostnummerClassifier.Response response; response = postnummerClassifier.classify(postort, longitude, latitude, metersSearchArea, metersSearchAreaIncrement, 5000, interpolationMeters); if (response.getClassification().isEmpty()) { throw new RuntimeException("Klassificeraren returnerade ett tomt resultat!"); } Postnummer postnummer = null; // todo this is a bug! should be ordered by score! fail! for (Scored<Postnummer> scored : response.getClassification()) { if (scored.getScore() == 0d) { postnummer = scored.getObject(); break; } } if (postnummer == null) { throw new RuntimeException("No classification with score 0d!"); } DecimalFormat df = new DecimalFormat("#.##########################"); String outputParameter = req.getParameter("output"); if ("osm.xml".equals(outputParameter)) { resp.setContentType("text/xml"); resp.getWriter().write(response.getVoronoiOsmXml()); } else if (null == outputParameter || "json".equals(outputParameter)) { resp.setContentType("application/json"); resp.getWriter().write("{ \"success\" : true ,\"classifications\" : ["); for (Iterator<Scored<Postnummer>> classifications = response.getClassification().iterator(); classifications.hasNext(); ) { Scored<Postnummer> classification = classifications.next(); resp.getWriter().write(" { "); resp.getWriter().write("\"postnummer\" : \"" + classification.getObject().getIdentity() + "\", "); resp.getWriter().write("\"postort\" : \"" + classification.getObject().getPostort().getIdentity() + "\", "); if ("true".equalsIgnoreCase(req.getParameter("geometries"))) { resp.getWriter().write("\"geometries\" : [ "); List<Polygon> geometries = response.getGeometryPerPostnummer().get(classification.getObject()); for (Iterator<Polygon> geometryIterator = geometries.iterator(); geometryIterator.hasNext(); ) { Polygon polygon = geometryIterator.next(); resp.getWriter().write("{ \"outer\" : [ "); for (Iterator<Coordinate> coordinates = Arrays.asList(polygon.getExteriorRing().getCoordinates()).iterator(); coordinates.hasNext(); ) { Coordinate coordinate = coordinates.next(); resp.getWriter().write("["); resp.getWriter().write(df.format(coordinate.x)); resp.getWriter().write(","); resp.getWriter().write(df.format(coordinate.y)); resp.getWriter().write("]"); if (coordinates.hasNext()) { resp.getWriter().write(", "); } } resp.getWriter().write(" ] "); // end outer if (polygon.getNumInteriorRing() > 0) { resp.getWriter().write(", \"inner\" : [ [ "); for (int interiorRingIndex = 0; interiorRingIndex < polygon.getNumInteriorRing(); interiorRingIndex++) { resp.getWriter().write("[ "); for (Iterator<Coordinate> coordinates = Arrays.asList(polygon.getInteriorRingN(interiorRingIndex).getCoordinates()).iterator(); coordinates.hasNext(); ) { Coordinate coordinate = coordinates.next(); resp.getWriter().write("["); resp.getWriter().write(df.format(coordinate.x)); resp.getWriter().write(","); resp.getWriter().write(df.format(coordinate.y)); resp.getWriter().write("]"); if (coordinates.hasNext()) { resp.getWriter().write(", "); } } resp.getWriter().write(" ]"); if (interiorRingIndex < polygon.getNumInteriorRing() - 1) { resp.getWriter().write(", "); } } resp.getWriter().write(" ] ] "); // end inner } resp.getWriter().write("}"); // end geometry if (geometryIterator.hasNext()) { resp.getWriter().write(", "); } } resp.getWriter().write(" ], "); // end geometries } resp.getWriter().write("\"score\" : " + df.format(classification.getScore())); resp.getWriter().write(" }"); // end postnummer if (classifications.hasNext()) { resp.getWriter().write(", "); } } resp.getWriter().append(" ] }"); } else { throw new IllegalArgumentException("Parameter output '" + outputParameter + "' not recognized. Should be 'osm.xml' or 'json'."); } } catch (Exception e) { StringBuilder message = new StringBuilder(); if (e.getMessage() != null) { message.append(e.getMessage()); } else { message.append(e.getClass().getName()); } resp.setStatus(500); resp.setContentType("application/json"); resp.getWriter().write("{ \"success\" : false, \"message\" : \"" + message.toString() + "\"}"); log.error("Caught exception, status 500 response sent to client.", e); } } }