Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utilities to convert GPS trace file formats into SnapToRoads request #44

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
113 changes: 113 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,86 @@ const request = {
};
```

### featureCollectionToRoadSnapTracePointList

Converts a GeoJSON FeatureCollection with Point Features to an array of RoadSnapTracePoint, so the result can be used to assemble the request to SnapToRoads API.
HabibaaElsherbiny marked this conversation as resolved.
Show resolved Hide resolved

```javascript
const featureCollection = { ... };
const request = {
Tracepoints: featureCollectionToRoadSnapTracePointList(featureCollection),
};
```

## CSV to Amazon Location Data Types

### csvStringToRoadSnapTracePointList

Converts a CSV string to an array of RoadSnapTracePoint, so the result can be used to assemble the request to SnapToRoads API.
The first line contains the attribute names, the subsequent lines the data in temporal order.
HabibaaElsherbiny marked this conversation as resolved.
Show resolved Hide resolved

```javascript
const csvString = "....";
const request = {
Tracepoints: csvStringToRoadSnapTracePointList(csvString),
};
```

## GPX to Amazon Location Data Types

### gpxToRoadSnapTracePointList

Converts a GPX string to an array of RoadSnapTracePoint, so the result can be used to assemble the request to SnapToRoads API.

```javascript
const gpxString = "....";
const request = {
Tracepoints: gpxToRoadSnapTracePointList(gpxString),
};
```

## KML to Amazon Location Data Types

### kmlStringToRoadSnapTracePointList

Converts a KML string to an array of RoadSnapTracePoint, so the result can be used to assemble the request to SnapToRoads API.

```javascript
const kmlString = "....";
const request = {
Tracepoints: kmlStringToRoadSnapTracePointList(kmlString),
};
```

## NMEA to Amazon Location Data Types

### nmeaStringToRoadSnapTracePointList

Converts a NMEA string containing $GPRMC and/or $GPGGA records to an array of RoadSnapTracePoint, so the result can be
used to assemble the request to SnapToRoads API.

```javascript
const nmeaString = "....";
const request = {
Tracepoints: nmeaStringToRoadSnapTracePointList(nmeaString),
};
```

## Flexible Polyline to Amazon Location Data Types

### flexiblePolylineStringToRoadSnapTracePointList

Converts a Flexible Polyline string to an array of RoadSnapTracePoint, so the result can be used to assemble the request
to SnapToRoads API.

```javascript
const flexiblePolylineString = "....";
const request = {
Tracepoints: flexiblePolylineStringToRoadSnapTracePointList(flexiblePolylineString),
};
__;
```

## Amazon Location Data Types to GeoJSON
HabibaaElsherbiny marked this conversation as resolved.
Show resolved Hide resolved

### devicePositionsToFeatureCollection
Expand Down Expand Up @@ -325,6 +405,39 @@ const response = { ... };
const featureCollection = snapToRoadsResponseToFeatureCollection(response)
```

## GeoJSON Utilities

### convertToPointFeatureCollection

Converts GeoJSON Point or LineString features to a FeatureCollection of Points. This utility function is particularly
useful when you need to convert complex geometries to individual points while preserving or transforming feature properties.

```typescript
const feature = {
type: "Feature",
geometry: {
type: "LineString",
coordinates: [
[-122.4194, 37.7749],
[-122.4201, 37.775],
],
},
properties: { timestamp: "2024-01-01T00:00:00Z" },
};
```

- Using default property handling (preserves existing properties or defaults to {})
```typescript
const pointCollection = convertToPointFeatureCollection(feature);
```
- Custom property transformation
```typescript
const pointCollectionWithCustomProps = convertToPointFeatureCollection(feature, (properties, index) => ({
...properties,
pointNumber: index,
}));
```

## Error Handling

If the data provided to the utility functions are invalid, the entries in the data will be skipped.
Expand Down
42 changes: 41 additions & 1 deletion package-lock.json

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

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@
"@aws-sdk/client-geo-routes": "^3.683.0",
"@aws-sdk/client-location": "^3.682.0",
"@aws/polyline": "^0.1.0",
"@tmcw/togeojson": "^6.0.1",
"@turf/circle": "^6.5.0",
"@types/geojson": "^7946.0.14"
"@types/geojson": "^7946.0.14",
"csv-parse": "^5.5.6",
"xmldom": "^0.6.0"
},
"devDependencies": {
"@babel/core": "^7.26.0",
Expand Down
1 change: 1 addition & 0 deletions src/from-csv/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./tracepoints-converter";
150 changes: 150 additions & 0 deletions src/from-csv/tracepoints-converter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { csvStringToRoadSnapTracePointList } from "./tracepoints-converter";

describe("csvToRoadSnapTracePointList", () => {
it("should convert csv string with headers to RoadSnapTracePointList (speed_kmh)", () => {
const csvString = `latitude,longitude,speed_kmh,timestamp,heading
53.3737131,-1.4704939,12.5,2024-11-15T10:30:00Z,45
53.3742428,-1.4677477,15.8,2024-11-15T10:31:30Z,78`;
expect(csvStringToRoadSnapTracePointList(csvString)).toEqual([
{
Position: [-1.4704939, 53.3737131],
Speed: 12.5,
Timestamp: "2024-11-15T10:30:00Z",
Heading: 45,
},
{
Position: [-1.4677477, 53.3742428],
Speed: 15.8,
Timestamp: "2024-11-15T10:31:30Z",
Heading: 78,
},
]);
});

it("should handle custom column mapping", () => {
const csvString = `y,x,velocity,time,direction
53.3737131,-1.4704939,12.5,2024-11-15T10:30:00Z,45
53.3742428,-1.4677477,15.8,2024-11-15T10:31:30Z,78`;

const result = csvStringToRoadSnapTracePointList(csvString, {
columnMapping: {
latitude: "y",
longitude: "x",
speed_kmh: "velocity",
timestamp: "time",
heading: "direction",
},
});

expect(result).toEqual([
{
Position: [-1.4704939, 53.3737131],
Speed: 12.5,
Timestamp: "2024-11-15T10:30:00Z",
Heading: 45,
},
{
Position: [-1.4677477, 53.3742428],
Speed: 15.8,
Timestamp: "2024-11-15T10:31:30Z",
Heading: 78,
},
]);
});

it("should handle speed in m/s", () => {
const csvString = `latitude,longitude,speed_mps,timestamp,heading
53.3737131,-1.4704939,3.47222,2024-11-15T10:30:00Z,45
53.3742428,-1.4677477,4.38889,2024-11-15T10:31:30Z,78`;

const result = csvStringToRoadSnapTracePointList(csvString);

expect(result).toEqual([
{
Position: [-1.4704939, 53.3737131],
Speed: 12.499992,
Timestamp: "2024-11-15T10:30:00Z",
Heading: 45,
},
{
Position: [-1.4677477, 53.3742428],
Speed: 15.800004,
Timestamp: "2024-11-15T10:31:30Z",
Heading: 78,
},
]);
});

it("should handle speed in mph", () => {
const csvString = `latitude,longitude,speed_mph,timestamp,heading
53.3737131,-1.4704939,7.76713,2024-11-15T10:30:00Z,45
53.3742428,-1.4677477,9.81747,2024-11-15T10:31:30Z,78`;

const result = csvStringToRoadSnapTracePointList(csvString);

expect(result).toEqual([
{
Position: [-1.4704939, 53.3737131],
Speed: 12.4999529942,
Timestamp: "2024-11-15T10:30:00Z",
Heading: 45,
},
{
Position: [-1.4677477, 53.3742428],
Speed: 15.7996471698,
Timestamp: "2024-11-15T10:31:30Z",
Heading: 78,
},
]);
});
it("should handle CSV without headers using columnNames", () => {
const csvString =
"37.774930,-122.419424,18.29,2024-11-19T14:45:00Z\n" + "37.775032,-122.420157,22.61,2024-11-19T14:46:30Z";

const result = csvStringToRoadSnapTracePointList(csvString, {
hasHeaders: false,
columnNames: ["latitude", "longitude", "speed_kmh", "timestamp"],
HabibaaElsherbiny marked this conversation as resolved.
Show resolved Hide resolved
});

expect(result).toEqual([
{
Position: [-122.419424, 37.77493],
Speed: 18.29,
Timestamp: "2024-11-19T14:45:00Z",
},
{
Position: [-122.420157, 37.775032],
Speed: 22.61,
Timestamp: "2024-11-19T14:46:30Z",
},
]);
});
it("should handle CSV without headers using columnNames and columnMapping", () => {
// NOTE: This test demonstrates the API's capability but doesn't represent a practical use case.
// In real usage, when providing column names for a headerless CSV (using columnNames),
// you should use the expected names directly (latitude, longitude, etc.) rather than
// using different names and mapping them.
const csvString = "37.774930,-122.419424,18.29\n" + "37.775032,-122.420157,22.61";

const result = csvStringToRoadSnapTracePointList(csvString, {
hasHeaders: false,
columnNames: ["y", "x", "speed"],
columnMapping: {
latitude: "y",
longitude: "x",
speed_kmh: "speed",
},
});
HabibaaElsherbiny marked this conversation as resolved.
Show resolved Hide resolved

expect(result).toEqual([
{
Position: [-122.419424, 37.77493],
Speed: 18.29,
},
{
Position: [-122.420157, 37.775032],
Speed: 22.61,
},
]);
});
});
Loading