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);
}
}
}