Skip to content

Commit

Permalink
add proto distance endpoint (#7)
Browse files Browse the repository at this point in the history
* add proto distance endpoint

* swap wrongly passed credentials

* remove unnecessary headers from proto client

* add time filter fast proto distances readme section

* release package to npm

Co-authored-by: MockusT <[email protected]>
  • Loading branch information
EivydasKoc and MockusT authored Oct 5, 2022
1 parent 6d827e3 commit aa166a1
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 46 deletions.
69 changes: 61 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,19 @@ travelTimeClient.timeFilterFast({
.catch((e) => console.error(e));
```

### [Time Filter Fast (Proto)](https://traveltime.com/docs/api/reference/supported-locations)
Filter out points that cannot be reached within specified time limit.
### Time Filter Fast (Proto)
A fast version of time filter communicating using [protocol buffers](https://github.com/protocolbuffers/protobuf).

Request Properties:
* departureLocation: Original point.
* destinationCoordinates: destination points. Cannot be more than 200,000.
* transportation: transportation type.
* travelTime: time limit;
* country: return the results that are within the specified country
The request parameters are much more limited and only travel time is returned. In addition, the results are only approximately correct (95% of the results are guaranteed to be within 5% of the routes returned by regular time filter).

This inflexibility comes with a benefit of faster response times (Over 5x faster compared to regular time filter) and larger limits on the amount of destination points.

Body attributes:
* departureLocation: Origin point.
* destinationCoordinates: Destination points. Cannot be more than 200,000.
* transportation: Transportation type.
* travelTime: Time limit;
* country: Return the results that are within the specified country

```ts
import { TravelTimeProtoClient, TimeFilterFastProtoRequest } from 'traveltime-api';
Expand Down Expand Up @@ -304,6 +308,55 @@ travelTimeProtoClient.timeFilterFast(requestData)
.catch((e) => console.error(e));
```

The responses are in the form of a list where each position denotes:
* travel time (in seconds) of a journey, or if negative that the journey from the origin to the destination point is impossible.

### Time Filter Fast with distance (Proto)

A fast version of time filter communicating using [protocol buffers](https://github.com/protocolbuffers/protobuf) that supports distance information.

The request parameters are much more limited and only travel time and distance is returned. In addition, the results are only approximately correct (95% of the results are guaranteed to be within 5% of the routes returned by regular time filter).

Body attributes:
* departureLocation: Origin point.
* destinationCoordinates: Destination points. Cannot be more than 200,000.
* transportation: Transportation type.
* travelTime: Time limit;
* country: Return the results that are within the specified country

```ts
import {
TravelTimeProtoClient, TimeFilterFastProtoDistanceRequest,
} from 'traveltime-api';

const travelTimeProtoClient = new TravelTimeProtoClient({
apiKey: 'YOUR_API_KEY',
applicationId: 'YOUR_APPLICATION_ID',
});

const requestData: TimeFilterFastProtoDistanceRequest = {
country: 'uk',
departureLocation: {
lat: 51.508930,
lng: -0.131387,
},
destinationCoordinates: [{
lat: 51.508824,
lng: -0.167093,
}],
transportation: 'driving+ferry',
travelTime: 7200,
};

travelTimeProtoClient.timeFilterFastDistance(requestData)
.then((data) => console.log(data))
.catch((e) => console.error(e));
```

The responses are in the form of lists where each position denotes:
* travel time (in seconds) of a journey, or if negative that the journey from the origin to the destination point is impossible.
* if a travel time for a position is non negative than the distance list at the same position denotes travel distance in meters for that journey, if the journey is impossible the distance value at the position is *undefined*.

### [Time Filter (Postcode Districts)](https://traveltime.com/docs/api/reference/postcode-district-filter)
Find districts that have a certain coverage from origin (or to destination) and get statistics about postcodes within such districts.
Currently only supports United Kingdom.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "traveltime-api",
"version": "2.3.0",
"version": "2.4.0",
"description": "TravelTime API SDK for node js with TypeScript",
"main": "target/index.js",
"types": "target/index.d.ts",
Expand Down
82 changes: 52 additions & 30 deletions src/client/protoClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable class-methods-use-this */
import axios, { AxiosInstance } from 'axios';
import protobuf from 'protobufjs';
import { Coords } from '../types';
import { TimeFilterFastProtoRequest, TimeFilterFastProtoResponse, TimeFilterFastProtoTransportation } from '../types/proto';
import {
TimeFilterFastProtoDistanceRequest, TimeFilterFastProtoRequest, TimeFilterFastProtoResponse, TimeFilterFastProtoTransportation,
} from '../types/proto';

interface TimeFilterFastProtoMessage {
oneToManyRequest: {
Expand All @@ -16,6 +19,10 @@ interface TimeFilterFastProtoMessage {
}
}

interface ProtoRequestBuildOptions {
useDistance?: boolean
}

export class TravelTimeProtoClient {
private apiKey: string;

Expand All @@ -25,6 +32,8 @@ export class TravelTimeProtoClient {

private baseUri = 'http://proto.api.traveltimeapp.com/api/v2';

private protoDistanceUri = 'https://proto-with-distance.api.traveltimeapp.com/api/v2';

private protoFileDir = `${__dirname}/proto/v2`;

private transportationMap: Record<TimeFilterFastProtoTransportation, number> = {
Expand All @@ -42,26 +51,23 @@ export class TravelTimeProtoClient {
this.apiKey = credentials.apiKey;
this.axiosInstance = axios.create({
auth: {
username: this.apiKey,
password: this.applicationId,
username: this.applicationId,
password: this.apiKey,
},
headers: {
'Content-Type': 'application/octet-stream',
'X-Application-Id': this.applicationId,
'X-Api-Key': this.apiKey,
Accept: 'application/octet-stream',
},
responseType: 'arraybuffer',
});
}

// eslint-disable-next-line class-methods-use-this
private encodeFixedPoint(sourcePoint: number, targetPoint: number) {
return Math.round((targetPoint - sourcePoint) * 1000000);
}

private buildRequestUrl({ country, transportation }: TimeFilterFastProtoRequest): string {
return `${this.baseUri}/${country}/time-filter/fast/${transportation}`;
private buildRequestUrl(uri: string, { country, transportation }: TimeFilterFastProtoRequest): string {
return `${uri}/${country}/time-filter/fast/${transportation}`;
}

private buildDeltas(departure: Coords, destinations: Array<Coords>) {
Expand All @@ -76,7 +82,7 @@ export class TravelTimeProtoClient {
destinationCoordinates,
transportation,
travelTime,
}: TimeFilterFastProtoRequest): TimeFilterFastProtoMessage {
}: TimeFilterFastProtoRequest, options?: ProtoRequestBuildOptions): TimeFilterFastProtoMessage {
if (!(transportation in this.transportationMap)) {
throw new Error('Transportation type is not supported');
}
Expand All @@ -90,30 +96,46 @@ export class TravelTimeProtoClient {
},
arrivalTimePeriod: 0,
travelTime,
properties: options?.useDistance ? [1] : undefined,
},
};
}

timeFilterFast = async (request: TimeFilterFastProtoRequest) => protobuf.load([
`${this.protoFileDir}/TimeFilterFastRequest.proto`,
`${this.protoFileDir}/TimeFilterFastResponse.proto`,
])
.then(async (root) => {
const TimeFilterFastRequest = root.lookupType('com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest');
const TimeFilterFastResponse = root.lookupType('com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse');
const messageRequest = this.buildProtoRequest(request);
const message = TimeFilterFastRequest.create(messageRequest);
const buffer = TimeFilterFastRequest.encode(message).finish();

try {
const { data } = await this.axiosInstance.post(this.buildRequestUrl(request), buffer);
const response = TimeFilterFastResponse.decode(data);
return response.toJSON() as TimeFilterFastProtoResponse;
} catch (e) {
throw new Error('Error while sending proto request');
}
})
.catch(() => {
private async readProtoFile() {
try {
return await protobuf.load([
`${this.protoFileDir}/TimeFilterFastRequest.proto`,
`${this.protoFileDir}/TimeFilterFastResponse.proto`,
]);
} catch {
throw new Error(`Could not load proto file at: ${this.protoFileDir}`);
});
}
}

private async handleProtoFile(
root: protobuf.Root,
uri: string,
request: TimeFilterFastProtoRequest | TimeFilterFastProtoDistanceRequest,
options?: ProtoRequestBuildOptions,
) {
const TimeFilterFastRequest = root.lookupType('com.igeolise.traveltime.rabbitmq.requests.TimeFilterFastRequest');
const TimeFilterFastResponse = root.lookupType('com.igeolise.traveltime.rabbitmq.responses.TimeFilterFastResponse');
const messageRequest = this.buildProtoRequest(request, options);
const message = TimeFilterFastRequest.create(messageRequest);
const buffer = TimeFilterFastRequest.encode(message).finish();

try {
const { data } = await this.axiosInstance.post(this.buildRequestUrl(uri, request), buffer);
const response = TimeFilterFastResponse.decode(data);
return response.toJSON() as TimeFilterFastProtoResponse;
} catch (e) {
throw new Error('Error while sending proto request');
}
}

timeFilterFast = async (request: TimeFilterFastProtoRequest) => this.readProtoFile()
.then(async (root) => this.handleProtoFile(root, this.baseUri, request));

timeFilterFastDistance = async (request: TimeFilterFastProtoDistanceRequest) => this.readProtoFile()
.then(async (root) => this.handleProtoFile(root, this.protoDistanceUri, request, { useDistance: true }));
}
27 changes: 22 additions & 5 deletions src/types/proto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Coords } from './common';

export type TimeFilterFastProtoTransportation = 'pt' | 'walking+ferry' | 'cycling+ferry' | 'driving+ferry';
export type TimeFilterFastProtoCountry = 'nl' | 'at' | 'be' | 'de' | 'fr' | 'ie' | 'lt' | 'uk'
export type TimeFilterFastProtoDistanceTransportation = 'driving+ferry' | 'walking+ferry'
export type TimeFilterFastProtoDistanceCountry = 'uk' | 'ie'
export type TimeFilterFastProtoTransportation = 'pt' | 'cycling+ferry' | TimeFilterFastProtoDistanceTransportation;
export type TimeFilterFastProtoCountry = 'nl' | 'at' | 'be' | 'de' | 'fr' | 'lt' | TimeFilterFastProtoDistanceCountry

export interface TimeFilterFastProtoProperties {
fares?: boolean,
Expand All @@ -16,16 +18,31 @@ export interface TimeFilterFastProtoRequest {
travelTime: number,
}

interface TimeFilterFastProtoResponseProperties {
export interface TimeFilterFastProtoDistanceRequest {
country: TimeFilterFastProtoDistanceCountry
departureLocation: Coords,
destinationCoordinates: Array<Coords>,
transportation: TimeFilterFastProtoDistanceTransportation,
travelTime: number,
}

export interface TimeFilterFastProtoResponseProperties {
properties: {
travelTimes: Array<number>
}
}

interface TimeFilterFastProtoResponseError {
export interface TimeFilterFastProtoDistanceResponseProperties {
properties: {
travelTimes: Array<number>,
distances: Array<number>
}
}

export interface TimeFilterFastProtoResponseError {
error: {
type: string
}
}

export type TimeFilterFastProtoResponse = TimeFilterFastProtoResponseProperties | TimeFilterFastProtoResponseError
export type TimeFilterFastProtoResponse = TimeFilterFastProtoResponseProperties | TimeFilterFastProtoDistanceResponseProperties | TimeFilterFastProtoResponseError

0 comments on commit aa166a1

Please sign in to comment.