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

com.graphhopper.api.GraphHopperWeb Maven / Gradle / Ivy

There is a newer version: 0.8.2.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.api;

import com.graphhopper.PathWrapper;
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopperAPI;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.GHPoint;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Main wrapper of the GraphHopper Directions API for a simple and efficient
 * usage.
 *
 * @author Peter Karich
 */
public class GraphHopperWeb implements GraphHopperAPI {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private OkHttpClient downloader;
    private String serviceUrl;
    private String key = "";
    private boolean instructions = true;
    private boolean calcPoints = true;
    private boolean elevation = false;
    private String optimize = "false";
    private final Set ignoreSet;

    public GraphHopperWeb() {
        this("https://graphhopper.com/api/1/route");
    }

    public GraphHopperWeb(String serviceUrl) {
        this.serviceUrl = serviceUrl;
        downloader = new OkHttpClient.Builder().
                connectTimeout(5, TimeUnit.SECONDS).
                readTimeout(5, TimeUnit.SECONDS).
                build();

        // some parameters are supported directly via Java API so ignore them when writing the getHints map
        ignoreSet = new HashSet();
        ignoreSet.add("calc_points");
        ignoreSet.add("calcpoints");
        ignoreSet.add("instructions");
        ignoreSet.add("elevation");
        ignoreSet.add("key");
        ignoreSet.add("optimize");

        // some parameters are in the request:
        ignoreSet.add("algorithm");
        ignoreSet.add("locale");
        ignoreSet.add("point");
        ignoreSet.add("vehicle");

        // some are special and need to be avoided
        ignoreSet.add("points_encoded");
        ignoreSet.add("pointsencoded");
        ignoreSet.add("type");
    }

    public GraphHopperWeb setDownloader(OkHttpClient downloader) {
        this.downloader = downloader;
        return this;
    }

    public OkHttpClient getDownloader() {
        return downloader;
    }

    @Override
    public boolean load(String serviceUrl) {
        this.serviceUrl = serviceUrl;
        return true;
    }

    public GraphHopperWeb setKey(String key) {
        if (key == null || key.isEmpty()) {
            throw new IllegalStateException("Key cannot be empty");
        }

        this.key = key;
        return this;
    }

    /**
     * Enable or disable calculating points for the way. The default is true.
     */
    public GraphHopperWeb setCalcPoints(boolean calcPoints) {
        this.calcPoints = calcPoints;
        return this;
    }

    /**
     * Enable or disable calculating and returning turn instructions. The
     * default is true.
     */
    public GraphHopperWeb setInstructions(boolean b) {
        instructions = b;
        return this;
    }

    /**
     * Enable or disable elevation. The default is false.
     */
    public GraphHopperWeb setElevation(boolean withElevation) {
        this.elevation = withElevation;
        return this;
    }

    /**
     * @param optimize "false" if the order of the locations should be left
     * unchanged, this is the default. Or if "true" then the order of the
     * location is optimized according to the overall best route and returned
     * this way i.e. the traveling salesman problem is solved under the hood.
     * Note that in this case the request takes longer and costs more credits.
     * For more details see:
     * https://github.com/graphhopper/directions-api/blob/master/FAQ.md#what-is-one-credit
     */
    public GraphHopperWeb setOptimize(String optimize) {
        this.optimize = optimize;
        return this;
    }

    @Override
    public GHResponse route(GHRequest ghRequest) {
        String str = "Creating request failed";
        try {
            Request okRequest = createRequest(ghRequest);
            str = downloader.newCall(okRequest).execute().body().string();

            JSONObject json = new JSONObject(str);
            GHResponse res = new GHResponse();
            res.addErrors(readErrors(json));
            if (res.hasErrors()) {
                return res;
            }

            JSONArray paths = json.getJSONArray("paths");

            boolean tmpInstructions = ghRequest.getHints().getBool("instructions", instructions);
            boolean tmpCalcPoints = ghRequest.getHints().getBool("calc_points", calcPoints);
            boolean tmpElevation = ghRequest.getHints().getBool("elevation", elevation);

            for (int index = 0; index < paths.length(); index++) {
                JSONObject path = paths.getJSONObject(index);
                PathWrapper altRsp = createAltResponse(path, tmpCalcPoints, tmpInstructions, tmpElevation);
                res.add(altRsp);
            }
            return res;
        } catch (Exception ex) {
            throw new RuntimeException("Problem while fetching path " + ghRequest.getPoints()
                    + ", error: " + ex.getMessage() + " response: " + str, ex);
        }
    }

    public Request createRequest(GHRequest request) {
        boolean tmpInstructions = request.getHints().getBool("instructions", instructions);
        boolean tmpCalcPoints = request.getHints().getBool("calc_points", calcPoints);
        String tmpOptimize = request.getHints().get("optimize", optimize);

        if (tmpInstructions && !tmpCalcPoints) {
            throw new IllegalStateException("Cannot calculate instructions without points (only points without instructions). "
                    + "Use calc_points=false and instructions=false to disable point and instruction calculation");
        }

        boolean tmpElevation = request.getHints().getBool("elevation", elevation);

        String places = "";
        for (GHPoint p : request.getPoints()) {
            places += "point=" + Helper.round6(p.lat) + "," + Helper.round6(p.lon) + "&";
        }

        String url = serviceUrl
                + "?"
                + places
                + "&type=json"
                + "&instructions=" + tmpInstructions
                + "&points_encoded=true"
                + "&calc_points=" + tmpCalcPoints
                + "&algorithm=" + request.getAlgorithm()
                + "&locale=" + request.getLocale().toString()
                + "&elevation=" + tmpElevation
                + "&optimize=" + tmpOptimize;

        if (!request.getVehicle().isEmpty()) {
            url += "&vehicle=" + request.getVehicle();
        }

        if (!key.isEmpty()) {
            url += "&key=" + WebHelper.encodeURL(key);
        }

        for (Map.Entry entry : request.getHints().toMap().entrySet()) {
            String urlKey = entry.getKey();
            String urlValue = entry.getValue();

            // use lower case conversion for check only!
            if (ignoreSet.contains(urlKey.toLowerCase())) {
                continue;
            }

            if (urlValue != null && !urlValue.isEmpty()) {
                url += "&" + WebHelper.encodeURL(urlKey) + "=" + WebHelper.encodeURL(urlValue);
            }
        }

        return new Request.Builder().url(url).build();
    }

    public static PathWrapper createAltResponse(JSONObject path,
            boolean tmpCalcPoints, boolean tmpInstructions, boolean tmpElevation) {

        PathWrapper pathWrapper = new PathWrapper();
        pathWrapper.addErrors(readErrors(path));
        if (pathWrapper.hasErrors()) {
            return pathWrapper;
        }

        if (path.has("snapped_waypoints")) {
            String snappedPointStr = path.getString("snapped_waypoints");
            PointList snappedPoints = WebHelper.decodePolyline(snappedPointStr, 5, tmpElevation);
            pathWrapper.setWaypoints(snappedPoints);
        }

        if (tmpCalcPoints) {
            String pointStr = path.getString("points");
            PointList pointList = WebHelper.decodePolyline(pointStr, 100, tmpElevation);
            pathWrapper.setPoints(pointList);

            if (tmpInstructions) {
                JSONArray instrArr = path.getJSONArray("instructions");

                InstructionList il = new InstructionList(null);
                int viaCount = 1;
                for (int instrIndex = 0; instrIndex < instrArr.length(); instrIndex++) {
                    JSONObject jsonObj = instrArr.getJSONObject(instrIndex);
                    double instDist = jsonObj.getDouble("distance");
                    String text = jsonObj.getString("text");
                    long instTime = jsonObj.getLong("time");
                    int sign = jsonObj.getInt("sign");
                    JSONArray iv = jsonObj.getJSONArray("interval");
                    int from = iv.getInt(0);
                    int to = iv.getInt(1);
                    PointList instPL = new PointList(to - from, tmpElevation);
                    for (int j = from; j <= to; j++) {
                        instPL.add(pointList, j);
                    }

                    InstructionAnnotation ia = InstructionAnnotation.EMPTY;
                    if (jsonObj.has("annotation_importance") && jsonObj.has("annotation_text")) {
                        ia = new InstructionAnnotation(jsonObj.getInt("annotation_importance"), jsonObj.getString("annotation_text"));
                    }

                    Instruction instr;
                    if (sign == Instruction.USE_ROUNDABOUT || sign == Instruction.LEAVE_ROUNDABOUT) {
                        RoundaboutInstruction ri = new RoundaboutInstruction(sign, text, ia, instPL);

                        if (jsonObj.has("exit_number")) {
                            ri.setExitNumber(jsonObj.getInt("exit_number"));
                        }

                        if (jsonObj.has("turn_angle")) {
                            // TODO provide setTurnAngle setter
                            double angle = jsonObj.getDouble("turn_angle");
                            ri.setDirOfRotation(angle);
                            ri.setRadian((angle < 0 ? -Math.PI : Math.PI) - angle);
                        }

                        instr = ri;
                    } else if (sign == Instruction.REACHED_VIA) {
                        ViaInstruction tmpInstr = new ViaInstruction(text, ia, instPL);
                        tmpInstr.setViaCount(viaCount);
                        viaCount++;
                        instr = tmpInstr;
                    } else if (sign == Instruction.FINISH) {
                        instr = new FinishInstruction(instPL, 0);
                    } else {
                        instr = new Instruction(sign, text, ia, instPL);
                    }

                    // The translation is done from the routing service so just use the provided string
                    // instead of creating a combination with sign and name etc
                    instr.setUseRawName();

                    instr.setDistance(instDist).setTime(instTime);
                    il.add(instr);
                }
                pathWrapper.setInstructions(il);
            }
        }

        double distance = path.getDouble("distance");
        long time = path.getLong("time");
        pathWrapper.setDistance(distance).setTime(time);
        return pathWrapper;
    }

    public static List readErrors(JSONObject json) {
        List errors = new ArrayList();

        JSONArray errorJson;

        if (json.has("message")) {
            if (json.has("hints")) {
                errorJson = json.getJSONArray("hints");
            } else {
                // should not happen
                errors.add(new RuntimeException(json.getString("message")));
                return errors;
            }
        } else {
            return errors;
        }

        for (int i = 0; i < errorJson.length(); i++) {
            JSONObject error = errorJson.getJSONObject(i);
            String exClass = "";
            if (error.has("details")) {
                exClass = error.getString("details");
            }
            String exMessage = error.getString("message");

            if (exClass.equals(UnsupportedOperationException.class.getName())) {
                errors.add(new UnsupportedOperationException(exMessage));
            } else if (exClass.equals(IllegalStateException.class.getName())) {
                errors.add(new IllegalStateException(exMessage));
            } else if (exClass.equals(RuntimeException.class.getName())) {
                errors.add(new RuntimeException(exMessage));
            } else if (exClass.equals(IllegalArgumentException.class.getName())) {
                errors.add(new IllegalArgumentException(exMessage));
            } else if (exClass.isEmpty()) {
                errors.add(new RuntimeException(exMessage));
            } else {
                errors.add(new RuntimeException(exClass + " " + exMessage));
            }
        }

        if (json.has("message") && errors.isEmpty()) {
            errors.add(new RuntimeException(json.getString("message")));
        }

        return errors;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy