Commit 04e2b4ef authored by Athanasios's avatar Athanasios
Browse files

initial commit

parents
const toLower = (value) => {
return value.toLowerCase();
}
module.exports = { toLower };
\ No newline at end of file
class DrawingBuffer {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
const fromString = (str) => {
let tokens = str.split(",");
return new DrawingBuffer(...tokens.map(token => parseFloat(token)));
}
module.exports = { DrawingBuffer, fromString };
\ No newline at end of file
const pug = require("pug");
const errorHandler = (errors) => {
let e = null;
if (errors.array()[0].nestedErrors) {
e = errors.array()[0].nestedErrors[0];
} else {
e = errors.array()[0];
}
const compiledFunction = pug.compileFile("./views/exception.pug");
let exception = compiledFunction({
exceptionCode: e.msg,
locator: e.param
});
return exception;
}
module.exports = { errorHandler };
\ No newline at end of file
class Geographic {
constructor(longitude, latitude, height) {
this.longitude = longitude;
this.latitude = latitude;
this.height = height || 0.0;
}
}
module.exports = Geographic;
\ No newline at end of file
const boxIntersect = require("box-intersect");
const angle = require("./angle");
class GeographicBoundingBox {
constructor(minLong, minLat, maxLong, maxLat) {
this.minLong = minLong;
this.minLat = minLat;
this.maxLong = maxLong;
this.maxLat = maxLat;
}
extend(percent) {
percent *= 0.01;
let diffLon = (this.maxLong - this.minLong) * percent;
let diffLat = (this.maxLat - this.minLat) * percent;
this.minLong -= diffLon;
this.maxLong += diffLon;
this.minLat -= diffLat;
this.maxLat += diffLat;
}
getCorners() {
return [
this.minLong,
this.minLat,
this.maxLong,
this.maxLat
];
}
intersectWith(gbb) {
let overlaps = boxIntersect([
this.getCorners(),
gbb.getCorners()
]);
if (overlaps.length > 0) {
return true;
}
return false;
}
intersectWithGeographic(geographic) {
if (this.minLong <= geographic.longitude &&
this.maxLong >= geographic.longitude &&
this.minLat <= geographic.latitude &&
this.maxLat >= geographic.latitude) {
return true;
}
return false;
}
clone() {
return new GeographicBoundingBox(...this.getCorners());
}
}
const fromString = (str) => {
let tokens = str.split(",");
return new GeographicBoundingBox(...tokens.map(token => parseFloat(token)));
}
const fromBoundingSphere = (boundingSphere) => {
let center = boundingSphere.geographicCenter;
let deltaLat = angle.roughDistanceToLatitude(boundingSphere.radius);
let deltaLong = angle.roughDistanceToLongitude(center.latitude, boundingSphere.radius);
return new GeographicBoundingBox(
center.longitude - deltaLong,
center.latitude - deltaLat,
center.longitude + deltaLong,
center.latitude + deltaLat
);
}
module.exports = { GeographicBoundingBox, fromString, fromBoundingSphere };
\ No newline at end of file
const assets = require("../src/assets");
const pug = require("pug");
const getCapabilitiesHandler = (req, res) => {
assets.getPublicLayers().then(layers => {
const compiledFunction = pug.compileFile("./views/capabilities.pug");
let capabilities = compiledFunction({ layers: layers });
res.set("Content-Type", "application/xml");
res.status(200).send(capabilities);
});
}
module.exports = { getCapabilitiesHandler };
\ No newline at end of file
const assets = require("../src/assets");
const Traverse = require("./traverse");
const getSceneHandler = (req, res) => {
assets.getIntersecting(req.query.boundingbox).then(layers => {
let traverse = new Traverse();
traverse.begin(req, layers[0].url)
.then(nodes => {
res.status(200).json(nodes);
});
});
}
module.exports = { getSceneHandler };
\ No newline at end of file
const Intersect = {
INSIDE: 0,
OUTSIDE: 1,
INTERSECTING: 2
}
module.exports = Intersect;
\ No newline at end of file
class Nodes {
constructor() {
this.nodes = [];
}
get sorted() {
return this.nodes.sort((a, b) => a.distanceToCamera > b.distanceToCamera);
}
add(context, node, boundingSphere) {
let entry = {
id: node.id,
level: node.level,
lng: node.mbs[0],
lat: node.mbs[1],
radius: node.mbs[3],
url: `${context.layer}/nodes/${node.id}`,
distanceToCamera: boundingSphere.distanseTo(context.camera.position),
time: context.requestTime
};
if (context.intersect) {
let requestedBB = context.bb.clone();
requestedBB.extend(25);
if (requestedBB.intersectWithGeographic(boundingSphere.geographicCenter)) {
this.nodes.push(entry);
}
} else {
this.nodes.push(entry);
}
}
}
module.exports = Nodes;
\ No newline at end of file
class OffCenterFrustum {
constructor(near, top, right) {
this.near = near;
this.top = top;
this.right = right;
}
}
const fromString = (str) => {
let tokens = str.split(",");
return new OffCenterFrustum(...tokens.map(token => parseFloat(token)));
}
module.exports = { OffCenterFrustum, fromString };
\ No newline at end of file
class Plane {
constructor(normal, distance) {
this.normal = normal;
this.distance = distance;
}
}
module.exports = Plane;
\ No newline at end of file
const { getSceneHandler } = require("./getSceneHandler");
const { getCapabilitiesHandler } = require("./getCapabilitiesHandler");
const createHandler = (request) => {
switch (request.toLowerCase()) {
case "getscene": {
return getSceneHandler;
}
case "getcapabilities": {
return getCapabilitiesHandler;
}
}
}
module.exports = { createHandler };
\ No newline at end of file
const screenSpaceError = (context, bs) => {
let c = context.camera;
let frustum = context.offCenterFrustum;
let buffer = context.drawingBuffer;
let cameraToSphere = bs.center.subtract(c.position);
let cameraToSphereProj = c.direction.clone();
cameraToSphereProj.multiplyScalar(c.direction.dot(cameraToSphere));
let distance = cameraToSphereProj.magnitude();
let inverseNear = 1.0 / frustum.near;
let tanTheta = frustum.top * inverseNear;
let pixelHeight = 2.0 * distance * tanTheta / buffer.height;
tanTheta = frustum.right * inverseNear;
let pixelWidth = 2.0 * distance * tanTheta / buffer.width;
return Math.max(bs.diameter / pixelWidth, bs.diameter / pixelHeight);
}
module.exports = { screenSpaceError };
\ No newline at end of file
const fetch = require("node-fetch");
const context = require("./context");
const bs = require("./boundingSphere");
const Intersect = require("./intersect");
const screenSpace = require("./screenSpace");
const Nodes = require("./nodes");
class Traverse {
constructor() {
this.stack = [];
this.nodes = new Nodes();
}
async begin(req, layer) {
let ctx = context.fromRequest(req);
ctx.layer = layer;
let rootNode = `${layer}/nodes/root`;
this.stack.push(fetch(rootNode));
while (this.stack.length > 0) {
let currentResponses = await Promise.all(this.stack);
let currentNodes = await Promise.all(currentResponses.map(node => node.json()));
this.stack = currentNodes.reduce((accumulator, node) => {
if (this.shouldDescend(ctx, node)) {
accumulator.push(...node.children.map(child => fetch(`${layer}/nodes/${child.id}`)));
}
return accumulator;
}, []);
}
return this.nodes.sorted;
}
shouldDescend(ctx, child) {
let boundingSphere = bs.fromArray(child.mbs);
if (ctx.cullingVolume.computeVisibility(boundingSphere) == Intersect.OUTSIDE) {
return false;
}
let maxError = child.lodSelection[0].maxError;
let screenSpaceError = screenSpace.screenSpaceError(ctx, boundingSphere);
if (screenSpaceError >= maxError && child.children) {
return true;
}
this.nodes.add(ctx, child, boundingSphere);
return false;
}
}
module.exports = Traverse;
\ No newline at end of file
const { check } = require("express-validator");
const {toLower} = require("../src/customSanitizers");
const assets = require("../src/assets");
const bb = require("../src/boundingbox");
const commonChecks = () => {
return [
check("service")
.exists().withMessage("MissingParameterValue")
.customSanitizer(toLower)
.equals("3dps").withMessage("InvalidParameterValue"),
check("acceptversions")
.exists().withMessage("MissingParameterValue")
.equals("1.0").withMessage("InvalidParameterValue")
];
}
const specificChecks = (parameter) => {
switch (parameter.toLowerCase()) {
case "getscene": {
return [
check("boundingbox")
.exists().withMessage("MissingParameterValue")
.matches(/^((\-?\d+(\.\d+)?),){3}(\-?\d+(\.\d+)?)$/).withMessage("InvalidParameterValue")
.custom(value => bb.isValid(value)).withMessage("InvalidParameterValue"),
check("layers")
.exists().withMessage("MissingParameterValue")
.custom(value => assets.allLayersExist(value)).withMessage("UnknownLayer")
];
}
case "getcapabilities": {
return [];
}
}
}
module.exports = { commonChecks, specificChecks };
\ No newline at end of file
const angle = require("./angle");
class Vector3 {
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
normalize() {
let magnitude = this.magnitude();
this.x /= magnitude;
this.y /= magnitude;
this.z /= magnitude;
}
add(vector) {
return new Vector3(
this.x + vector.x,
this.y + vector.y,
this.z + vector.z
);
}
subtract(vector) {
return new Vector3(
this.x - vector.x,
this.y - vector.y,
this.z - vector.z
);
}
multiply(vector) {
return new Vector3(
this.x * vector.x,
this.y * vector.y,
this.z * vector.z
);
}
multiplyScalar(value) {
this.x *= value;
this.y *= value;
this.z *= value;
}
devideScalar(value) {
this.x /= value;
this.y /= value;
this.z /= value;
}
dot(vector) {
return this.x * vector.x + this.y * vector.y + this.z * vector.z;
}
distance(vector) {
return this.subtract(vector).magnitude();
}
clone() {
return new Vector3(this.x, this.y, this.z);
}
}
const fromString = (str) => {
let tokens = str.split(",");
return new Vector3(...tokens.map(token => parseFloat(token)));
}
const fromDegrees = (lng, lat, height) => {
lng = angle.degreesToRadians(lng);
lat = angle.degreesToRadians(lat);
let radiiSquared = new Vector3(6378137.0 * 6378137.0, 6378137.0 * 6378137.0, 6356752.3142451793 * 6356752.3142451793);
let cosLatitude = Math.cos(lat);
let scratchN = new Vector3(cosLatitude * Math.cos(lng), cosLatitude * Math.sin(lng), Math.sin(lat));
scratchN.normalize();
let scratchK = radiiSquared.multiply(scratchN);
let gamma = Math.sqrt(scratchN.dot(scratchK));
scratchK.devideScalar(gamma);
scratchN.multiplyScalar(height);
return scratchK.add(scratchN);
}
module.exports = { Vector3, fromString, fromDegrees };
\ No newline at end of file
doctype xml
ps:Capabilities(xmlns:ps="http://www.opengis.net/3dps/1.0" xmlns:ows="http://www.opengis.net/ows/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/3dps/1.0 ../../../schema/3dpResp.xsd" version="1.0.0")
ows:ServiceIdentification
ows:Title HFT Stuttagrt 3DPS Implementation
ows:ServiceTypeVersion 1.0
ows:ServiceType(codeSpace="OGC") 3DPS
ows:OperationsMetadata
ows:Operation(name="GetScene")
Contents
each layer in layers
Layer
Title(xml:lang="en-us")= layer.name
Identifier(codeSpace="http://www.opengis.net/3dps/1.0")= layer.id
ows:BoundingBox
ows:LowerCorner= `${layer.boundingbox[0]} ${layer.boundingbox[1]}`
ows:UpperCorner= `${layer.boundingbox[2]} ${layer.boundingbox[3]}`
AvailbaleCRS= layer.CRS
doctype xml
ExceptionReport(xmlns="http://www.opengis.net/ows/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ows/2.0 owsExceptionReport.xsd" version="1.0.0" xml:lang="en")
Exception(exceptionCode=`${exceptionCode}` locator=`${locator}`)
\ No newline at end of file
doctype html
html(lang="en")
head
style(type="text/css").
body {
font-family: monospace;
}
.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
title HFT Stuttgart 3DPS
body
div(class="centered")
pre= art
p This is an impementation of the 3D Portrayal Service.
p For more details visit #[a( href="https://gitlab.com/tomeof/node-3dps-extended", target="_blank" ) https://gitlab.com/tomeof/node-3dps-extended]
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