Skip to content

Commit

Permalink
Added tile-clusterer mode
Browse files Browse the repository at this point in the history
  • Loading branch information
xdan committed Sep 15, 2023
1 parent bb2ab08 commit d464d03
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
39 changes: 39 additions & 0 deletions docs/v1/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,45 @@ paths:
400:
$ref: '#/components/responses/BadRequest'

/v1/tile-clusterer:
get:
summary: |
Returns geo features whose coordinates are within a rectangle that is uniquely defined by its x,y and z tile coordinates
But reduce all points to one point with count of points in cluster
parameters:
- in: query
name: x
schema:
type: integer
required: true
example: 10

- in: query
name: y
schema:
type: integer
required: true
example: 20

- in: query
name: z
schema:
type: integer
required: true
example: 5

responses:
200:
description: Features list.
content:
application/json:
schema:
$ref: '#/components/schemas/FeaturesWithBounds'

400:
$ref: '#/components/responses/BadRequest'

/version:
get:
description: Application Version
Expand Down
2 changes: 2 additions & 0 deletions src/app/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import {Router} from 'express';
import {asyncMiddleware} from '../lib/async-middlware';
import {loadByTile} from './load-by-tile';
import {loadByBBox} from './load-by-bbox';
import {loadByTileClusterer} from './load-by-tile-cluster';
import {apiDocs} from '../middleware/api-docs';

export const router = Router({mergeParams: true})
.use('/api_docs', apiDocs)
.get('/tile', asyncMiddleware(loadByTile))
.get('/tile-clusterer', asyncMiddleware(loadByTileClusterer))
.get('/bbox', asyncMiddleware(loadByBBox));
59 changes: 59 additions & 0 deletions src/app/v1/load-by-tile-cluster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {Request, Response} from 'express';
import {z} from 'zod';
import * as Boom from '@hapi/boom';
import {formatZodError, numericString} from '../lib/zod';
import {Bounds} from '../lib/geo';
import {fromWorldCoordinates, tileToWorld} from '../lib/projection/projection';

const getTileRequestSchema = z
.object({
limit: numericString(z.number().int().min(100).max(10000).default(1000)),
x: numericString(z.number().int()),
y: numericString(z.number().int()),
z: numericString(z.number().int())
})
.strict();

export async function loadByTileClusterer(req: Request, res: Response): Promise<void> {
const validationResult = getTileRequestSchema.safeParse(req.query);
if (!validationResult.success) {
throw Boom.badRequest(formatZodError(validationResult.error));
}

const {x: tx, y: ty, z: tz, limit} = validationResult.data;

const coordinates: Bounds = tileToWorld(tx, ty, tz).map(fromWorldCoordinates) as Bounds;
const result = await req.dataProvider.getFeaturesByBBox(coordinates, limit);
const {leftBottom, rightTop} = result.features.reduce(
(mm, point) => {
const [lng, lat] = point.geometry.coordinates;
return {
leftBottom: [Math.min(mm.leftBottom[0], lng), Math.max(mm.leftBottom[1], lat)],
rightTop: [Math.max(mm.rightTop[0], lng), Math.min(mm.rightTop[1], lat)]
};
},
{leftBottom: [Infinity, -Infinity], rightTop: [-Infinity, Infinity]}
);

res.send({
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
(leftBottom[0] + rightTop[0]) / 2,
(leftBottom[1] + rightTop[1]) / 2
]
},
properties: {
count: result.total
}
}
],

total: result.total,
minMax: [leftBottom, rightTop],
bounds: coordinates
});
}
25 changes: 25 additions & 0 deletions src/tests/v1/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,30 @@ describe('/v1', () => {
});
});
});

describe('Check tile clusterer', () => {
it('should return 1 point inside tile', async () => {
const res = await testServer.request('/v1/tile-clusterer?x=10&y=11&z=5', {
json: true
});
expect(res.statusCode).toEqual(200);

const result = res.body as {features: Feature<Point>[]; bounds: Bounds; minMax: Bounds};
expect(result.features.length).toEqual(1);
expect(result.bounds).toEqual([
[-67.5, 48.92249926375823],
[-56.25, 40.97989806962013]
]);

expect(result.minMax).toEqual([
[-67.30010370199994, 47.960976981000044],
[-56.3153175459999, 43.45754888200008]
]);

expect(result.features[0].geometry.coordinates).toEqual([
-61.807710623999924, 45.709262931500064
]);
});
});
});
});

0 comments on commit d464d03

Please sign in to comment.