Initial overpass api connections
This commit is contained in:
parent
246debe642
commit
3dd43deec0
36
apps/blakus-api/src/app/routes/blakus.ts
Normal file
36
apps/blakus-api/src/app/routes/blakus.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { FastifyInstance } from 'fastify';
|
||||||
|
import { OverpassService } from '../services/overpass';
|
||||||
|
import { OverpassQuerySchema, OverpassQueryType } from '../schemas/overpass';
|
||||||
|
|
||||||
|
const overpassService = new OverpassService();
|
||||||
|
|
||||||
|
export default async function (fastify: FastifyInstance) {
|
||||||
|
|
||||||
|
fastify.get<{ Querystring: OverpassQueryType }>('/blakus', {
|
||||||
|
schema: {
|
||||||
|
querystring: OverpassQuerySchema,
|
||||||
|
response: {
|
||||||
|
200: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
title: { type: 'string' },
|
||||||
|
amenity: { type: 'string' },
|
||||||
|
tags: { type: 'object', additionalProperties: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async function ({query}) {
|
||||||
|
const elements = await overpassService.getAmenitiesAroundLocation({
|
||||||
|
latlon: {
|
||||||
|
lat: query.lat,
|
||||||
|
lon: query.lon,
|
||||||
|
},
|
||||||
|
amenities: ['restaurant', 'cafe', 'bar', 'pub', 'biergarten', 'fast_food', 'food_court', 'ice_cream']
|
||||||
|
});
|
||||||
|
return elements;
|
||||||
|
});
|
||||||
|
}
|
||||||
10
apps/blakus-api/src/app/schemas/overpass.ts
Normal file
10
apps/blakus-api/src/app/schemas/overpass.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Static, Type } from '@sinclair/typebox'
|
||||||
|
|
||||||
|
export const OverpassQuerySchema = Type.Object({
|
||||||
|
lat: Type.Number(),
|
||||||
|
lon: Type.Number(),
|
||||||
|
radius: Type.Optional(Type.Number()),
|
||||||
|
amenities: Type.Optional(Type.Array(Type.String())),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type OverpassQueryType = Static<typeof OverpassQuerySchema>;
|
||||||
89
apps/blakus-api/src/app/services/overpass.ts
Normal file
89
apps/blakus-api/src/app/services/overpass.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
type OverpassResult = {
|
||||||
|
version: number;
|
||||||
|
generator: string;
|
||||||
|
osm3s: Record<string, string>;
|
||||||
|
elements: (NodeElement | WayElement | RelationElement)[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type OverpassLocation = {
|
||||||
|
lat: number,
|
||||||
|
lon: number
|
||||||
|
}
|
||||||
|
|
||||||
|
type OverpassElement = {
|
||||||
|
type: string;
|
||||||
|
id: number;
|
||||||
|
tags?: { [key: string]: string }
|
||||||
|
};
|
||||||
|
|
||||||
|
type NodeElement = OverpassElement & OverpassLocation & {
|
||||||
|
type: 'node';
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type WayElement = OverpassElement & {
|
||||||
|
type: 'way';
|
||||||
|
nodes: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type RelationElement = OverpassElement & {
|
||||||
|
type: 'relation';
|
||||||
|
members: {
|
||||||
|
type: 'node' | 'way' | 'relation';
|
||||||
|
ref: number;
|
||||||
|
role: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function opq(strings, ...expressions) {
|
||||||
|
let query = '[out:json];\n';
|
||||||
|
|
||||||
|
for (let i = 0; i < strings.length; i++) {
|
||||||
|
query += strings[i];
|
||||||
|
if (i < expressions.length) query += expressions[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
query += '\nout body;\n>;\nout skel qt;';
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OverpassService {
|
||||||
|
private axios: AxiosInstance;
|
||||||
|
|
||||||
|
constructor(baseURL = 'https://overpass-api.de/api/') {
|
||||||
|
this.axios = axios.create({ baseURL });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAmenitiesAroundLocation({radius = 500, amenities = [], latlon}: {radius?: number, latlon: OverpassLocation, amenities: string[]}) {
|
||||||
|
|
||||||
|
if (!latlon || !latlon.lat || !latlon.lon) throw new Error('Invalid location');
|
||||||
|
|
||||||
|
const { lat, lon } = latlon;
|
||||||
|
|
||||||
|
const query = opq`node(around:${radius}, ${lat}, ${lon})["amenity"~"${amenities.join('|')}"];`;
|
||||||
|
|
||||||
|
const result = await this.overpassQuery(query);
|
||||||
|
|
||||||
|
return result.elements
|
||||||
|
.map(({ tags }) => ({ title: tags.name, amenity: tags.amenity, tags}))
|
||||||
|
.filter(i => amenities.includes(i.amenity)); // query returns some random results. need to look into this
|
||||||
|
}
|
||||||
|
|
||||||
|
async overpassQuery(query: string): Promise<OverpassResult> {
|
||||||
|
return this.getData('interpreter', { data: query });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData<T>(endpoint: string, params: Record<string, string>): Promise<T> {
|
||||||
|
try {
|
||||||
|
const response = await this.axios.get(endpoint, { params });
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching data from ${endpoint}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user