Commit c3d17c03 authored by Anne Blankert's avatar Anne Blankert

start data browse UI, add README

parent c93728c6
# PGServer
*Work in progress, many/some features still missing!*
Another PostGIS http API server
Upload and download geo-data, preview, filter
Serve cached mapbox vector tiles (mvt), geojson, geobuf
......@@ -20,6 +22,7 @@ If you don't have git, you can donwload [a zip file](https://github.com/anneb/pg
npm start
# point your browser to localost:8090 for more info
For interactive data browsing, preview, administration and api documentation, head to [http://localhost:8090](http://localhost:8090).
### Due credit
API based on [Dirt Simple PostGIS http API](https://github.com/tobinbradley/dirt-simple-postgis-http-api)
\ No newline at end of file
const sql = (params) => {
return `
SELECT
attname as field_name,
typname as field_type
FROM
pg_namespace, pg_attribute, pg_type, pg_class
WHERE
pg_type.oid = atttypid AND
pg_class.oid = attrelid AND
relnamespace = pg_namespace.oid AND
attnum >= 1 AND
relname = '${params.table}'
`
}
module.exports = function (app, pool) {
/**
* @swagger
*
* /data/layer_columns/:table:
* get:
* description: Returns a list of columns in the specified table.
* tags: ['meta']
* summary: 'list table columns'
* produces:
* - application/json
* parameters:
* - name: table
* description: The name of the table
* in: path
* required: true
* type: string
* responses:
* 200:
* description: list of columns (names and types)
* 422:
* description: table not found or not accessible
*/
app.get('/data/layer_columns/:table', async (req, res)=> {
const sqlString = sql(req.params, req.query);
try {
const result = await pool.query(sqlString);
res.json(result.rows);
} catch (err) {
switch (err.code) {
case '42P01':
err.name = `table ${req.params.table} does not exist`;
break;
case '42703':
err.name = `column does not exist`;
break;
}
res.status(422).json({error: err});
}
})
}
\ No newline at end of file
......@@ -9,7 +9,8 @@ const sql = () => {
a.attname AS f_geometry_column,
COALESCE(postgis_typmod_dims(a.atttypmod), sn.ndims, 2) AS coord_dimension,
COALESCE(NULLIF(postgis_typmod_srid(a.atttypmod), 0), sr.srid, 0) AS srid,
replace(replace(COALESCE(NULLIF(upper(postgis_typmod_type(a.atttypmod)), 'GEOMETRY'::text), st.type, 'GEOMETRY'::text), 'ZM'::text, ''::text), 'Z'::text, ''::text)::character varying(30) AS type
replace(replace(COALESCE(NULLIF(upper(postgis_typmod_type(a.atttypmod)), 'GEOMETRY'::text), st.type, 'GEOMETRY'::text), 'ZM'::text, ''::text), 'Z'::text, ''::text)::character varying(30) AS type,
((c.reltuples/case when c.relpages=0 then 1 else c.relpages end) * (pg_relation_size(c.oid) / (current_setting('block_size')::integer)))::bigint as estimated_rows
FROM pg_class c
JOIN pg_attribute a ON a.attrelid = c.oid AND NOT a.attisdropped
JOIN pg_namespace n ON c.relnamespace = n.oid
......
......@@ -21,7 +21,8 @@ const upload = require('./upload.js')(app);
const mvt = require('./mvt.js')(app, readOnlyPool);
const geojson = require('./geojson.js')(app, readOnlyPool);
const geobuf = require('./geobuf.js')(app, readOnlyPool);
const list_layers = require('./list_layers')(app, readOnlyPool);
const list_layers = require('./list_layers.js')(app, readOnlyPool);
const layer_columns = require('./layer_columns.js')(app, readOnlyPool);
const bbox = require('./bbox.js')(app, readOnlyPool);
app.listen(pgserverconfig.port);
......
......@@ -13,6 +13,7 @@
<body>
<h1>PGServer</h1>
Welcome to pgserver<br>
<a href="/info.html">Browse geo-data</a><br>
<a href="/api-docs">Swagger api docs</a><br>
<a href="/upload.html">Upload files</a>
</body>
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Info</title>
<script>
function init() {
fetch('data/list_layers').then(response=>{
if (response.ok) {
response.json().then(json=> {
const list = document.querySelector('#layerlist');
for (item of json) {
const li = document.createElement('li');
li.innerHTML = `<a href="tableinfo.html?table=${item.f_table_schema}.${item.f_table_name}&geom_column=${item.f_geometry_column}&srid=${item.srid}&geom_type=${item.type}&dimensions=${item.coord_dimension}">${item.f_table_schema}.${item.f_table_name}</a> geom: ${item.f_geometry_column}, srid:${item.srid}, geom_type:${item.type}, ${item.coord_dimension}D, count: ${item.estimated_rows}`
list.appendChild(li);
}
})
}
})
}
</script>
</head>
<body onload="init()">
<h1>Vector layers in database</h1>
<ul id="layerlist"></ul>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Info</title>
<script>
function init() {
const urlParams = new URLSearchParams(window.location.search);
const fullTableName = urlParams.get('table');
const geomType = urlParams.get('geomtype');
document.querySelector('#tablename').innerHTML = fullTableName;
const parts = fullTableName.split('.');
const tableName = (parts.length > 1) ? parts[1] : parts[0];
fetch(`data/layer_columns/${tableName}`).then(response=>{
const list = document.querySelector('#columns');
if (response.ok) {
response.json().then(json=> {
for (item of json) {
const li = document.createElement('li');
li.innerHTML = `<a href="./attrinfo.html?table=${fullTableName}&column=${item.field_name}&columntype=${item.field_type}&geomtype=${geomType}"><b>${item.field_name}</b></a> (${item.field_type})`
list.appendChild(li);
}
})
} else {
const li = document.createElement('li');
li.innerHTML = `Error getting list, response: ${response.status} ${response.statusText?response.statusText:''} ${response.url}`
list.appendChild(li);
}
})
fetch(`api/bbox/${fullTableName}`).then(response=>{
const bbox = document.querySelector('#bbox');
if (response.ok) {
response.json().then(json=> {
bbox.innerHTML = "";
for (item of json) {
bbox.innerHTML += item.bbox;
}
})
} else {
bbox.innerHTML = `Error getting bbox, response: response: ${response.status} ${response.statusText?response.statusText:''} ${response.url}`
}
})
}
</script>
</head>
<body onload="init()">
<h1>Layer informatie</h1>
<h2 id="tablename"></h2>
<div id="bbox">waiting for bbox...</div>
<ul id="columns"></ul>
<a href="info.html">Terug naar layer overzicht</a>
</body>
</html>
\ No newline at end of file
......@@ -23,7 +23,7 @@ const swaggerDefinition = {
const swaggerJSDocOptions = {
swaggerDefinition,
apis: ['./login.js', './mvt.js', './list_layers.js', './bbox.js', './geojson.js', './geobuf.js']
apis: ['./login.js', './mvt.js', './list_layers.js', './layer_columns.js', './bbox.js', './geojson.js', './geobuf.js']
}
const swaggerSpec = swaggerJSDoc(swaggerJSDocOptions);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment