All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.http.GraphHopperServlet Maven / Gradle / Ivy

There is a newer version: 9.1
Show newest version
/*
 *  Licensed to GraphHopper and Peter Karich under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for 
 *  additional information regarding copyright ownership.
 * 
 *  GraphHopper licenses this file to you 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.
 */
package com.graphhopper.http;

import com.graphhopper.PathWrapper;
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper;
import com.graphhopper.routing.util.FlagEncoder;
import com.graphhopper.routing.util.WeightingMap;
import com.graphhopper.util.StopWatch;
import com.graphhopper.util.shapes.GHPoint;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.Map.Entry;

import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;

/**
 * Servlet to use GraphHopper in a remote client application like mobile or browser. Note: If type
 * is json it returns the points in GeoJson format (longitude,latitude) unlike the format "lat,lon"
 * used otherwise. See the full API response format in docs/web/api-doc.md
 * 

* * @author Peter Karich */ public class GraphHopperServlet extends GHBaseServlet { @Inject private GraphHopper hopper; @Inject private RouteSerializer routeSerializer; @Override public void doGet( HttpServletRequest httpReq, HttpServletResponse httpRes ) throws ServletException, IOException { List requestPoints = getPoints(httpReq, "point"); GHResponse ghRsp = new GHResponse(); // we can reduce the path length based on the maximum differences to the original coordinates double minPathPrecision = getDoubleParam(httpReq, "way_point_max_distance", 1d); boolean writeGPX = "gpx".equalsIgnoreCase(getParam(httpReq, "type", "json")); boolean enableInstructions = writeGPX || getBooleanParam(httpReq, "instructions", true); boolean calcPoints = getBooleanParam(httpReq, "calc_points", true); boolean enableElevation = getBooleanParam(httpReq, "elevation", false); boolean pointsEncoded = getBooleanParam(httpReq, "points_encoded", true); String vehicleStr = getParam(httpReq, "vehicle", "car"); String weighting = getParam(httpReq, "weighting", "fastest"); String algoStr = getParam(httpReq, "algorithm", ""); String localeStr = getParam(httpReq, "locale", "en"); List favoredHeadings = Collections.EMPTY_LIST; try { favoredHeadings = getDoubleParamList(httpReq, "heading"); } catch (java.lang.NumberFormatException e) { throw new RuntimeException(e); } StopWatch sw = new StopWatch().start(); List errorList = new ArrayList(); if (!hopper.getEncodingManager().supports(vehicleStr)) { errorList.add(new IllegalArgumentException("Vehicle not supported: " + vehicleStr)); } else if (enableElevation && !hopper.hasElevation()) { errorList.add(new IllegalArgumentException("Elevation not supported!")); } else if (favoredHeadings.size() > 1 && favoredHeadings.size() != requestPoints.size()) { errorList.add(new IllegalArgumentException("The number of 'heading' parameters must be <= 1 " + "or equal to the number of points (" + requestPoints.size() + ")")); } ghRsp.addErrors(errorList); if (errorList.isEmpty()) { FlagEncoder algoVehicle = hopper.getEncodingManager().getEncoder(vehicleStr); GHRequest request; if (favoredHeadings.size() > 0) { // if only one favored heading is specified take as start heading if (favoredHeadings.size() == 1) { List paddedHeadings = new ArrayList(Collections.nCopies(requestPoints.size(), Double.NaN)); paddedHeadings.set(0, favoredHeadings.get(0)); request = new GHRequest(requestPoints, paddedHeadings); } else { request = new GHRequest(requestPoints, favoredHeadings); } } else { request = new GHRequest(requestPoints); } initHints(request, httpReq.getParameterMap()); request.setVehicle(algoVehicle.toString()). setWeighting(weighting). setAlgorithm(algoStr). setLocale(localeStr). getHints(). put("calcPoints", calcPoints). put("instructions", enableInstructions). put("wayPointMaxDistance", minPathPrecision); ghRsp = hopper.route(request); } float took = sw.stop().getSeconds(); String infoStr = httpReq.getRemoteAddr() + " " + httpReq.getLocale() + " " + httpReq.getHeader("User-Agent"); String logStr = httpReq.getQueryString() + " " + infoStr + " " + requestPoints + ", took:" + took + ", " + algoStr + ", " + weighting + ", " + vehicleStr; httpRes.setHeader("X-GH-Took", "" + Math.round(took * 1000)); int alternatives = ghRsp.getAll().size(); if (writeGPX && alternatives > 1) ghRsp.addError(new IllegalAccessException("Alternatives are currently not supported for GPX")); if (ghRsp.hasErrors()) { logger.error(logStr + ", errors:" + ghRsp.getErrors()); } else { PathWrapper altRsp0 = ghRsp.getBest(); logger.info(logStr + ", alternatives: " + alternatives + ", distance0: " + altRsp0.getDistance() + ", time0: " + Math.round(altRsp0.getTime() / 60000f) + "min" + ", points0: " + altRsp0.getPoints().getSize() + ", debugInfo: " + ghRsp.getDebugInfo()); } if (writeGPX) { if (ghRsp.hasErrors()) { httpRes.setStatus(SC_BAD_REQUEST); httpRes.getWriter().append(errorsToXML(ghRsp.getErrors())); } else { // no error => we can now safely call getFirst String xml = createGPXString(httpReq, httpRes, ghRsp.getBest()); writeResponse(httpRes, xml); } } else { Map map = routeSerializer.toJSON(ghRsp, calcPoints, pointsEncoded, enableElevation, enableInstructions); // this makes java client 0.5 fail so not in 0.6 but in 0.7 Object infoMap = map.get("info"); if (infoMap != null) ((Map) infoMap).put("took", Math.round(took * 1000)); if (ghRsp.hasErrors()) writeJsonError(httpRes, SC_BAD_REQUEST, new JSONObject(map)); else writeJson(httpReq, httpRes, new JSONObject(map)); } } protected String createGPXString( HttpServletRequest req, HttpServletResponse res, PathWrapper rsp ) { boolean includeElevation = getBooleanParam(req, "elevation", false); // default to false for the route part in next API version, see #437 boolean withRoute = getBooleanParam(req, "gpx.route", true); boolean withTrack = getBooleanParam(req, "gpx.track", true); boolean withWayPoints = getBooleanParam(req, "gpx.waypoints", false); res.setCharacterEncoding("UTF-8"); res.setContentType("application/xml"); String trackName = getParam(req, "trackname", "GraphHopper Track"); res.setHeader("Content-Disposition", "attachment;filename=" + "GraphHopper.gpx"); long time = getLongParam(req, "millis", System.currentTimeMillis()); return rsp.getInstructions().createGPX(trackName, time, includeElevation, withRoute, withTrack, withWayPoints); } protected String errorsToXML( List list ) { if (list.isEmpty()) throw new RuntimeException("errorsToXML should not be called with an empty list"); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.newDocument(); Element gpxElement = doc.createElement("gpx"); gpxElement.setAttribute("creator", "GraphHopper"); gpxElement.setAttribute("version", "1.1"); doc.appendChild(gpxElement); Element mdElement = doc.createElement("metadata"); gpxElement.appendChild(mdElement); Element extensionsElement = doc.createElement("extensions"); mdElement.appendChild(extensionsElement); Element messageElement = doc.createElement("message"); extensionsElement.appendChild(messageElement); messageElement.setTextContent(list.get(0).getMessage()); Element hintsElement = doc.createElement("hints"); extensionsElement.appendChild(hintsElement); for (Throwable t : list) { Element error = doc.createElement("error"); hintsElement.appendChild(error); error.setAttribute("message", t.getMessage()); error.setAttribute("details", t.getClass().getName()); } TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); StringWriter writer = new StringWriter(); transformer.transform(new DOMSource(doc), new StreamResult(writer)); return writer.toString(); } catch (Exception ex) { throw new RuntimeException(ex); } } protected List getPoints( HttpServletRequest req, String key ) { String[] pointsAsStr = getParams(req, key); final List infoPoints = new ArrayList(pointsAsStr.length); for (String str : pointsAsStr) { String[] fromStrs = str.split(","); if (fromStrs.length == 2) { GHPoint point = GHPoint.parse(str); if (point != null) infoPoints.add(point); } } return infoPoints; } protected void initHints( GHRequest request, Map parameterMap ) { WeightingMap m = request.getHints(); for (Entry e : parameterMap.entrySet()) { if (e.getValue().length == 1) m.put(e.getKey(), e.getValue()[0]); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy