Skip to content

Commit

Permalink
feat(tiler): basic testing setup, and first attempt at attributes map…
Browse files Browse the repository at this point in the history
…ping
  • Loading branch information
bartolkaruza committed Oct 21, 2019
1 parent 6af3ea5 commit 32455d7
Show file tree
Hide file tree
Showing 9 changed files with 3,859 additions and 66 deletions.
30 changes: 24 additions & 6 deletions example/exampleServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,41 @@ supertiler({
maxZoomLevel,
geometry,
table,
resolution: 512, // Mapbox default, try 256 if you are unsure what your mapping front-end library uses
attributeMap: { status: 'status' },
// filterQuery: `
// WHERE [22, 55, 100] @> {$power} AND
// power_available: []
// type_available: []
// `,
// additionalProperties: ['status', 'speed']
}).then(server => {
// availableOnly=
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
next();
});
app.get('/stations/:z/:x/:y/tile.mvt', (req, res) => {
req.id = uuid.v4();
console.time(req.id);
server({ z: req.params.z, x: req.params.x, y: req.params.y, id: req.id }).then(
result => {
server({
z: req.params.z,
x: req.params.x,
y: req.params.y,
id: req.id,
})
.then(result => {
res.setHeader('Content-Type', 'application/x-protobuf');
res.setHeader('Content-Encoding', 'gzip');
console.time('send' + req.id);
res.status(200).send(result);
console.timeEnd('send' + req.id);
console.timeEnd(req.id);
}
).catch(e => {
res.status(400).send('Oops');
});
})
.catch(e => {
res.status(400).send('Oops');
});
});
app.listen(port, () =>
console.log(`Example app listening on port ${port}!`)
Expand Down
105 changes: 105 additions & 0 deletions js/__test__/__snapshots__/createClusterQuery.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`createClusterQuery should create an unclustered Query 1`] = `
"
with filtered AS
(SELECT public.stations.wkb_geometry
FROM public.stations
WHERE ST_Intersects(TileDoubleBBox(1, 0, 1, 3857), ST_Transform(public.stations.wkb_geometry, 3857))
),
clustered as
(SELECT unnest(ST_ClusterWithin(wkb_geometry, 0.006)) as clusters
FROM filtered),
clusters_with_meta_10 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered),
clustered_9 as
(SELECT unnest(ST_ClusterWithin(center, 0.12345679012345678)) as clusters
FROM clusters_with_meta_10),
clusters_with_meta_9 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_9),
clustered_8 as
(SELECT unnest(ST_ClusterWithin(center, 0.15625)) as clusters
FROM clusters_with_meta_9),
clusters_with_meta_8 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_8),
clustered_7 as
(SELECT unnest(ST_ClusterWithin(center, 0.20408163265306123)) as clusters
FROM clusters_with_meta_8),
clusters_with_meta_7 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_7),
clustered_6 as
(SELECT unnest(ST_ClusterWithin(center, 0.2777777777777778)) as clusters
FROM clusters_with_meta_7),
clusters_with_meta_6 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_6),
clustered_5 as
(SELECT unnest(ST_ClusterWithin(center, 0.4)) as clusters
FROM clusters_with_meta_6),
clusters_with_meta_5 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_5),
clustered_4 as
(SELECT unnest(ST_ClusterWithin(center, 0.625)) as clusters
FROM clusters_with_meta_5),
clusters_with_meta_4 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_4),
clustered_3 as
(SELECT unnest(ST_ClusterWithin(center, 1.1111111111111112)) as clusters
FROM clusters_with_meta_4),
clusters_with_meta_3 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_3),
clustered_2 as
(SELECT unnest(ST_ClusterWithin(center, 2.5)) as clusters
FROM clusters_with_meta_3),
clusters_with_meta_2 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_2),
clustered_1 as
(SELECT unnest(ST_ClusterWithin(center, 10)) as clusters
FROM clusters_with_meta_2),
clusters_with_meta_1 as
(SELECT ST_Centroid(ST_CollectionExtract(clusters, 1)) as center,
ST_NumGeometries(clusters) as theCount
FROM clustered_1),
tiled as
(SELECT center,
theCount
FROM clusters_with_meta_1
WHERE ST_Intersects(TileBBox(1, 0, 1, 3857), ST_Transform(center, 3857))),
q as
(SELECT 1 as c1,
ST_AsMVTGeom(ST_Transform(center, 3857), TileBBox(1, 0, 1, 3857), 512, 10, false) AS geom,
jsonb_build_object('count', theCount,a,b) as attributes
FROM tiled)
SELECT ST_AsMVT(q, 'stations', 512, 'geom') as mvt
from q
"
`;

exports[`createClusterQuery should map attributes to features select array 1`] = `",a,b,c,d,e,f"`;
35 changes: 35 additions & 0 deletions js/__test__/createClusterQuery.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const {
createQueryForTile,
attributeMapToFeatureAttribute,
} = require('../createClusterQuery');

describe('createClusterQuery', () => {
it('should create an unclustered Query', () => {
expect(
createQueryForTile({
z: 1,
x: 0,
y: 1,
table: 'public.stations',
geometry: 'wkb_geometry',
maxZoomLevel: 10,
resolution: 512,
attributeMap: { a: 'b' },
})
).toMatchSnapshot();
});

it('should map attributes to features select array', () => {
expect(attributeMapToFeatureAttribute({
a: 'b',
c: 'd',
e: 'f',
})).toMatchSnapshot();
});
});

// http://localhost:3005/stations/13/4400/2687/tile.mvt
// http://localhost:3005/stations/14/8609/5642/tile.mvt
// http://localhost:3005/stations/1/0/1/tile.mvt

// 9/284/168/tile.mvt
105 changes: 93 additions & 12 deletions js/createClusterQuery.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const filterBlock = ({ x, y, z, maxZoomLevel, table, geometry }) =>
` (SELECT ${table}.${geometry}
const filterBlock = ({ x, y, z, maxZoomLevel, table, geometry, attributes }) =>
`
(SELECT ${table}.${geometry}
FROM ${table}
WHERE ST_Intersects(TileDoubleBBox(${z}, ${x}, ${y}, 3857), ST_Transform(${table}.${geometry}, 3857))
),
Expand All @@ -11,7 +12,41 @@ const filterBlock = ({ x, y, z, maxZoomLevel, table, geometry }) =>
ST_NumGeometries(clusters) as theCount
FROM clustered),`;

const clustered_query = ({ filterBlock, additionalLevels, z, x, y }) => `
const unclusteredQuery = ({
x,
y,
z,
table,
geometry,
resolution,
attributes,
}) =>
`
WITH filtered AS
(SELECT ${table}.${geometry}${attributeMapToSelect(attributes)}
FROM ${table}
WHERE ST_Intersects(TileBBox(${z}, ${x}, ${y}, 3857), ST_Transform(${table}.${geometry}, 3857))
),
q as
(SELECT 1 as c1,
${geometry} AS geom,
jsonb_build_object('count', 1${attributeMapToFeatureAttribute(
attributes
)}) as attributes
FROM filtered)
SELECT ST_AsMVT(q, 'stations', ${resolution}, 'geom') as mvt
from q
`;

const base_query = ({
filterBlock,
additionalLevels,
z,
x,
y,
resolution,
attributes,
}) => `
with filtered AS
${filterBlock}
${additionalLevels}
Expand All @@ -22,14 +57,14 @@ with filtered AS
WHERE ST_Intersects(TileBBox(${z}, ${x}, ${y}, 3857), ST_Transform(center, 3857))),
q as
(SELECT 1 as c1,
ST_AsMVTGeom(ST_Transform(center, 3857), TileBBox(${z}, ${x}, ${y}, 3857), 256, 10, false) AS geom,
theCount
ST_AsMVTGeom(ST_Transform(center, 3857), TileBBox(${z}, ${x}, ${y}, 3857), ${resolution}, 10, false) AS geom,
jsonb_build_object('count', theCount) as attributes
FROM tiled)
SELECT ST_AsMVT(q, 'stations', 256, 'geom') as mvt
SELECT ST_AsMVT(q, 'stations', ${resolution}, 'geom') as mvt
from q
`;

const additionalLevel = zoomLevel => `
const additionalLevel = ({ zoomLevel, attributes }) => `
clustered_${zoomLevel} as
(SELECT unnest(ST_ClusterWithin(center, ${zoomToDistance(
zoomLevel
Expand All @@ -42,27 +77,73 @@ const additionalLevel = zoomLevel => `
`;

const zoomToDistance = zoomLevel => 10 / (zoomLevel * zoomLevel);
const attributeMapToFeatureAttribute = attributeMap =>
`${
Object.keys(attributeMap).length > 0
? Object.entries(attributeMap)
.map(([key, value]) => `,${key},${value}`)
.join('')
: ''
}`;
const attributeMapToSelect = attributes =>
Object.entries(attributes)
.map(([_, value]) => `,${value}`)
.join('');

function createQueryForTile({ z, x, y, maxZoomLevel, table, geometry }) {
function createQueryForTile({
z,
x,
y,
maxZoomLevel,
table,
geometry,
resolution,
attributeMap,
}) {
if (z < maxZoomLevel) {
let additionalLevels = '';
for (let i = maxZoomLevel - 1; i >= z; --i) {
additionalLevels += additionalLevel(i);
additionalLevels += additionalLevel({
zoomLevel: i,
attributes: attributeMap,
});
}
const ret = clustered_query({
filterBlock: filterBlock({ z, x, y, maxZoomLevel, table, geometry }),
const ret = base_query({
filterBlock: filterBlock({
z,
x,
y,
maxZoomLevel,
table,
geometry,
resolution,
attributes: attributeMap,
}),
z,
x,
y,
additionalLevels,
resolution,
attributes: attributeMap,
});

return ret;
} else {
// TODO Implement unclustered query
const ret = unclusteredQuery({
x,
y,
z,
table,
geometry,
resolution,
attributes: attributeMap,
});
return ret;
}
}

module.exports = {
createQueryForTile,
zoomToDistance,
attributeMapToFeatureAttribute,
};
Loading

0 comments on commit 32455d7

Please sign in to comment.