mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 09:37:08 +00:00
Also serve preplot source/sail points as binary.
This commit adds the ability to pack preplot points in Dougal binary format. Sail line points take udv=0 and source line points take udv=1 – udv=2 remains sequence data. Endpoints for retrieving the data in JSON, GeoJSON and binary formats have also been added. Data may be retrieved as a single line or for a whole project.
This commit is contained in:
@@ -155,10 +155,13 @@ app.map({
|
|||||||
'/project/:project/line/': {
|
'/project/:project/line/': {
|
||||||
get: [ mw.auth.access.read, mw.line.list ],
|
get: [ mw.auth.access.read, mw.line.list ],
|
||||||
},
|
},
|
||||||
'/project/:project/line/:line': {
|
'/project/:project/line/:line(\\d+)': {
|
||||||
// get: [ mw.auth.access.read, mw.line.get ],
|
get: [ mw.auth.access.read, mw.line.get ],
|
||||||
patch: [ mw.auth.access.write, mw.line.patch ],
|
patch: [ mw.auth.access.write, mw.line.patch ],
|
||||||
},
|
},
|
||||||
|
'/project/:project/line/:class(sail|source)': {
|
||||||
|
get: [ mw.auth.access.read, mw.line.get ],
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sequence endpoints
|
* Sequence endpoints
|
||||||
|
|||||||
17
lib/www/server/api/middleware/line/get/binary.js
Normal file
17
lib/www/server/api/middleware/line/get/binary.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const { bundle } = require('../../../../lib/binary');
|
||||||
|
const { line } = require('../../../../lib/db');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = await line.get(req.params.project, req.params.class, req.params.line, {wgs84: true, ...req.query});
|
||||||
|
const data = bundle(json, {type: req.params.class == "source" ? 1 : 0});
|
||||||
|
console.log("bundle", data);
|
||||||
|
|
||||||
|
res.status(200).send(Buffer.from(data));
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
27
lib/www/server/api/middleware/line/get/geojson.js
Normal file
27
lib/www/server/api/middleware/line/get/geojson.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
const { line } = require('../../../../lib/db');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = await line.get(req.params.project, req.params.class, req.params.line, {wgs84: true, ...req.query});
|
||||||
|
|
||||||
|
const geojson = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: json.map(feature => {
|
||||||
|
return {
|
||||||
|
type: "Feature",
|
||||||
|
geometry: feature["geometry"],
|
||||||
|
properties: {...feature}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).send(geojson);
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
26
lib/www/server/api/middleware/line/get/index.js
Normal file
26
lib/www/server/api/middleware/line/get/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const json = require('./json');
|
||||||
|
const geojson = require('./geojson');
|
||||||
|
const binary = require('./binary');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
try {
|
||||||
|
const handlers = {
|
||||||
|
"application/json": json,
|
||||||
|
"application/geo+json": geojson,
|
||||||
|
"application/vnd.aaltronav.dougal+octet-stream": binary,
|
||||||
|
"application/vnd.aaltronav.dougal+octet-stream; format=0x1c": binary,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
||||||
|
|
||||||
|
if (mimetype) {
|
||||||
|
res.set("Content-Type", mimetype);
|
||||||
|
await handlers[mimetype](req, res, next);
|
||||||
|
} else {
|
||||||
|
res.status(406).send();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
lib/www/server/api/middleware/line/get/json.js
Normal file
14
lib/www/server/api/middleware/line/get/json.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
const { line } = require('../../../../lib/db');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.status(200).send(await line.get(req.params.project, req.params.class, req.params.line, req.query));
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
17
lib/www/server/api/middleware/line/list/binary.js
Normal file
17
lib/www/server/api/middleware/line/list/binary.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const { bundle } = require('../../../../lib/binary');
|
||||||
|
const { line } = require('../../../../lib/db');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = await line.get(req.params.project, req.params.class, req.params.line, {wgs84: true, ...req.query});
|
||||||
|
const data = bundle(json, {type: req.query.type ?? 0});
|
||||||
|
console.log("bundle", data);
|
||||||
|
|
||||||
|
res.status(200).send(Buffer.from(data));
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
27
lib/www/server/api/middleware/line/list/geojson.js
Normal file
27
lib/www/server/api/middleware/line/list/geojson.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
const { line } = require('../../../../lib/db');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const json = await line.get(req.params.project, req.params.class, req.params.line, {wgs84: true, ...req.query});
|
||||||
|
|
||||||
|
const geojson = {
|
||||||
|
type: "FeatureCollection",
|
||||||
|
features: json.map(feature => {
|
||||||
|
return {
|
||||||
|
type: "Feature",
|
||||||
|
geometry: feature["geometry"],
|
||||||
|
properties: {...feature}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).send(geojson);
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
26
lib/www/server/api/middleware/line/list/index.js
Normal file
26
lib/www/server/api/middleware/line/list/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const json = require('./json');
|
||||||
|
const geojson = require('./geojson');
|
||||||
|
const binary = require('./binary');
|
||||||
|
|
||||||
|
module.exports = async function (req, res, next) {
|
||||||
|
try {
|
||||||
|
const handlers = {
|
||||||
|
"application/json": json,
|
||||||
|
"application/geo+json": geojson,
|
||||||
|
"application/vnd.aaltronav.dougal+octet-stream": binary,
|
||||||
|
"application/vnd.aaltronav.dougal+octet-stream; format=0x1c": binary,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
||||||
|
|
||||||
|
if (mimetype) {
|
||||||
|
res.set("Content-Type", mimetype);
|
||||||
|
await handlers[mimetype](req, res, next);
|
||||||
|
} else {
|
||||||
|
res.status(406).send();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
const { line } = require('../../../lib/db');
|
const { line } = require('../../../../lib/db');
|
||||||
|
|
||||||
module.exports = async function (req, res, next) {
|
module.exports = async function (req, res, next) {
|
||||||
|
|
||||||
@@ -10,4 +10,5 @@ module.exports = async function (req, res, next) {
|
|||||||
next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -5,7 +5,7 @@ module.exports = async function (req, res, next) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const json = await sequence.get(req.params.project, req.params.sequence, req.query);
|
const json = await sequence.get(req.params.project, req.params.sequence, req.query);
|
||||||
const data = bundle(json, {type: req.query.type});
|
const data = bundle(json, {type: req.query.type ?? 2});
|
||||||
console.log("bundle", data);
|
console.log("bundle", data);
|
||||||
|
|
||||||
res.status(200).send(Buffer.from(data));
|
res.status(200).send(Buffer.from(data));
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ module.exports = async function (req, res, next) {
|
|||||||
const handlers = {
|
const handlers = {
|
||||||
"application/json": json,
|
"application/json": json,
|
||||||
"application/geo+json": geojson,
|
"application/geo+json": geojson,
|
||||||
// application/vnd.aaltronav.dougal+octet-stream; format=0x1c
|
"application/vnd.aaltronav.dougal+octet-stream": binary,
|
||||||
"application/vnd.aaltronav.dougal+octet-stream": binary
|
"application/vnd.aaltronav.dougal+octet-stream; format=0x1c": binary,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module.exports = async function (req, res, next) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const json = await sequence.get(req.params.project, null, req.query);
|
const json = await sequence.get(req.params.project, null, req.query);
|
||||||
const data = bundle(json, {type: req.query.type});
|
const data = bundle(json, {type: req.query.type ?? 2});
|
||||||
console.log("bundle", data);
|
console.log("bundle", data);
|
||||||
|
|
||||||
res.status(200).send(Buffer.from(data));
|
res.status(200).send(Buffer.from(data));
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ module.exports = async function (req, res, next) {
|
|||||||
const handlers = {
|
const handlers = {
|
||||||
"application/json": json,
|
"application/json": json,
|
||||||
"application/geo+json": geojson,
|
"application/geo+json": geojson,
|
||||||
"application/vnd.aaltronav.dougal+octet-stream": binary
|
"application/vnd.aaltronav.dougal+octet-stream": binary,
|
||||||
|
"application/vnd.aaltronav.dougal+octet-stream; format=0x1c": binary,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
const mimetype = (handlers[req.query.mime] && req.query.mime) || req.accepts(Object.keys(handlers));
|
||||||
|
|||||||
@@ -11,6 +11,73 @@ function bundle (json, opts = {}) {
|
|||||||
// console.log("JSON LENGTH", json.length);
|
// console.log("JSON LENGTH", json.length);
|
||||||
// console.log("OPTS", geometries, payload);
|
// console.log("OPTS", geometries, payload);
|
||||||
|
|
||||||
|
if (type == 0) {
|
||||||
|
/* Preplot information – sail line points
|
||||||
|
*
|
||||||
|
* elem 0: Float32Array Longitude
|
||||||
|
* elem 1: Float32Array Latitude
|
||||||
|
* elem 2: Uint8Array – Flags: 0x01 point ntba, 0x02 sailline ntba
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Add preplot positions
|
||||||
|
values.push({
|
||||||
|
// longitude
|
||||||
|
key: el => el.geometry.coordinates?.[0],
|
||||||
|
type: Float32Array
|
||||||
|
});
|
||||||
|
|
||||||
|
values.push({
|
||||||
|
// latitude
|
||||||
|
key: el => el.geometry?.coordinates?.[1],
|
||||||
|
type: Float32Array
|
||||||
|
});
|
||||||
|
|
||||||
|
values.push({
|
||||||
|
// flags
|
||||||
|
key: el => (el.sailline_ntba ? 0x02 : 0) | (el.ntba ? 0x01 : 0),
|
||||||
|
type: Uint8Array
|
||||||
|
});
|
||||||
|
|
||||||
|
return encode.sequential(json, el => el.sailline, el => el.point, deltas, values, type)
|
||||||
|
|
||||||
|
} if (type == 1) {
|
||||||
|
/* Preplot information – source line points
|
||||||
|
*
|
||||||
|
* elem 0: Float32Array Longitude
|
||||||
|
* elem 1: Float32Array Latitude
|
||||||
|
* elem 2: Uint8Array – Flags: 0x01 point ntba, 0x02 sailline ntba
|
||||||
|
* elem 3: Uint16Array Sailline
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Add preplot positions
|
||||||
|
values.push({
|
||||||
|
// longitude
|
||||||
|
key: el => el.geometry.coordinates?.[0],
|
||||||
|
type: Float32Array
|
||||||
|
});
|
||||||
|
|
||||||
|
values.push({
|
||||||
|
// latitude
|
||||||
|
key: el => el.geometry?.coordinates?.[1],
|
||||||
|
type: Float32Array
|
||||||
|
});
|
||||||
|
|
||||||
|
values.push({
|
||||||
|
// flags
|
||||||
|
key: el => (el.sailline_ntba ? 0x02 : 0) | (el.ntba ? 0x01 : 0),
|
||||||
|
type: Uint8Array
|
||||||
|
});
|
||||||
|
|
||||||
|
values.push({
|
||||||
|
// sailline
|
||||||
|
key: el => el.sailline,
|
||||||
|
type: Uint16Array
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("JSON", json[0]);
|
||||||
|
return encode.sequential(json, el => el.line, el => el.point, deltas, values, type)
|
||||||
|
|
||||||
|
} else if (type == 2) {
|
||||||
/* Gun information:
|
/* Gun information:
|
||||||
*
|
*
|
||||||
* - Δelem 0: BigUint64Array + Int16Array – timestamps
|
* - Δelem 0: BigUint64Array + Int16Array – timestamps
|
||||||
@@ -23,7 +90,6 @@ function bundle (json, opts = {}) {
|
|||||||
* - elem 16‒18: Gun delay (μ, σ, R)
|
* - elem 16‒18: Gun delay (μ, σ, R)
|
||||||
* - elem 19: No fire / autofire (in a single byte)
|
* - elem 19: No fire / autofire (in a single byte)
|
||||||
*/
|
*/
|
||||||
if (type == 2) {
|
|
||||||
|
|
||||||
// Add timestamps
|
// Add timestamps
|
||||||
deltas.push({
|
deltas.push({
|
||||||
@@ -155,12 +221,12 @@ function bundle (json, opts = {}) {
|
|||||||
key: el => (el.meta.raw?.smsrc?.no_fire ?? 0) << 4 | (el.meta.raw?.smsrc?.num_auto ?? 0),
|
key: el => (el.meta.raw?.smsrc?.no_fire ?? 0) << 4 | (el.meta.raw?.smsrc?.num_auto ?? 0),
|
||||||
type: Uint8Array
|
type: Uint8Array
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
console.log("DELTAS", deltas);
|
console.log("DELTAS", deltas);
|
||||||
console.log("VALUES", values);
|
console.log("VALUES", values);
|
||||||
|
|
||||||
return encode.sequential(json, el => el.sequence, el => el.point, deltas, values, type)
|
return encode.sequential(json, el => el.sequence, el => el.point, deltas, values, type)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = { bundle };
|
module.exports = { bundle };
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
const { setSurvey } = require('../connection');
|
||||||
|
|
||||||
|
async function getSummary (projectId, line, opts = {}) {
|
||||||
|
const client = await setSurvey(projectId);
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
SELECT pls.line, pls.fsp, pls.lsp, pls.num_points, pls.length, pls.azimuth, pls.incr,
|
||||||
|
pl.ntba, pl.geometry, pl.remarks, pl.meta
|
||||||
|
FROM preplot_lines_summary pls
|
||||||
|
INNER JOIN preplot_lines pl
|
||||||
|
ON psl.line = pl.line and pl.class = 'V'
|
||||||
|
WHERE ($1:integer IS NULL) OR (line = $1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const res = await client.query(text, [line]);
|
||||||
|
client.release();
|
||||||
|
|
||||||
|
return res.rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function get (projectId, type = "sail", line = null, opts = {}) {
|
||||||
|
if (opts.summary) {
|
||||||
|
return await getSummary(projectId, line, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await setSurvey(projectId);
|
||||||
|
|
||||||
|
const sortFields = [
|
||||||
|
"sailline", "line", "point", "ntba", "sailline_ntba"
|
||||||
|
];
|
||||||
|
const sortKey = opts.sortBy && sortFields.includes(opts.sortBy) && opts.sortBy || "line";
|
||||||
|
const sortDir = opts.sortDesc == "false" ? "ASC" : "DESC";
|
||||||
|
const offset = Math.abs(opts.offset) || Math.abs((opts.page-1)*opts.itemsPerPage) || 0;
|
||||||
|
const limit = Math.abs(opts.limit) || Math.abs(Number(opts.itemsPerPage)) || null;
|
||||||
|
|
||||||
|
let res;
|
||||||
|
|
||||||
|
if (type == "source") {
|
||||||
|
|
||||||
|
const geometry = (opts.wgs84 || opts.lonlat || opts.latlon)
|
||||||
|
? "ST_Transform(geometry, 4326)::json geometry"
|
||||||
|
: "geometry::json";
|
||||||
|
|
||||||
|
const restriction = line
|
||||||
|
? "(sailline = $3 OR line = $3)"
|
||||||
|
: "(TRUE OR $3)";
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
SELECT
|
||||||
|
sailline, sailline_ntba, line, point, ntba,
|
||||||
|
${geometry},
|
||||||
|
meta
|
||||||
|
FROM preplot_saillines_points
|
||||||
|
WHERE ${restriction}
|
||||||
|
ORDER BY ${sortKey} ${sortDir}
|
||||||
|
OFFSET $1
|
||||||
|
LIMIT $2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const values = [offset, limit, line];
|
||||||
|
res = await client.query(text, values);
|
||||||
|
|
||||||
|
} else if (type == "sail") {
|
||||||
|
|
||||||
|
const geometry = (opts.wgs84 || opts.lonlat || opts.latlon)
|
||||||
|
? "ST_Transform(pp.geometry, 4326)::json geometry"
|
||||||
|
: "pp.geometry::json";
|
||||||
|
|
||||||
|
const restriction = line
|
||||||
|
? "(sailline = $3 OR psl.line = $3)"
|
||||||
|
: "(TRUE OR $3)";
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
WITH psl AS (
|
||||||
|
SELECT DISTINCT sailline, ntba
|
||||||
|
FROM preplot_saillines
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
psl.sailline, pp.point, pp.ntba, psl.ntba sailline_ntba, pp.meta, ${geometry}
|
||||||
|
FROM preplot_points pp
|
||||||
|
INNER JOIN psl ON psl.sailline = pp.line
|
||||||
|
WHERE pp.class = 'V'
|
||||||
|
AND ${restriction}
|
||||||
|
ORDER BY ${sortKey} ${sortDir}
|
||||||
|
OFFSET $1
|
||||||
|
LIMIT $2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const values = [offset, limit, line];
|
||||||
|
res = await client.query(text, values);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
client.release();
|
||||||
|
|
||||||
|
if (opts.project) {
|
||||||
|
const tokens = opts.project.split(/\s*[,;:\s]\s*/).filter(e => e.length);
|
||||||
|
const project = tokens.map(i => i.replace(/^([^.]+)\..*$/, "$1"));
|
||||||
|
return res.rows.map( r =>
|
||||||
|
Object.fromEntries(Object.entries(r).filter(entry => project.includes(entry[0])))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return res.rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = get;
|
||||||
|
|||||||
Reference in New Issue
Block a user