justrouting

package module
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 13 Imported by: 0

README

JustRouting Go Client

CI Go Report Card Go Reference

Official Go client for the JustRouting API — routing, distance matrices, map matching, trips, nearest-road lookup, vehicle routing optimization, and geocoding across Southeast Asia.

No dependencies outside the standard library.

Install

go get github.com/justrouting/go-client
import justrouting "github.com/justrouting/go-client"

Quickstart

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/justrouting/go-client"
)

func main() {
    client := justrouting.NewClient(
        "YOUR_API_KEY",
        justrouting.WithHTTPClient(&http.Client{
            Timeout: 10 * time.Second,
        }),
    )

    route, err := client.Routes.Get(
        context.Background(),
        &justrouting.RouteRequest{
            Origin:      []float64{103.708362, 1.357371},
            Destination: []float64{103.984748, 1.352212},
        },
    )

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf(
        "Distance: %.1f km\n",
        route.Distance/1000,
    )
}

The coordinates above are in Singapore. See Coordinates must share a country.

Services

A Client exposes eight services.

Routes

Routes.Get returns the best route. Routes.GetAll additionally returns alternatives and the snapped input waypoints.

route, err := client.Routes.Get(ctx, &justrouting.RouteRequest{
    Origin:      justrouting.Point{103.8198, 1.3521},
    Destination: justrouting.Point{103.9915, 1.3644},
    Waypoints:   []justrouting.Point{{103.8514, 1.2897}}, // stops in order
    Overview:    "full",                                   // full geometry
    Steps:       true,                                     // turn-by-turn
})

fmt.Println(route.Distance) // metres
fmt.Println(route.Duration) // seconds

Route.Geometry holds whichever encoding you asked for:

polyline, err := route.Geometry.Polyline()  // default, and "polyline6"
line, err := route.Geometry.GeoJSON()       // when Geometries: "geojson"
Matrix

Travel time and distance between many points at once.

m, err := client.Matrix.Get(ctx, &justrouting.MatrixRequest{
    Coordinates:  []justrouting.Point{depot, stopA, stopB},
    Sources:      []int{0},    // only the depot row; cheaper than N×N
    Destinations: []int{1, 2},
})

if seconds, ok := m.Duration(0, 1); ok {
    fmt.Printf("depot → stopA: %.0f min\n", seconds/60)
}

The accessors return false for unreachable pairs. The API reports those as null, which is deliberately kept distinct from a genuine zero — that is why Durations and Distances hold *float64.

Map Matching

Snap a noisy GPS trace onto the road network and get the route that was driven.

match, err := client.MapMatching.Get(ctx, &justrouting.MapMatchingRequest{
    Coordinates: trace, // GPS points in chronological order, at least 2
    Timestamps:  ts,    // required: UNIX seconds per point, monotonically increasing
})
fmt.Printf("%.0f%% confidence\n", match.Confidence*100)
fmt.Println(match.Distance) // metres, via the embedded Route

Match embeds Route, so every route field (Distance, Duration, Geometry, Legs, …) is promoted, plus the engine's Confidence (0–1). MapMatching.GetAll additionally returns Tracepoints — the input coordinates snapped to the road network, aligned with your input; an entry is nil when the engine could not match that point. Useful options: Timestamps (required — UNIX seconds per point, monotonically increasing), Radiuses (max snap distance, one value per point — the default is only a few metres, so noisy GPS points usually need it), Gaps ("split"/"ignore"), Tidy, Waypoints (indices to use as waypoints), Snapping ("default"/"any"), Bearings, Hints, Approaches, Exclude, GenerateHints, and SkipWaypoints.

Trip

Visit a set of points in the fastest possible order (a travelling-salesman heuristic).

trip, err := client.Trip.Get(ctx, &justrouting.TripRequest{
    Coordinates: []justrouting.Point{depot, stopA, stopB},
})
fmt.Println(trip.Distance) // metres

By default the trip returns to its starting point; pass Roundtrip (a bool pointer) to change that, and Source/Destination ("any", "first"/"last") to pin the ends. Trip.GetAll also returns Waypoints in the order the trip visits them.

Nearest

Find the road segment closest to a coordinate.

wp, err := client.Nearest.Get(ctx, &justrouting.NearestRequest{
    Coordinate: []float64{103.8198, 1.3521},
})
fmt.Printf("%s, %.0f m away\n", wp.Name, wp.Distance)

Pass Number to get the second-, third-, … nearest segments; Nearest.GetAll returns all of them. The returned Waypoint includes the OSM Nodes of the matched segment.

Geocode

Geocode.Search converts an address into coordinates, by free text or by structured fields (exactly one of the two):

results, err := client.Geocode.Search(ctx, &justrouting.GeocodeRequest{
    Text: "Marina Bay Sands, Singapore",
})
for _, r := range results.Results {
    fmt.Println(r.Formatted, r.Location()) // address text, [lon, lat]
}
results, err := client.Geocode.Search(ctx, &justrouting.GeocodeRequest{
    Structured: &justrouting.StructuredQuery{
        Housenumber: "10",
        Street:      "Bayfront Avenue",
        City:        "Singapore",
    },
})

Results are ordered best first; an empty Results list simply means nothing matched. The client always requests format=json, regardless of the API's default response format. Use Filters (repeatable, e.g. countrycode:sg) to restrict results and Bias (e.g. proximity:103.8,1.3) to prefer places near a point. GeocodeResult.Location() returns a Point in the usual [longitude, latitude] order, ready to feed into Routes, Matrix, or Optimization. Errors from the geocoding upstream are classified by HTTP status like any other failure (401 → ErrUnauthorized, 429 → ErrRateLimited, 502 → ErrUpstreamUnavailable).

Optimization

Assign tasks to a fleet and order each vehicle's stops.

solution, err := client.Optimization.Solve(ctx, &justrouting.OptimizationRequest{
    Vehicles: []justrouting.Vehicle{
        {ID: 1, Start: depot, End: depot, Capacity: []int{4}},
    },
    Jobs: []justrouting.Job{
        {ID: 1, Location: stopA, Delivery: []int{1}, Service: 300},
        {ID: 2, Location: stopB, Delivery: []int{2}, Service: 300},
    },
})

for _, route := range solution.Routes {
    fmt.Printf("vehicle %d: %d stops\n", route.Vehicle, len(route.Steps))
}
fmt.Println(len(solution.Unassigned), "task(s) could not be served")

Use Shipments instead of Jobs for pickup-and-delivery pairs that must be served in order by the same vehicle.

The full engine feature set is modeled: capacities, skills, priorities, time windows, breaks, per-vehicle costs, speed factors and limits, per-type setup/service overrides, custom matrices (with *_index locations), warm-start vehicle steps, and route geometry via Options.Geometry.

Health

The only call that works without an API key, which makes it a useful connectivity check.

health, err := client.Health.Get(ctx)
fmt.Println(health.OK(), health.Upstreams)

Error handling

Every failure is an *Error. Classify it with errors.Is against the package sentinels rather than matching on message text:

switch {
case errors.Is(err, justrouting.ErrQuotaExceeded):
    // daily allowance used up — retrying will not help
case errors.Is(err, justrouting.ErrRateLimited):
    // throttled; the client already retried
case errors.Is(err, justrouting.ErrCrossCountry):
    // coordinates span more than one country
case errors.Is(err, justrouting.ErrNoRoute):
    // no road connects these points
}
Sentinel Meaning
ErrUnauthorized API key missing, invalid, or revoked
ErrRateLimited Throttled (per-second limit or daily quota)
ErrQuotaExceeded Daily quota exhausted; retrying will not help
ErrPlanLimitExceeded Too many matrix coordinates, jobs, or vehicles
ErrCrossCountry Coordinates span more than one country
ErrInvalidCoordinates Coordinate malformed or out of range
ErrNoRoute No route exists between the points
ErrUpstreamUnavailable Routing engine unreachable; usually transient
ErrInvalidRequest Rejected locally before any request was sent

Reach for the concrete type when you need the status code or raw body:

var apiErr *justrouting.Error
if errors.As(err, &apiErr) {
    log.Printf("HTTP %d: %s\n%s", apiErr.StatusCode, apiErr.Message, apiErr.Body)
}

Configuration

Option Default Purpose
WithHTTPClient 30s timeout Custom transport, timeouts, instrumentation
WithBaseURL https://api.justrouting.tech Target a local or staging server
WithUserAgent justrouting-go/<version> Identify your application
WithMaxRetries 2 Retry budget on top of the initial attempt
WithBackoff 500ms → 8s, jittered Replace the retry delay schedule

NewClient never returns an error. If an option is invalid, the failure is retained and returned by the first API call, so a misconfigured client fails loudly at the point of use instead of silently falling back to a default.

Retries

Rate limits (429), server errors (500, 502, 503, 504), and transport failures are retried with exponential backoff and jitter; a Retry-After header takes precedence when present. Other 4xx responses are returned immediately — they would fail identically on a retry and would still consume quota.

Retries are bounded by the context, not the HTTP client's timeout. A timeout on your http.Client applies to each individual attempt; use a context deadline to bound the whole call:

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

Things to know

Coordinates are [longitude, latitude]

This is the GeoJSON order, and the reverse of the "lat, lng" used by most map UIs. Swapped coordinates are usually caught locally — a longitude in the latitude slot fails the [-90, 90] check before a request is sent — but a swap that stays in range will silently route somewhere unexpected.

Point is defined over []float64, so both of these work:

Origin: []float64{103.8198, 1.3521}
Origin: justrouting.Point{103.8198, 1.3521}
Coordinates must share a country

Every coordinate in a single request must fall within one country; the API routes each request to a per-country engine. A Singapore → Kuala Lumpur request fails with ErrCrossCountry.

Supported countries: Brunei, Cambodia, Indonesia, Laos, Malaysia, Myanmar, the Philippines, Singapore, Thailand, and Vietnam.

Plan limits
Free Hobby
Requests per day 100 10,000
Requests per second 5 10
Matrix coordinates 100 500
Jobs per optimization 100 1,000
Vehicles per optimization 10 50

Exceeding a size limit returns ErrPlanLimitExceeded; exhausting the daily allowance returns ErrQuotaExceeded.

Examples

Runnable programs live in examples/:

export JUSTROUTING_API_KEY=<your key>
go run ./examples/route
go run ./examples/matrix
go run ./examples/matching
go run ./examples/trip
go run ./examples/nearest
go run ./examples/geocode
go run ./examples/optimization

Development

go build ./...
go vet ./...
go test -race -cover ./...

The default suite runs entirely against httptest servers — no network access and no API key.

Integration tests hit a live API and are behind a build tag, so they never run by accident:

go test -tags=integration ./...                          # health and auth only
JUSTROUTING_API_KEY=<key> go test -tags=integration ./... # full coverage
JUSTROUTING_BASE_URL=http://localhost:8080 go test -tags=integration ./...

License

MIT

Documentation

Overview

Package justrouting provides a Go client for the JustRouting API.

A client is created with an API key and, optionally, one or more options:

client := justrouting.NewClient("YOUR_API_KEY",
	justrouting.WithHTTPClient(&http.Client{Timeout: 10 * time.Second}))

route, err := client.Routes.Get(ctx, &justrouting.RouteRequest{
	Origin:      []float64{103.8198, 1.3521},
	Destination: []float64{103.9915, 1.3644},
})
if err != nil {
	log.Fatal(err)
}
fmt.Printf("Distance: %.1f km\n", route.Distance/1000)

Coordinates are always [longitude, latitude], the order used by GeoJSON, OSRM and VROOM. All requests take a context.Context and are safe for concurrent use.

Failed calls return an *Error. Use errors.Is with the package sentinels for control flow and errors.As when the status code or raw body is needed:

if errors.Is(err, justrouting.ErrRateLimited) {
	// back off and retry later
}

The package has no dependencies outside the standard library.

Example

This is the package quickstart. It compiles as part of the test suite, so the documented usage cannot drift from the API.

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"time"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient(
		"YOUR_API_KEY",
		justrouting.WithHTTPClient(&http.Client{
			Timeout: 10 * time.Second,
		}),
	)

	route, err := client.Routes.Get(
		context.Background(),
		&justrouting.RouteRequest{
			Origin:      []float64{103.8198, 1.3521},
			Destination: []float64{103.9915, 1.3644},
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf(
		"Distance: %.1f km\n",
		route.Distance/1000,
	)
}

Index

Examples

Constants

View Source
const (
	// Version is the client version, reported in the User-Agent header.
	Version = "0.3.2"

	// DefaultBaseURL is the hosted JustRouting API endpoint.
	DefaultBaseURL = "https://api.justrouting.tech"

	// DefaultProfile is the routing profile used when a request leaves
	// Profile empty.
	DefaultProfile = "driving"
)

Variables

View Source
var (
	// ErrUnauthorized means the API key is missing, invalid or revoked.
	ErrUnauthorized = errors.New("justrouting: unauthorized")

	// ErrRateLimited means the request was throttled. It matches both
	// per-second throttling and daily quota exhaustion; use
	// ErrQuotaExceeded to distinguish the latter.
	ErrRateLimited = errors.New("justrouting: rate limit exceeded")

	// ErrQuotaExceeded means the plan's daily request quota is used up.
	// Retrying before the quota resets will not help.
	ErrQuotaExceeded = errors.New("justrouting: daily quota exceeded")

	// ErrPlanLimitExceeded means the request is larger than the plan
	// allows — too many matrix coordinates, jobs, or vehicles.
	ErrPlanLimitExceeded = errors.New("justrouting: plan limit exceeded")

	// ErrCrossCountry means the coordinates span more than one country.
	// Every coordinate in a request must fall within a single country.
	ErrCrossCountry = errors.New("justrouting: cross-country request not supported")

	// ErrInvalidCoordinates means a coordinate was malformed, out of range,
	// or could not be parsed by the API.
	ErrInvalidCoordinates = errors.New("justrouting: invalid coordinates")

	// ErrNoRoute means no route exists between the given coordinates.
	ErrNoRoute = errors.New("justrouting: no route found")

	// ErrUpstreamUnavailable means the routing engine behind the API could
	// not be reached. This is usually transient.
	ErrUpstreamUnavailable = errors.New("justrouting: upstream unavailable")

	// ErrInvalidRequest means the request was rejected before being sent
	// because required fields were missing or inconsistent.
	ErrInvalidRequest = errors.New("justrouting: invalid request")
)

Sentinel errors for use with errors.Is. They classify an *Error by the condition it represents rather than by an exact message, which keeps calling code stable if the API's wording changes.

Functions

This section is empty.

Types

type Annotation

type Annotation struct {
	Distance    []float64 `json:"distance,omitempty"`
	Duration    []float64 `json:"duration,omitempty"`
	Speed       []float64 `json:"speed,omitempty"`
	Weight      []float64 `json:"weight,omitempty"`
	Nodes       []int64   `json:"nodes,omitempty"`
	Datasources []int     `json:"datasources,omitempty"`
}

Annotation holds per-segment metadata, returned when RouteRequest.Annotations is set.

type BBox added in v0.2.0

type BBox struct {
	Lon1 float64 `json:"lon1"`
	Lat1 float64 `json:"lat1"`
	Lon2 float64 `json:"lon2"`
	Lat2 float64 `json:"lat2"`
}

BBox is a bounding box as [lon1, lat1, lon2, lat2].

type Break added in v0.3.2

type Break struct {
	// ID identifies the break. It must be unique within the vehicle.
	ID int `json:"id"`
	// TimeWindows bound when the break may start.
	TimeWindows []TimeWindow `json:"time_windows,omitempty"`
	// Service is the break length in seconds.
	Service int `json:"service,omitempty"`
	// Description is an opaque label echoed back in the solution.
	Description string `json:"description,omitempty"`
	// MaxLoad is the maximum load, per capacity dimension, under which
	// the break may happen.
	MaxLoad []int `json:"max_load,omitempty"`
}

Break is a rest period scheduled during a vehicle's route.

type Client

type Client struct {

	// Routes computes routes between two or more coordinates.
	Routes *RoutesService
	// Matrix computes duration and distance matrices.
	Matrix *MatrixService
	// MapMatching snaps GPS traces onto the road network.
	MapMatching *MapMatchingService
	// Trip finds the fastest order to visit a set of coordinates.
	Trip *TripService
	// Nearest finds the road segment closest to a coordinate.
	Nearest *NearestService
	// Geocode converts addresses into coordinates.
	Geocode *GeocodeService
	// Optimization solves vehicle routing problems.
	Optimization *OptimizationService
	// Health reports API and upstream availability.
	Health *HealthService
	// contains filtered or unexported fields
}

Client is a JustRouting API client. It is safe for concurrent use by multiple goroutines, and should be created once and reused so that underlying HTTP connections are pooled.

func NewClient

func NewClient(apiKey string, opts ...ClientOption) *Client

NewClient returns a Client authenticated with apiKey.

The key is sent as an "Authorization: Bearer" header on every request that needs it. An empty key is allowed but only HealthService.Get will work; every other call fails with ErrUnauthorized before any request is sent.

Invalid options do not panic. The first option error is retained and returned by every subsequent API call, so a misconfigured client surfaces the problem at the first request rather than silently using a default.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the API endpoint the client sends requests to.

type ClientOption

type ClientOption func(*Client) error

A ClientOption configures a Client. Options are applied by NewClient in the order they are given.

func WithBackoff

func WithBackoff(fn func(attempt int) time.Duration) ClientOption

WithBackoff replaces the delay function used between retries. attempt is 1 for the first retry, 2 for the second, and so on.

The default is exponential backoff with jitter, starting at 500ms and capped at 8s. A Retry-After response header, when present, takes precedence over this function.

func WithBaseURL

func WithBaseURL(raw string) ClientOption

WithBaseURL points the client at a different API endpoint, such as a local development server:

justrouting.WithBaseURL("http://localhost:8080")

The URL must be absolute. Any path is kept as a prefix for every request.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) ClientOption

WithHTTPClient sets the HTTP client used for all requests. Use it to control timeouts, proxies, or transport-level instrumentation:

justrouting.NewClient(key, justrouting.WithHTTPClient(&http.Client{
	Timeout: 10 * time.Second,
}))

The timeout on the supplied client covers a single HTTP attempt, not the whole retry sequence; use a context deadline to bound the total call.

func WithMaxRetries

func WithMaxRetries(n int) ClientOption

WithMaxRetries sets how many times a failed request is retried, on top of the initial attempt. The default is 2. Pass 0 to disable retries.

Only rate-limit (429), server (5xx) and transport errors are retried; other 4xx responses are returned immediately.

func WithUserAgent

func WithUserAgent(ua string) ClientOption

WithUserAgent overrides the User-Agent header. Identifying your application helps when diagnosing traffic against the API.

type CustomMatrix added in v0.3.2

type CustomMatrix struct {
	// Durations is a travel-time matrix in seconds. Providing it for every
	// profile in use makes Location, Start and End optional.
	Durations [][]int `json:"durations,omitempty"`
	// Distances is a distance matrix in meters. It requires Durations.
	Distances [][]int `json:"distances,omitempty"`
	// Costs is a cost matrix used in all route cost evaluations.
	Costs [][]int `json:"costs,omitempty"`
}

CustomMatrix is a user-supplied travel-time, distance or cost matrix for one routing profile. Values are read as matrix[row][column]; rows and columns are indexed by the *_index fields on vehicles and tasks.

type Datasource added in v0.2.0

type Datasource struct {
	Sourcename  string `json:"sourcename"`
	Attribution string `json:"attribution"`
	License     string `json:"license"`
	URL         string `json:"url"`
}

Datasource credits the data provider of a GeocodeResult.

type Error

type Error struct {
	// StatusCode is the HTTP status of the response.
	StatusCode int

	// Message is the human-readable reason reported by the API.
	Message string

	// OSRMCode is the routing engine's status code, such as "NoRoute" or
	// "InvalidValue". Empty when the error did not come from the routing
	// engine.
	OSRMCode string

	// VROOMCode is the optimization engine's non-zero status code. Zero
	// when the error did not come from the optimization engine.
	VROOMCode int

	// Body is the raw response body, truncated to a sane limit. Useful for
	// logging responses this package does not model.
	Body []byte
}

Error describes a failed API call.

It is returned whenever the API responds with a non-2xx status, and also when a 2xx response carries an in-body failure code — the routing and optimization engines can both report errors alongside HTTP 200.

Match it with errors.Is against the package sentinels, or retrieve it with errors.As when the status code or raw body is needed:

var apiErr *justrouting.Error
if errors.As(err, &apiErr) {
	log.Printf("status %d: %s", apiErr.StatusCode, apiErr.Message)
}
Example

Failures are classified with sentinel errors, so callers never match on message text.

package main

import (
	"context"
	"errors"
	"fmt"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	_, err := client.Routes.Get(context.Background(), &justrouting.RouteRequest{
		Origin:      justrouting.Point{103.8198, 1.3521},
		Destination: justrouting.Point{101.6869, 3.1390},
	})

	switch {
	case err == nil:
		fmt.Println("ok")
	case errors.Is(err, justrouting.ErrCrossCountry):
		fmt.Println("all coordinates must be in the same country")
	case errors.Is(err, justrouting.ErrQuotaExceeded):
		fmt.Println("daily quota used up; retrying will not help")
	case errors.Is(err, justrouting.ErrRateLimited):
		fmt.Println("throttled; the client already retried")
	case errors.Is(err, justrouting.ErrNoRoute):
		fmt.Println("no road connects these points")
	default:
		// Reach for the concrete type when the status or raw body matters.
		var apiErr *justrouting.Error
		if errors.As(err, &apiErr) {
			fmt.Printf("HTTP %d: %s\n", apiErr.StatusCode, apiErr.Message)
		}
	}
}

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) Is

func (e *Error) Is(target error) bool

Is reports whether e matches one of the package sentinel errors, so that errors.Is classifies API failures without string matching in user code.

type GeocodeQuery added in v0.2.0

type GeocodeQuery struct {
	// Text is the free-text query as received by the API.
	Text string `json:"text"`
	// Parsed is the API's structured interpretation of Text. Its shape is
	// not guaranteed, so it is preserved as raw JSON.
	Parsed json.RawMessage `json:"parsed"`
}

GeocodeQuery echoes how the API interpreted the request.

type GeocodeRequest added in v0.2.0

type GeocodeRequest struct {
	// Text is the free-form address to search for, such as
	// "Marina Bay Sands, Singapore". Mutually exclusive with Structured.
	Text string

	// Structured is an address given as typed components. Mutually
	// exclusive with Text.
	Structured *StructuredQuery

	// Limit caps the number of results. The API defaults to 5.
	Limit int

	// Offset skips the first Offset results before applying Limit.
	Offset int

	// Filters restrict results, for example "countrycode:sg". Each entry
	// is sent as its own filter parameter; the API applies all of them.
	Filters []string

	// Bias steers results toward a location, for example
	// "proximity:103.8,1.3". It is forwarded verbatim.
	Bias string

	// Type restricts results to a feature type such as "street" or "city".
	Type string

	// Lang requests results in a language, such as "en".
	Lang string
}

GeocodeRequest describes a forward-geocoding search. Exactly one of Text and Structured must be set.

type GeocodeResponse added in v0.2.0

type GeocodeResponse struct {
	// Results are ordered best first. Empty when nothing matches.
	Results []*GeocodeResult `json:"results"`
	// Query echoes how the API interpreted the request, when returned.
	Query *GeocodeQuery `json:"query"`
}

GeocodeResponse is the result of a forward-geocoding search.

type GeocodeResult added in v0.2.0

type GeocodeResult struct {
	// Datasource credits the data provider.
	Datasource   Datasource `json:"datasource,omitempty"`
	Housenumber  string     `json:"housenumber,omitempty"`
	Street       string     `json:"street,omitempty"`
	Suburb       string     `json:"suburb,omitempty"`
	District     string     `json:"district,omitempty"`
	City         string     `json:"city,omitempty"`
	County       string     `json:"county,omitempty"`
	State        string     `json:"state,omitempty"`
	StateCode    string     `json:"state_code,omitempty"`
	Postcode     string     `json:"postcode,omitempty"`
	Country      string     `json:"country,omitempty"`
	CountryCode  string     `json:"country_code,omitempty"`
	Lon          float64    `json:"lon"`
	Lat          float64    `json:"lat"`
	Formatted    string     `json:"formatted,omitempty"`
	AddressLine1 string     `json:"address_line1,omitempty"`
	AddressLine2 string     `json:"address_line2,omitempty"`
	ResultType   string     `json:"result_type,omitempty"`
	// Rank scores the result's relevance and match confidence.
	Rank Rank `json:"rank,omitempty"`
	// Timezone is the result's time zone with standard- and daylight-time
	// offsets.
	Timezone Timezone `json:"timezone,omitempty"`
	// PlaceID is the upstream's opaque identifier for the place.
	PlaceID  string `json:"place_id,omitempty"`
	Category string `json:"category,omitempty"`
	PlusCode string `json:"plus_code,omitempty"`
	Name     string `json:"name,omitempty"`
	// BBox is the result's bounding box as [lon1, lat1, lon2, lat2].
	BBox BBox `json:"bbox,omitempty"`
	// Distance is the metres from the bias location, present only when
	// Bias is set.
	Distance float64 `json:"distance,omitempty"`
}

GeocodeResult is one matching place.

func (*GeocodeResult) Location added in v0.2.0

func (r *GeocodeResult) Location() Point

Location returns the result's position as a Point, in the package's usual [longitude, latitude] order.

type GeocodeService added in v0.2.0

type GeocodeService struct {
	// contains filtered or unexported fields
}

GeocodeService converts addresses into coordinates.

func (*GeocodeService) Search added in v0.2.0

Search geocodes req and returns matching places, ordered best first.

results, err := client.Geocode.Search(ctx, &justrouting.GeocodeRequest{
	Text: "Marina Bay Sands, Singapore",
})
fmt.Println(results.Results[0].Formatted)

A response with an empty Results list means nothing matched; it is not an error.

Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	results, err := client.Geocode.Search(context.Background(), &justrouting.GeocodeRequest{
		Text: "Marina Bay Sands, Singapore",
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, r := range results.Results {
		fmt.Printf("%s (%.4f, %.4f)\n", r.Formatted, r.Lon, r.Lat)
	}
}

type Geometry

type Geometry struct {
	// contains filtered or unexported fields
}

Geometry holds a route geometry. OSRM encodes it either as an encoded polyline string (geometries=polyline, the default, or polyline6) or as a GeoJSON LineString object (geometries=geojson). Geometry preserves whichever form the server sent; read it with Geometry.Polyline or Geometry.GeoJSON.

func (Geometry) GeoJSON

func (g Geometry) GeoJSON() (*LineString, error)

GeoJSON returns the geometry as a GeoJSON LineString. It reports an error if the request used the default polyline encoding.

func (Geometry) IsZero

func (g Geometry) IsZero() bool

IsZero reports whether the server omitted the geometry, which happens when a request sets overview=false.

func (Geometry) MarshalJSON

func (g Geometry) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

func (Geometry) Polyline

func (g Geometry) Polyline() (string, error)

Polyline returns the geometry as an encoded polyline string. It reports an error if the request asked for GeoJSON instead.

func (Geometry) Raw

func (g Geometry) Raw() json.RawMessage

Raw returns the geometry exactly as the server encoded it.

func (*Geometry) UnmarshalJSON

func (g *Geometry) UnmarshalJSON(b []byte) error

UnmarshalJSON implements json.Unmarshaler.

type Health

type Health struct {
	// Status is "ok" when every upstream is reachable, otherwise
	// "degraded".
	Status string `json:"status"`
	// Timestamp is when the check ran, in RFC 3339 format.
	Timestamp string `json:"timestamp"`
	// Upstreams maps each routing engine to its reachability.
	Upstreams map[string]bool `json:"upstreams"`
}

Health is the API's self-reported status.

func (*Health) OK

func (h *Health) OK() bool

OK reports whether every upstream is healthy.

type HealthService

type HealthService struct {
	// contains filtered or unexported fields
}

HealthService reports API availability.

func (*HealthService) Get

func (s *HealthService) Get(ctx context.Context) (*Health, error)

Get reports the current API status. It is the only call that works without an API key, which makes it useful as a connectivity check.

type Intersection

type Intersection struct {
	Location Point  `json:"location"`
	Bearings []int  `json:"bearings"`
	Entry    []bool `json:"entry"`
	In       *int   `json:"in,omitempty"`
	Out      *int   `json:"out,omitempty"`
	Lanes    []Lane `json:"lanes,omitempty"`
	// Classes lists the road classes of the exiting roads, such as "toll"
	// and "ferry". The engine reports it only when turn-by-turn steps are
	// requested (see [RouteRequest.Steps]).
	Classes []string `json:"classes,omitempty"`
}

Intersection is a junction passed during a Step.

type Job

type Job struct {
	// ID identifies the job in the solution. It must be unique.
	ID int `json:"id"`
	// Location is where the job happens, as [longitude, latitude]. Omit
	// when custom matrices cover every profile in use; then LocationIndex
	// is required.
	Location Point `json:"location,omitempty"`
	// LocationIndex is the row/column in custom matrices that replaces
	// Location.
	LocationIndex *int `json:"location_index,omitempty"`
	// Setup is fixed preparation time in seconds.
	Setup int `json:"setup,omitempty"`
	// Service is time spent on site, in seconds.
	Service int `json:"service,omitempty"`
	// SetupPerType overrides Setup for vehicles of each named type.
	SetupPerType map[string]int `json:"setup_per_type,omitempty"`
	// ServicePerType overrides Service for vehicles of each named type.
	ServicePerType map[string]int `json:"service_per_type,omitempty"`
	// Delivery is the amount unloaded here, matching vehicle Capacity.
	Delivery []int `json:"delivery,omitempty"`
	// Pickup is the amount loaded here, matching vehicle Capacity.
	Pickup []int `json:"pickup,omitempty"`
	// Skills lists capabilities a vehicle must have to serve this job.
	Skills []int `json:"skills,omitempty"`
	// Priority ranks this job from 0 to 100 when not everything fits.
	Priority int `json:"priority,omitempty"`
	// TimeWindows constrains when the job may be served.
	TimeWindows []TimeWindow `json:"time_windows,omitempty"`
	// Description is an opaque label echoed back in the solution.
	Description string `json:"description,omitempty"`
}

Job is a task carried out at a single location.

type Lane

type Lane struct {
	Indications []string `json:"indications"`
	Valid       bool     `json:"valid"`
}

Lane describes a turn lane at an Intersection.

type Leg

type Leg struct {
	Distance   float64     `json:"distance"`
	Duration   float64     `json:"duration"`
	Weight     float64     `json:"weight"`
	Summary    string      `json:"summary"`
	Steps      []*Step     `json:"steps,omitempty"`
	Annotation *Annotation `json:"annotation,omitempty"`
}

Leg is the portion of a route between two consecutive waypoints.

type LineString

type LineString struct {
	Type        string  `json:"type"`
	Coordinates []Point `json:"coordinates"`
}

LineString is a GeoJSON LineString geometry.

type Maneuver

type Maneuver struct {
	Location      Point  `json:"location"`
	BearingBefore int    `json:"bearing_before"`
	BearingAfter  int    `json:"bearing_after"`
	Type          string `json:"type"`
	Modifier      string `json:"modifier,omitempty"`
	Exit          int    `json:"exit,omitempty"`
}

Maneuver describes the action taken at the start of a Step.

type MapMatchingRequest added in v0.3.0

type MapMatchingRequest struct {
	// Coordinates is the trace to match, in chronological order.
	Coordinates []Point

	// Timestamps are UNIX seconds for each coordinate, in monotonically
	// increasing order. They are required: the API specification and
	// upstream OSRM both expect them, even though the hosted gateway
	// currently accepts requests without them (with degraded matching).
	Timestamps []int64

	// Radiuses are the maximum distance in metres each coordinate may
	// snap, one value per coordinate. The engine's default snap distance
	// is only a few metres, so GPS points further from the road need this
	// set.
	Radiuses []float64

	// Gaps controls how a trace with gaps is matched: "split" (the
	// default) or "ignore".
	Gaps string

	// Tidy drops tracepoints that cannot be matched from the response.
	Tidy bool

	// Waypoints selects which coordinates to use as waypoints, by index
	// into Coordinates. Empty means all of them.
	Waypoints []int

	// Snapping controls edge snapping: "default" or "any".
	Snapping string

	// Profile selects the routing profile. Defaults to [DefaultProfile].
	Profile string

	// Steps requests turn-by-turn instructions on each leg.
	Steps bool

	// Annotations requests per-segment metadata. Valid values include
	// "duration", "distance", "speed" and "nodes".
	Annotations []string

	// Geometries selects the geometry encoding: "polyline" (the default),
	// "polyline6" or "geojson". See [Geometry].
	Geometries string

	// Overview controls geometry detail: "simplified" (the default),
	// "full" or "false" to omit it.
	Overview string

	// Exclude lists road classes to avoid, such as "motorway" or "ferry".
	// Supported values depend on the profile.
	Exclude []string

	// Bearings restricts the direction of travel at each coordinate, one
	// entry per coordinate in visiting order: "{bearing}" or
	// "{bearing},{range}" in degrees, for example "0,20" for north within
	// ±20°. Coordinates without a restriction use the default when their
	// entry is missing at the end of the list.
	Bearings []string

	// Hints are opaque tokens from a previous response's tracepoints that
	// speed up snapping, one entry per coordinate in visiting order.
	Hints []string

	// Approaches restricts the road direction at each waypoint, one entry
	// per waypoint in visiting order: "curb", "opposite", or
	// "unrestricted" (the engine default). An empty entry also selects
	// the default.
	Approaches []string

	// GenerateHints requests a "hint" on every response tracepoint for
	// reuse in later requests. The engine does this by default; set to
	// false to omit hints.
	GenerateHints *bool

	// SkipWaypoints omits the response waypoints. They still take part in
	// the matching calculation.
	SkipWaypoints bool
}

MapMatchingRequest describes a map-matching query. Coordinates is required and must hold at least two points in chronological order.

The number of coordinates is capped by the account plan; exceeding it returns an error matching ErrPlanLimitExceeded.

type MapMatchingResponse added in v0.3.0

type MapMatchingResponse struct {
	// Code is the engine status, "Ok" on success.
	Code string `json:"code"`
	// Message explains a non-Ok Code.
	Message string `json:"message,omitempty"`
	// Tracepoints are the input coordinates snapped to the road network,
	// aligned with req.Coordinates. An entry is nil when that coordinate
	// could not be matched.
	Tracepoints []*Tracepoint `json:"tracepoints"`
	// Matchings are the routes assembled from the trace, best first.
	Matchings []*Match `json:"matchings"`
}

MapMatchingResponse is the full result of a map-matching query.

type MapMatchingService added in v0.3.0

type MapMatchingService struct {
	// contains filtered or unexported fields
}

MapMatchingService snaps a noisy GPS trace onto the road network and assembles the driven route.

func (*MapMatchingService) Get added in v0.3.0

Get returns the best matching for req.

match, err := client.MapMatching.Get(ctx, &justrouting.MapMatchingRequest{
	Coordinates: trace, // GPS points, in order
})
fmt.Printf("%.0f%% confidence, %.1f km\n", match.Confidence*100, match.Distance/1000)

It returns an error with OSRMCode "NoMatch" when no coordinate can be matched. Use MapMatchingService.GetAll for the snapped tracepoints.

Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	// A noisy GPS trace, in chronological order.
	trace := []justrouting.Point{
		{103.8198, 1.3521},
		{103.8514, 1.2897},
		{103.9915, 1.3644},
	}

	match, err := client.MapMatching.Get(context.Background(), &justrouting.MapMatchingRequest{
		Coordinates: trace,
		Timestamps:  []int64{1720000000, 1720000300, 1720000600},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%.0f%% confidence, %.1f km\n", match.Confidence*100, match.Distance/1000)
}

func (*MapMatchingService) GetAll added in v0.3.0

GetAll returns every matching the engine produced for req, along with the snapped tracepoints.

type Match added in v0.3.0

type Match struct {
	Route
	// Confidence is the engine's trust in the match, from 0 to 1.
	Confidence float64 `json:"confidence"`
}

Match is one route assembled from a trace. It embeds Route, adding the engine's confidence in the match.

type MatrixRequest

type MatrixRequest struct {
	// Coordinates are the points to measure between.
	Coordinates []Point

	// Sources selects which coordinates act as row origins, by index into
	// Coordinates. Empty means all of them.
	Sources []int

	// Destinations selects which coordinates act as column targets, by
	// index into Coordinates. Empty means all of them.
	Destinations []int

	// Annotations selects which matrices to compute: "duration",
	// "distance", or both. Defaults to both.
	Annotations []string

	// FallbackSpeed estimates the duration of unreachable pairs from their
	// as-the-crow-flies distance at this speed, in km/h. The response
	// reports which cells were estimated in [MatrixResponse.FallbackSpeedCells].
	FallbackSpeed *float64

	// FallbackCoordinate selects which coordinates fallback estimates are
	// measured from: "input" (the engine default) or "snapped".
	FallbackCoordinate string

	// ScaleFactor scales every duration by this factor, for example 1.2
	// for +20%.
	ScaleFactor *float64

	// Bearings restricts the direction of travel at each coordinate, one
	// entry per coordinate in visiting order: "{bearing}" or
	// "{bearing},{range}" in degrees, for example "0,20" for north within
	// ±20°. Coordinates without a restriction use the default when their
	// entry is missing at the end of the list.
	Bearings []string

	// Radiuses sets the snapping radius in meters per coordinate, one
	// entry per coordinate in visiting order; "unlimited" disables
	// snapping for that coordinate.
	Radiuses []string

	// Hints are opaque tokens from a previous response's waypoints that
	// speed up snapping, one entry per coordinate in visiting order.
	Hints []string

	// Approaches restricts the road direction at each waypoint, one entry
	// per waypoint in visiting order: "curb", "opposite", or
	// "unrestricted" (the engine default). An empty entry also selects
	// the default.
	Approaches []string

	// Exclude lists road classes to avoid, such as "motorway" or "ferry".
	// Supported values depend on the profile.
	Exclude []string

	// Snapping selects how coordinates snap onto the road network:
	// "default" (the engine default) avoids edges that cannot be used as
	// start or end points; "any" snaps to any edge.
	Snapping string

	// GenerateHints requests a "hint" on every response waypoint for
	// reuse in later requests. The engine does this by default; set to
	// false to omit hints.
	GenerateHints *bool

	// SkipWaypoints omits the response sources and destinations. They
	// still take part in the matrix calculation.
	SkipWaypoints bool

	// Profile selects the routing profile. Defaults to [DefaultProfile].
	Profile string
}

MatrixRequest describes a matrix query. Coordinates is required and must hold at least two points.

By default every coordinate is used as both a source and a destination, producing an NxN matrix. Set Sources and/or Destinations to compute a rectangular subset instead, which is considerably cheaper.

The number of coordinates is capped by the account plan; exceeding it returns an error matching ErrPlanLimitExceeded.

type MatrixResponse

type MatrixResponse struct {
	// Code is the engine status, "Ok" on success.
	Code string `json:"code"`
	// Message explains a non-Ok Code.
	Message string `json:"message,omitempty"`

	// Durations holds travel times in seconds, indexed [source][destination].
	Durations [][]*float64 `json:"durations,omitempty"`
	// Distances holds travel distances in metres, indexed [source][destination].
	Distances [][]*float64 `json:"distances,omitempty"`

	// Sources are the source coordinates snapped to the road network.
	Sources []*Waypoint `json:"sources"`
	// Destinations are the destination coordinates snapped to the road network.
	Destinations []*Waypoint `json:"destinations"`

	// FallbackSpeedCells lists the [source, destination] pairs whose
	// duration was estimated via the fallback_speed option.
	FallbackSpeedCells [][]int `json:"fallback_speed_cells,omitempty"`
}

MatrixResponse holds the computed matrices.

Entries are pointers because the engine reports an unreachable pair as null, which must stay distinguishable from a genuine zero. Prefer the MatrixResponse.Duration and MatrixResponse.Distance accessors.

func (*MatrixResponse) Distance

func (r *MatrixResponse) Distance(i, j int) (float64, bool)

Distance returns the travel distance in metres from source i to destination j. The boolean is false when the indices are out of range or the pair is unreachable.

func (*MatrixResponse) Duration

func (r *MatrixResponse) Duration(i, j int) (float64, bool)

Duration returns the travel time in seconds from source i to destination j. The boolean is false when the indices are out of range or the pair is unreachable.

type MatrixService

type MatrixService struct {
	// contains filtered or unexported fields
}

MatrixService computes travel duration and distance between many coordinates at once.

func (*MatrixService) Get

Get computes the duration and distance matrices for req.

m, err := client.Matrix.Get(ctx, &justrouting.MatrixRequest{
	Coordinates: []justrouting.Point{depot, stopA, stopB},
})
if secs, ok := m.Duration(0, 1); ok {
	fmt.Printf("depot -> stopA: %.0f min", secs/60)
}
Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	depot := justrouting.Point{103.8198, 1.3521}
	stops := []justrouting.Point{
		{103.8514, 1.2897},
		{103.9915, 1.3644},
	}

	m, err := client.Matrix.Get(context.Background(), &justrouting.MatrixRequest{
		Coordinates: append([]justrouting.Point{depot}, stops...),
		// Only the depot-to-stop row is needed, which is cheaper than
		// computing the full square matrix.
		Sources:      []int{0},
		Destinations: []int{1, 2},
	})
	if err != nil {
		log.Fatal(err)
	}

	for j := range stops {
		if seconds, ok := m.Duration(0, j); ok {
			fmt.Printf("stop %d: %.0f min\n", j+1, seconds/60)
		} else {
			fmt.Printf("stop %d: unreachable\n", j+1)
		}
	}
}

type NearestRequest added in v0.3.0

type NearestRequest struct {
	// Coordinate is the point to snap, as [longitude, latitude].
	Coordinate Point

	// Number is how many nearby segments to return, ordered by distance.
	// Defaults to 1.
	Number int

	// Profile selects the routing profile. Defaults to [DefaultProfile].
	Profile string

	// Exclude lists road classes to avoid, such as "motorway" or "ferry".
	// Supported values depend on the profile.
	Exclude []string

	// Bearings restricts the direction of travel at the coordinate:
	// "{bearing}" or "{bearing},{range}" in degrees, for example "0,20"
	// for north within ±20°.
	Bearings []string

	// Radiuses sets the snapping radius in meters for the coordinate;
	// "unlimited" disables snapping.
	Radiuses []string

	// Hints are opaque tokens from a previous response's waypoints that
	// speed up snapping.
	Hints []string

	// Approaches restricts the road direction at the waypoint: "curb",
	// "opposite", or "unrestricted" (the engine default).
	Approaches []string

	// Snapping selects how the coordinate snaps onto the road network:
	// "default" (the engine default) avoids edges that cannot be used as
	// start or end points; "any" snaps to any edge.
	Snapping string

	// GenerateHints requests a "hint" on every response waypoint for
	// reuse in later requests. The engine does this by default; set to
	// false to omit hints.
	GenerateHints *bool

	// SkipWaypoints omits the response waypoints. They still take part in
	// the nearest-segment calculation.
	SkipWaypoints bool
}

NearestRequest describes a nearest-segment query. Coordinate is required.

type NearestResponse added in v0.3.0

type NearestResponse struct {
	// Code is the engine status, "Ok" on success.
	Code string `json:"code"`
	// Message explains a non-Ok Code.
	Message string `json:"message,omitempty"`
	// Waypoints are the segments nearest to the coordinate, best first.
	Waypoints []*Waypoint `json:"waypoints"`
}

NearestResponse is the result of a nearest-segment query.

type NearestService added in v0.3.0

type NearestService struct {
	// contains filtered or unexported fields
}

NearestService finds the road segments closest to a coordinate.

func (*NearestService) Get added in v0.3.0

Get returns the road segment closest to req.Coordinate.

wp, err := client.Nearest.Get(ctx, &justrouting.NearestRequest{
	Coordinate: []float64{103.8198, 1.3521},
})
fmt.Printf("snapped to %s, %.0f m away\n", wp.Name, wp.Distance)

Use NearestService.GetAll for the second-, third-, ... nearest segments.

Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	wp, err := client.Nearest.Get(context.Background(), &justrouting.NearestRequest{
		Coordinate: justrouting.Point{103.8198, 1.3521},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s, %.0f m away\n", wp.Name, wp.Distance)
}

func (*NearestService) GetAll added in v0.3.0

GetAll returns up to req.Number road segments near req.Coordinate, best first.

type OptimizationOptions

type OptimizationOptions struct {
	// Geometry requests a road-following geometry for each route. It costs
	// extra computation, so it is off by default.
	Geometry bool `json:"g,omitempty"`
}

OptimizationOptions tunes solver behaviour.

type OptimizationRequest

type OptimizationRequest struct {
	// Vehicles is the available fleet.
	Vehicles []Vehicle `json:"vehicles"`
	// Jobs are single-location tasks.
	Jobs []Job `json:"jobs,omitempty"`
	// Shipments are pickup-and-delivery pairs handled by one vehicle.
	Shipments []Shipment `json:"shipments,omitempty"`
	// Matrices are custom matrices per routing profile; the map keys are
	// profile names such as "car" or "motorcycle".
	Matrices map[string]CustomMatrix `json:"matrices,omitempty"`
	// Options tunes the solver.
	Options *OptimizationOptions `json:"options,omitempty"`
}

OptimizationRequest describes a vehicle routing problem. At least one vehicle and at least one job or shipment are required.

Fleet and task counts are capped by the account plan; exceeding either returns an error matching ErrPlanLimitExceeded.

type OptimizationService

type OptimizationService struct {
	// contains filtered or unexported fields
}

OptimizationService solves vehicle routing problems: given a fleet and a set of tasks, it assigns tasks to vehicles and orders each vehicle's stops.

func (*OptimizationService) Solve

Solve assigns the request's tasks to its vehicles and orders each route.

solution, err := client.Optimization.Solve(ctx, &justrouting.OptimizationRequest{
	Vehicles: []justrouting.Vehicle{{ID: 1, Start: depot, End: depot}},
	Jobs: []justrouting.Job{
		{ID: 1, Location: stopA},
		{ID: 2, Location: stopB},
	},
})

All coordinates in a request must lie within a single country; otherwise the call fails with an error matching ErrCrossCountry.

Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	depot := justrouting.Point{103.8198, 1.3521}

	solution, err := client.Optimization.Solve(context.Background(), &justrouting.OptimizationRequest{
		Vehicles: []justrouting.Vehicle{
			{ID: 1, Start: depot, End: depot, Capacity: []int{4}},
		},
		Jobs: []justrouting.Job{
			{ID: 1, Location: justrouting.Point{103.8514, 1.2897}, Delivery: []int{1}, Service: 300},
			{ID: 2, Location: justrouting.Point{103.9915, 1.3644}, Delivery: []int{2}, Service: 300},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, route := range solution.Routes {
		fmt.Printf("vehicle %d serves %d stops\n", route.Vehicle, len(route.Steps))
	}
	fmt.Printf("%d task(s) unassigned\n", len(solution.Unassigned))
}

type Point

type Point []float64

Point is a geographic coordinate expressed as [longitude, latitude] — the order used by GeoJSON, OSRM and VROOM, and the reverse of the "lat, lng" convention used by most map UIs.

Point is defined over []float64, so an ordinary slice literal works anywhere a Point is expected:

Origin: []float64{103.8198, 1.3521}   // Singapore

func (Point) Lat

func (p Point) Lat() float64

Lat returns the latitude, or 0 if p is not a well-formed pair.

func (Point) Lon

func (p Point) Lon() float64

Lon returns the longitude, or 0 if p is not a well-formed pair.

func (Point) String

func (p Point) String() string

String formats p as "longitude,latitude" using the shortest representation that round-trips exactly.

func (Point) Validate

func (p Point) Validate() error

Validate reports whether p is a well-formed [longitude, latitude] pair within the valid ranges. It wraps ErrInvalidCoordinates.

type Rank added in v0.2.0

type Rank struct {
	Importance            float64 `json:"importance"`
	Popularity            float64 `json:"popularity"`
	Confidence            float64 `json:"confidence"`
	ConfidenceCityLevel   float64 `json:"confidence_city_level"`
	ConfidenceStreetLevel float64 `json:"confidence_street_level"`
	MatchType             string  `json:"match_type"`
}

Rank scores a GeocodeResult's relevance and match confidence.

type Route

type Route struct {
	// Distance is the route length in metres.
	Distance float64 `json:"distance"`
	// Duration is the estimated travel time in seconds.
	Duration float64 `json:"duration"`
	// Weight is the value the engine minimised, in WeightName units.
	Weight float64 `json:"weight"`
	// WeightName names the optimisation metric, such as "routability".
	WeightName string `json:"weight_name"`
	// Geometry is the route's shape.
	Geometry Geometry `json:"geometry"`
	// Legs holds one entry per consecutive pair of waypoints.
	Legs []*Leg `json:"legs"`
}

Route is a single path through the road network.

type RouteRequest

type RouteRequest struct {
	// Origin is where the route starts, as [longitude, latitude].
	Origin Point
	// Destination is where the route ends, as [longitude, latitude].
	Destination Point
	// Waypoints are intermediate stops, visited in the order given.
	Waypoints []Point

	// Profile selects the routing profile. Defaults to [DefaultProfile].
	Profile string

	// Alternatives requests up to this many alternative routes. They are
	// returned by GetAll; Get always yields the best route. The engine may
	// return fewer, or none, if no reasonable alternative exists.
	Alternatives int

	// Steps requests turn-by-turn instructions on each leg.
	Steps bool

	// Annotations requests per-segment metadata. Valid values include
	// "duration", "distance", "speed" and "nodes".
	Annotations []string

	// Geometries selects the geometry encoding: "polyline" (the default),
	// "polyline6" or "geojson". See [Geometry].
	Geometries string

	// Overview controls geometry detail: "simplified" (the default),
	// "full" or "false" to omit it.
	Overview string

	// ContinueStraight forces or forbids continuing straight at the first
	// waypoint. Nil leaves the decision to the engine.
	ContinueStraight *bool

	// Exclude lists road classes to avoid, such as "motorway" or "ferry".
	// Supported values depend on the profile.
	Exclude []string

	// Bearings restricts the direction of travel at each coordinate, one
	// entry per coordinate in visiting order: "{bearing}" or
	// "{bearing},{range}" in degrees, for example "0,20" for north within
	// ±20°. Coordinates without a restriction use the default when their
	// entry is missing at the end of the list.
	Bearings []string

	// Radiuses sets the snapping radius in meters per coordinate, one
	// entry per coordinate in visiting order; "unlimited" disables
	// snapping for that coordinate.
	Radiuses []string

	// Hints are opaque tokens from a previous response's waypoints that
	// speed up snapping, one entry per coordinate in visiting order.
	Hints []string

	// Approaches restricts the road direction at each waypoint, one entry
	// per waypoint in visiting order: "curb", "opposite", or
	// "unrestricted" (the engine default). An empty entry also selects
	// the default.
	Approaches []string

	// Snapping selects how coordinates snap onto the road network:
	// "default" (the engine default) avoids edges that cannot be used as
	// start or end points; "any" snaps to any edge.
	Snapping string

	// GenerateHints requests a "hint" on every response waypoint for
	// reuse in later requests. The engine does this by default; set to
	// false to omit hints.
	GenerateHints *bool

	// SkipWaypoints omits the response waypoints. They still take part in
	// the route calculation.
	SkipWaypoints bool

	// WaypointIndices lists the indices of the input coordinates that the
	// response should treat as waypoints, for example [0, 3] to keep only
	// the first and last. The engine treats every input coordinate as a
	// waypoint by default.
	WaypointIndices []int
}

RouteRequest describes a routing query. Origin and Destination are required; every other field is optional.

type RouteResponse

type RouteResponse struct {
	// Code is the engine status, "Ok" on success.
	Code string `json:"code"`
	// Message explains a non-Ok Code.
	Message string `json:"message,omitempty"`
	// DataVersion is the timestamp of the OSM data the engine used.
	DataVersion string `json:"data_version,omitempty"`
	// Routes are ordered best first.
	Routes []*Route `json:"routes"`
	// Waypoints are the input coordinates snapped to the road network.
	Waypoints []*Waypoint `json:"waypoints"`
}

RouteResponse is the full result of a routing query.

type RouteStep

type RouteStep struct {
	// Type is one of "start", "job", "pickup", "delivery", "break" or "end".
	Type string `json:"type"`
	// Location is where the stop happens.
	Location Point `json:"location,omitempty"`
	// LocationIndex is the row/column in custom matrices, when the
	// request used matrices.
	LocationIndex int `json:"location_index,omitempty"`
	// ID is the task ID, for task steps.
	ID int `json:"id,omitempty"`
	// Job is the job ID, for job steps.
	Job         int `json:"job,omitempty"`
	Setup       int `json:"setup"`
	Service     int `json:"service"`
	WaitingTime int `json:"waiting_time"`
	// Arrival is the arrival time in seconds.
	Arrival  int `json:"arrival"`
	Duration int `json:"duration"`
	Distance int `json:"distance,omitempty"`
	// Load is the vehicle load after this stop.
	Load        []int       `json:"load,omitempty"`
	Description string      `json:"description,omitempty"`
	Violations  []Violation `json:"violations,omitempty"`
}

RouteStep is a single stop on a VehicleRoute.

type RoutesService

type RoutesService struct {
	// contains filtered or unexported fields
}

RoutesService computes the fastest route between coordinates.

func (*RoutesService) Get

func (s *RoutesService) Get(ctx context.Context, req *RouteRequest) (*Route, error)

Get returns the best route for req.

route, err := client.Routes.Get(ctx, &justrouting.RouteRequest{
	Origin:      []float64{103.8198, 1.3521},
	Destination: []float64{103.9915, 1.3644},
})
fmt.Printf("%.1f km", route.Distance/1000)

It returns an error matching ErrNoRoute when the coordinates cannot be connected. Use RoutesService.GetAll for alternatives and snapped waypoints.

Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	route, err := client.Routes.Get(context.Background(), &justrouting.RouteRequest{
		Origin:      justrouting.Point{103.8198, 1.3521},
		Destination: justrouting.Point{103.9915, 1.3644},
		Waypoints:   []justrouting.Point{{103.8514, 1.2897}},
		Overview:    "full",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%.1f km in %.0f min\n", route.Distance/1000, route.Duration/60)

	if polyline, err := route.Geometry.Polyline(); err == nil {
		fmt.Println("geometry:", polyline)
	}
}

func (*RoutesService) GetAll

func (s *RoutesService) GetAll(ctx context.Context, req *RouteRequest) (*RouteResponse, error)

GetAll returns every route the engine produced for req, along with the snapped input waypoints.

type Shipment

type Shipment struct {
	Pickup   ShipmentStep `json:"pickup"`
	Delivery ShipmentStep `json:"delivery"`
	// Amount is the load carried between the two steps.
	Amount []int `json:"amount,omitempty"`
	// Skills lists capabilities a vehicle must have.
	Skills []int `json:"skills,omitempty"`
	// Priority ranks this shipment from 0 to 100.
	Priority int `json:"priority,omitempty"`
}

Shipment is a pickup and a delivery that must be served in order by the same vehicle.

type ShipmentStep

type ShipmentStep struct {
	ID int `json:"id"`
	// Location is where the step happens, as [longitude, latitude]. Omit
	// when custom matrices cover every profile in use; then LocationIndex
	// is required.
	Location Point `json:"location,omitempty"`
	// LocationIndex is the row/column in custom matrices that replaces
	// Location.
	LocationIndex *int `json:"location_index,omitempty"`
	// Setup is fixed preparation time in seconds.
	Setup int `json:"setup,omitempty"`
	// Service is time spent on site, in seconds.
	Service int `json:"service,omitempty"`
	// SetupPerType overrides Setup for vehicles of each named type.
	SetupPerType map[string]int `json:"setup_per_type,omitempty"`
	// ServicePerType overrides Service for vehicles of each named type.
	ServicePerType map[string]int `json:"service_per_type,omitempty"`
	// TimeWindows constrains when the step may be served.
	TimeWindows []TimeWindow `json:"time_windows,omitempty"`
	// Description is an opaque label echoed back in the solution.
	Description string `json:"description,omitempty"`
}

ShipmentStep is one half of a Shipment.

type Solution

type Solution struct {
	// Code is the engine status, 0 on success.
	Code int `json:"code"`
	// Error explains a non-zero Code.
	Error string `json:"error,omitempty"`
	// Summary aggregates the whole solution.
	Summary Summary `json:"summary"`
	// Routes holds one entry per vehicle that was used.
	Routes []*VehicleRoute `json:"routes"`
	// Unassigned lists tasks that could not be served.
	Unassigned []*Unassigned `json:"unassigned"`
}

Solution is the result of an optimization run.

type Step

type Step struct {
	Distance      float64         `json:"distance"`
	Duration      float64         `json:"duration"`
	Weight        float64         `json:"weight"`
	Geometry      Geometry        `json:"geometry"`
	Name          string          `json:"name"`
	Ref           string          `json:"ref,omitempty"`
	Mode          string          `json:"mode"`
	Maneuver      Maneuver        `json:"maneuver"`
	Intersections []*Intersection `json:"intersections,omitempty"`
}

Step is a single turn-by-turn instruction, returned when RouteRequest.Steps is set.

type StructuredQuery added in v0.2.0

type StructuredQuery struct {
	Name string
	// Housenumber is lowercased to match the upstream parameter name.
	Housenumber string
	Street      string
	Postcode    string
	City        string
	State       string
	Country     string
}

StructuredQuery is an address expressed as typed fields. Only the fields that are set are sent.

type Summary

type Summary struct {
	Cost        int         `json:"cost"`
	Routes      int         `json:"routes"`
	Unassigned  int         `json:"unassigned"`
	Delivery    []int       `json:"delivery,omitempty"`
	Pickup      []int       `json:"pickup,omitempty"`
	Setup       int         `json:"setup"`
	Service     int         `json:"service"`
	Duration    int         `json:"duration"`
	WaitingTime int         `json:"waiting_time"`
	Priority    int         `json:"priority"`
	Distance    int         `json:"distance,omitempty"`
	Violations  []Violation `json:"violations,omitempty"`
}

Summary aggregates the cost and time of a whole Solution.

type TimeWindow

type TimeWindow [2]int

TimeWindow is an inclusive [start, end] pair, in seconds, relative to the same origin used by the rest of the request.

type Timezone added in v0.2.0

type Timezone struct {
	Name             string `json:"name"`
	OffsetSTD        string `json:"offset_STD"`
	OffsetSTDSeconds int    `json:"offset_STD_seconds"`
	OffsetDST        string `json:"offset_DST"`
	OffsetDSTSeconds int    `json:"offset_DST_seconds"`
	AbbreviationSTD  string `json:"abbreviation_STD"`
	AbbreviationDST  string `json:"abbreviation_DST"`
}

Timezone is a GeocodeResult's time zone with standard- and daylight-time offsets.

type Tracepoint added in v0.3.0

type Tracepoint struct {
	// AlternativesCount is how many other plausible matches exist for this
	// tracepoint.
	AlternativesCount int `json:"alternatives_count,omitempty"`
	// WaypointIndex is the index of this point within the matched route's
	// waypoints.
	WaypointIndex int `json:"waypoint_index,omitempty"`
	// MatchingsIndex identifies which entry of Matchings this tracepoint
	// belongs to.
	MatchingsIndex int `json:"matchings_index,omitempty"`
	// Distance is the metres between the input coordinate and Location.
	Distance float64 `json:"distance"`
	// Name is the street the coordinate snapped to, if known.
	Name string `json:"name"`
	// Location is the snapped position.
	Location Point `json:"location"`
	// Hint is an opaque token that can speed up subsequent requests.
	Hint string `json:"hint,omitempty"`
}

Tracepoint is one input coordinate snapped to the road network.

type TripRequest added in v0.3.0

type TripRequest struct {
	// Coordinates are the points to visit, in any order.
	Coordinates []Point

	// Roundtrip makes the trip end where it started. Nil leaves the
	// engine default, which is true.
	Roundtrip *bool

	// Source pins where the trip starts: "any" (the default) or "first".
	Source string

	// Destination pins where the trip ends: "any" (the default) or
	// "last".
	Destination string

	// Profile selects the routing profile. Defaults to [DefaultProfile].
	Profile string

	// Steps requests turn-by-turn instructions on each leg.
	Steps bool

	// Annotations requests per-segment metadata. Valid values include
	// "duration", "distance", "speed" and "nodes".
	Annotations []string

	// Geometries selects the geometry encoding: "polyline" (the default),
	// "polyline6" or "geojson". See [Geometry].
	Geometries string

	// Overview controls geometry detail: "simplified" (the default),
	// "full" or "false" to omit it.
	Overview string

	// Exclude lists road classes to avoid, such as "motorway" or "ferry".
	// Supported values depend on the profile.
	Exclude []string

	// Bearings restricts the direction of travel at each coordinate, one
	// entry per coordinate in visiting order: "{bearing}" or
	// "{bearing},{range}" in degrees, for example "0,20" for north within
	// ±20°. Coordinates without a restriction use the default when their
	// entry is missing at the end of the list.
	Bearings []string

	// Radiuses sets the snapping radius in meters per coordinate, one
	// entry per coordinate in visiting order; "unlimited" disables
	// snapping for that coordinate.
	Radiuses []string

	// Hints are opaque tokens from a previous response's waypoints that
	// speed up snapping, one entry per coordinate in visiting order.
	Hints []string

	// Approaches restricts the road direction at each waypoint, one entry
	// per waypoint in visiting order: "curb", "opposite", or
	// "unrestricted" (the engine default). An empty entry also selects
	// the default.
	Approaches []string

	// Snapping selects how coordinates snap onto the road network:
	// "default" (the engine default) avoids edges that cannot be used as
	// start or end points; "any" snaps to any edge.
	Snapping string

	// GenerateHints requests a "hint" on every response waypoint for
	// reuse in later requests. The engine does this by default; set to
	// false to omit hints.
	GenerateHints *bool

	// SkipWaypoints omits the response waypoints. They still take part in
	// the trip calculation.
	SkipWaypoints bool
}

TripRequest describes a trip query. Coordinates is required and must hold at least two points.

The engine solves the travelling-salesman problem with a greedy heuristic: the returned trip visits every coordinate once, in whatever order is fastest. The number of coordinates is capped by the account plan; exceeding it returns an error matching ErrPlanLimitExceeded.

type TripResponse added in v0.3.0

type TripResponse struct {
	// Code is the engine status, "Ok" on success.
	Code string `json:"code"`
	// Message explains a non-Ok Code.
	Message string `json:"message,omitempty"`
	// Trips are the candidate round trips, best first.
	Trips []*Route `json:"trips"`
	// Waypoints are the input coordinates snapped to the road network, in
	// the order the trip visits them.
	Waypoints []*Waypoint `json:"waypoints"`
}

TripResponse is the full result of a trip query.

type TripService added in v0.3.0

type TripService struct {
	// contains filtered or unexported fields
}

TripService finds the fastest order to visit a set of coordinates.

func (*TripService) Get added in v0.3.0

func (s *TripService) Get(ctx context.Context, req *TripRequest) (*Route, error)

Get returns the best trip for req.

trip, err := client.Trip.Get(ctx, &justrouting.TripRequest{
	Coordinates: []justrouting.Point{depot, stopA, stopB},
})
fmt.Printf("fastest order covers %.1f km\n", trip.Distance/1000)

The waypoints in TripResponse.Waypoints are ordered as visited. Use TripService.GetAll to retrieve them.

Example
package main

import (
	"context"
	"fmt"
	"log"

	justrouting "github.com/justrouting/go-client"
)

func main() {
	client := justrouting.NewClient("YOUR_API_KEY")

	depot := justrouting.Point{103.8198, 1.3521}
	trip, err := client.Trip.Get(context.Background(), &justrouting.TripRequest{
		Coordinates: []justrouting.Point{
			depot,
			{103.8514, 1.2897},
			{103.9915, 1.3644},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%.1f km in %.0f min\n", trip.Distance/1000, trip.Duration/60)
}

func (*TripService) GetAll added in v0.3.0

func (s *TripService) GetAll(ctx context.Context, req *TripRequest) (*TripResponse, error)

GetAll returns every trip the engine produced for req, along with the snapped waypoints in visiting order.

type Unassigned

type Unassigned struct {
	ID int `json:"id"`
	// Type is the task type, e.g. "job".
	Type string `json:"type,omitempty"`
	// Location is the task location, when provided in the request.
	Location Point `json:"location,omitempty"`
	// LocationIndex is the row/column in custom matrices, when the
	// request used matrices.
	LocationIndex int    `json:"location_index,omitempty"`
	Description   string `json:"description,omitempty"`
}

Unassigned is a task the solver could not fit into any route.

type Vehicle

type Vehicle struct {
	// ID identifies the vehicle in the solution. It must be unique.
	ID int `json:"id"`
	// Profile selects the routing profile for this vehicle.
	Profile string `json:"profile,omitempty"`
	// Start is where the vehicle begins, as [longitude, latitude]. Omit
	// for a vehicle that may start anywhere.
	Start Point `json:"start,omitempty"`
	// StartIndex is the row/column in custom matrices that replaces Start.
	StartIndex *int `json:"start_index,omitempty"`
	// End is where the vehicle must finish. Omit to end anywhere.
	End Point `json:"end,omitempty"`
	// EndIndex is the row/column in custom matrices that replaces End.
	EndIndex *int `json:"end_index,omitempty"`
	// Capacity is a multidimensional capacity vector. Its length must
	// match the Delivery and Pickup vectors on tasks.
	Capacity []int `json:"capacity,omitempty"`
	// Skills lists capabilities this vehicle provides.
	Skills []int `json:"skills,omitempty"`
	// Type names this vehicle for per-type setup and service times on
	// tasks.
	Type string `json:"type,omitempty"`
	// TimeWindow bounds when the vehicle is available.
	TimeWindow *TimeWindow `json:"time_window,omitempty"`
	// Breaks are rest periods during the route.
	Breaks []Break `json:"breaks,omitempty"`
	// SpeedFactor scales all travel times for this vehicle, in (0, 5].
	SpeedFactor float64 `json:"speed_factor,omitempty"`
	// MaxTasks caps how many tasks the vehicle may be assigned.
	MaxTasks int `json:"max_tasks,omitempty"`
	// MaxTravelTime caps total travel time in seconds.
	MaxTravelTime int `json:"max_travel_time,omitempty"`
	// MaxDistance caps total travel distance in meters.
	MaxDistance int `json:"max_distance,omitempty"`
	// Costs adjusts how the solver values this vehicle.
	Costs *VehicleCosts `json:"costs,omitempty"`
	// Steps is a custom route used to warm-start the search.
	Steps []VehicleStep `json:"steps,omitempty"`
	// Description is an opaque label echoed back in the solution.
	Description string `json:"description,omitempty"`
}

Vehicle is one member of the fleet.

type VehicleCosts added in v0.3.2

type VehicleCosts struct {
	// Fixed is the one-time cost of using this vehicle. Defaults to 0.
	Fixed int `json:"fixed,omitempty"`
	// PerHour is the cost of one hour of travel time. Defaults to 3600.
	PerHour int `json:"per_hour,omitempty"`
	// PerTaskHour is the cost of one hour of task time (setup + service).
	// Defaults to 0.
	PerTaskHour int `json:"per_task_hour,omitempty"`
	// PerKM is the cost of one kilometer of travel. Defaults to 0.
	PerKM int `json:"per_km,omitempty"`
}

VehicleCosts adjusts how the solver values one vehicle.

type VehicleRoute

type VehicleRoute struct {
	// Vehicle is the ID of the vehicle serving this route.
	Vehicle     int   `json:"vehicle"`
	Cost        int   `json:"cost"`
	Setup       int   `json:"setup"`
	Service     int   `json:"service"`
	Duration    int   `json:"duration"`
	WaitingTime int   `json:"waiting_time"`
	Priority    int   `json:"priority"`
	Distance    int   `json:"distance,omitempty"`
	Delivery    []int `json:"delivery,omitempty"`
	Pickup      []int `json:"pickup,omitempty"`
	// Description is the vehicle description echoed from the request.
	Description string `json:"description,omitempty"`
	// Geometry is an encoded polyline, present only when
	// [OptimizationOptions.Geometry] was set.
	Geometry string       `json:"geometry,omitempty"`
	Steps    []*RouteStep `json:"steps"`
	// Violations holds constraint violations for this route. It is
	// always empty in solving mode, the only mode the hosted API runs.
	Violations []Violation `json:"violations,omitempty"`
}

VehicleRoute is the itinerary assigned to one vehicle.

type VehicleStep added in v0.3.2

type VehicleStep struct {
	// Type is "job", "pickup" or "delivery".
	Type string `json:"type"`
	// ID is the task ID this step refers to.
	ID int `json:"id,omitempty"`
}

VehicleStep is one entry of a custom route on a Vehicle. The hosted API runs in solving mode, where Steps warm-start the search and only the "job", "pickup" and "delivery" step types are used; the plan-only service_at, service_after and service_before keys would be ignored by the engine, so the client does not expose them.

type Violation added in v0.3.2

type Violation struct {
	// Cause is the violated constraint, e.g. "delay", "skills" or
	// "missing_break".
	Cause string `json:"cause"`
	// Duration is the lateness in seconds for "delay" causes, earliness
	// for "lead_time".
	Duration int `json:"duration"`
}

Violation describes one constraint violation. Violations are always empty in solving mode, the only mode the hosted API runs, but the fields exist for API completeness.

type Waypoint

type Waypoint struct {
	// Name is the street the coordinate snapped to, if known.
	Name string `json:"name"`
	// Location is the snapped position.
	Location Point `json:"location"`
	// Distance is the metres between the input coordinate and Location.
	Distance float64 `json:"distance"`
	// Hint is an opaque token that can speed up subsequent requests.
	Hint string `json:"hint,omitempty"`
	// Nodes are the OSM node IDs of the snapped segment, when the engine
	// returns them.
	Nodes []int64 `json:"nodes,omitempty"`
	// TripsIndex identifies which entry of a trip response this waypoint
	// belongs to, when the engine returns it.
	TripsIndex int `json:"trips_index,omitempty"`
	// WaypointIndex is the position of this waypoint within its trip,
	// when the engine returns it.
	WaypointIndex int `json:"waypoint_index,omitempty"`
}

Waypoint is an input coordinate snapped to the road network.

Directories

Path Synopsis
examples
geocode command
Command geocode searches for an address and prints the matches.
Command geocode searches for an address and prints the matches.
matching command
Command matching snaps a noisy GPS trace onto the road network.
Command matching snaps a noisy GPS trace onto the road network.
matrix command
Command matrix computes travel times from a depot to several stops.
Command matrix computes travel times from a depot to several stops.
nearest command
Command nearest finds the road segment closest to a coordinate.
Command nearest finds the road segment closest to a coordinate.
optimization command
Command optimization assigns deliveries to a small fleet and prints the resulting routes.
Command optimization assigns deliveries to a small fleet and prints the resulting routes.
route command
Command route computes a route between two points and prints its distance.
Command route computes a route between two points and prints its distance.
trip command
Command trip finds the fastest order to visit a set of points.
Command trip finds the fastest order to visit a set of points.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL