Skip to content

Commit

Permalink
Merge pull request #18 from doneill/jdo-35-optimizations
Browse files Browse the repository at this point in the history
Enhance GeoJSON generation
  • Loading branch information
doneill authored Sep 9, 2024
2 parents 22f6f98 + f26a6ae commit df4dc3d
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 139 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ $ koop serve
Replace `${schema}.${table}` with **schema.table** from your PostGIS db to translate.

```bash
curl localhost:8080/pg/${schema}.${table}/rest/services/FeatureServer/0/query
curl localhost:8080/pg/rest/services/${schema}.${table}/FeatureServer/0/query
```

**Add as Feature Layer**
Expand Down
3 changes: 1 addition & 2 deletions src/db/repo/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DataRepository {
offset: offset
});

return result;
return result.jsonb_build_object;
} catch (error) {
console.error('Error in createGeoJson:', error);
throw error;
Expand All @@ -40,4 +40,3 @@ class DataRepository {
}

module.exports = DataRepository;

35 changes: 26 additions & 9 deletions src/db/sql/createGeoJson.sql
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
SELECT jsonb_build_object(
'type', 'FeatureCollection',
'features', jsonb_agg(features.feature)
) FROM (
WITH ordered_ids AS (
SELECT ${id:raw}
FROM ${table:raw}
ORDER BY ${id:raw}
LIMIT ${limit:raw} OFFSET ${offset:raw}
),
feature_data AS (
SELECT
${id:raw},
ST_AsGeoJSON(ST_Transform(${geom:raw}, ${srid}))::jsonb AS geometry,
to_jsonb(t) - ${geom} AS properties
FROM ${table:raw} t
INNER JOIN ordered_ids USING (${id:raw})
),
features AS (
SELECT jsonb_build_object(
'type', 'Feature',
'gid', ${id},
'geometry', ST_AsGeoJSON(ST_Transform(${geom:raw},${srid}))::jsonb,
'properties', to_jsonb(inputs) - ${geom}
'type', 'Feature',
'gid', ${id:raw},
'geometry', geometry,
'properties', properties
) AS feature
FROM (SELECT * FROM ${table:raw} WHERE ${id:raw} IN (SELECT ${id:raw} FROM ${table:raw} ORDER BY ${id:raw} LIMIT ${limit:raw} OFFSET ${offset:raw})) inputs) features;
FROM feature_data
)
SELECT jsonb_build_object(
'type', 'FeatureCollection',
'features', COALESCE(jsonb_agg(feature), '[]'::jsonb)
) AS jsonb_build_object
FROM features;
44 changes: 32 additions & 12 deletions src/model/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,55 @@ const { db } = require('../db')

class Model {
constructor() { }

async getData(req, callback) {
try {
const splitPath = req.params.id.split('.');
const schema = splitPath[0];
const table = splitPath[1];

const id = process.env.PG_OBJECTID || 'gid';
const pgLimit = process.env.PG_LIMIT || 10000000;

if (!table)
throw new Error('The "id" parameter must be in the form of "schema.table"');

const result = await db.data.getGeometryColumnName(schema, table);

if (!result) {
throw new Error('Invalid result from getGeometryColumnName');
const geomColumnName = await db.data.getGeometryColumnName(schema, table);
if (!geomColumnName || !geomColumnName.f_geometry_column || !geomColumnName.srid) {
console.log(`Table ${schema}.${table} does not have a geometry column.`);
return callback(null, {
type: 'FeatureCollection',
features: [],
metadata: {
title: schema,
name: schema + '.' + table,
description: 'This table does not contain spatial data.',
geometryType: null
}
});
}

const geom = result.f_geometry_column;
const srid = result.srid;
const geom = geomColumnName.f_geometry_column;
const srid = geomColumnName.srid;
const limit = parseInt(pgLimit);
const offset = 0;

const geojsonResult = await db.data.createGeoJson(id, geom, srid, schema + '.' + table, limit, offset);
let geojson = geojsonResult.jsonb_build_object;
const geojson = await db.data.createGeoJson(id, geom, srid, schema + '.' + table, limit, offset);

if (!geojson || typeof geojson !== 'object' || !geojson.type || !geojson.features) {
console.log(`Unexpected result from createGeoJson.`);
return callback(null, {
type: 'FeatureCollection',
features: [],
metadata: {
title: schema,
name: schema + '.' + table,
description: 'no-data',
geometryType: null
}
});
}

geojson.description = 'PG Koop Feature Service';

if (!geojson.metadata) {
geojson.metadata = {
title: schema,
Expand All @@ -43,11 +64,10 @@ class Model {

callback(null, geojson);
} catch (error) {
console.error('Error in getData:', error);
callback(error);
console.error(error);
}
}
}


module.exports = Model
145 changes: 72 additions & 73 deletions src/view/map.html
Original file line number Diff line number Diff line change
@@ -1,81 +1,80 @@
j<html lang="">

<head>
<meta charset="utf-8" />
<title>Feature Layer Viewer</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />

<style>
body {
margin: 0;
padding: 0;
}

#map {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}

</style>

<!-- Load Leaflet from CDN -->
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""></script>

<!-- Load Esri Leaflet from CDN -->
<script src="https://unpkg.com/[email protected]/dist/esri-leaflet.js"
integrity="sha512-ucw7Grpc+iEQZa711gcjgMBnmd9qju1CICsRaryvX7HJklK0pGl/prxKvtHwpgm5ZHdvAil7YPxI1oWPOWK3UQ=="
crossorigin=""></script>

<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>

<script src="util.js"></script>

<script>
// replace ${schema}.${table}
const featureLayerUrl = 'http://localhost:8080/pg/rest/services/${schema}.${table}/FeatureServer'

$.getJSON(featureLayerUrl, function (data) {

const bounds = L.latLngBounds(
[
getValue(data, "fullExtent.ymax"),
getValue(data, "fullExtent.xmin")
], [
getValue(data, "fullExtent.ymin"),
getValue(data, "fullExtent.xmax")
]
)

const map = L.map('map').fitBounds(bounds)

L.esri.basemapLayer('DarkGray', {
detectRetina: true
}).addTo(map)

const postgis = L.esri.featureLayer({
url: featureLayerUrl + '/layers'
}).addTo(map)

postgis.bindPopup(function (layer) {
return L.Util.template('{name}', layer.feature.properties)
})
})

</script>
<meta charset="utf-8" />
<title>Feature Layer Viewer</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />

<style>
body {
margin: 0;
padding: 0;
}

#map {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
</style>

<!-- Load Leaflet from CDN -->
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""></script>

<!-- Load Esri Leaflet from CDN -->
<script src="https://unpkg.com/[email protected]/dist/esri-leaflet.js"
integrity="sha512-ucw7Grpc+iEQZa711gcjgMBnmd9qju1CICsRaryvX7HJklK0pGl/prxKvtHwpgm5ZHdvAil7YPxI1oWPOWK3UQ=="
crossorigin=""></script>

<!-- jquery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>

<script src="util.js"></script>

<script>
// replace ${schema}.${table}
const featureLayerUrl = 'http://localhost:8080/pg/rest/services/${schema}.${table}/FeatureServer'

$.getJSON(featureLayerUrl, function (data) {

const bounds = L.latLngBounds(
[
getValue(data, "fullExtent.ymax"),
getValue(data, "fullExtent.xmin")
], [
getValue(data, "fullExtent.ymin"),
getValue(data, "fullExtent.xmax")
]
)

const map = L.map('map').fitBounds(bounds)

L.esri.basemapLayer('DarkGray', {
detectRetina: true
}).addTo(map)

const postgis = L.esri.featureLayer({
url: featureLayerUrl + '/layers'
}).addTo(map)

postgis.bindPopup(function (layer) {
return L.Util.template('{name}', layer.feature.properties)
})
})

</script>
</head>

<body>
<div id="map"></div>
<div id="map"></div>
</body>

</html>
</html>
82 changes: 40 additions & 42 deletions src/view/vector-tile.html
Original file line number Diff line number Diff line change
@@ -1,53 +1,51 @@
<html>

<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>VectorTileLayer</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>VectorTileLayer</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>

</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.19/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.19/"></script>

<link rel="stylesheet" href="https://js.arcgis.com/4.19/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.19/"></script>
<script>
require(["esri/Map", "esri/views/MapView", "esri/layers/VectorTileLayer"], function (
Map,
MapView,
VectorTileLayer
) {
// Create a Map
const map = new Map({
basemap: "dark-gray-vector"
});
// Make map view and bind it to the map
const view = new MapView({
container: 'viewDiv',
map: map,
center: [-151.32991, 65.06699],
zoom: 4,
})

<script>
require(["esri/Map", "esri/views/MapView", "esri/layers/VectorTileLayer"], function (
Map,
MapView,
VectorTileLayer
) {
// Create a Map
const map = new Map({
basemap: "dark-gray-vector"
});
// Make map view and bind it to the map
const view = new MapView({
container: 'viewDiv',
map: map,
center: [-151.32991, 65.06699],
zoom: 4,
})

const tileLayer = new VectorTileLayer({
url: 'http://localhost:8080/pg/rest/services/${schema}.${table}/VectorTileServer',
})
map.add(tileLayer);
});
</script>
const tileLayer = new VectorTileLayer({
url: 'http://localhost:8080/pg/rest/services/${schema}.${table}/VectorTileServer',
})
map.add(tileLayer);
}); </script>
</head>

<body>
<div id="viewDiv"></div>
<div id="viewDiv"></div>
</body>

</html>
</html>

0 comments on commit df4dc3d

Please sign in to comment.