kopia lustrzana https://github.com/FacilMap/facilmap
572 wiersze
16 KiB
JavaScript
572 wiersze
16 KiB
JavaScript
|
var backend = require("./databaseBackendSequelize");
|
||
|
var listeners = require("./listeners");
|
||
|
var routing = require("./routing");
|
||
|
var utils = require("./utils");
|
||
|
var underscore = require("underscore");
|
||
|
var stream = require("stream");
|
||
|
var Promise = require("promise");
|
||
|
|
||
|
var DEFAULT_TYPES = [
|
||
|
{ name: "Marker", type: "marker", fields: [ { name: "Description", type: "textarea" } ] },
|
||
|
{ name: "Line", type: "line", fields: [ { name: "Description", type: "textarea" } ] }
|
||
|
];
|
||
|
|
||
|
function padIdExists(padId) {
|
||
|
return backend.padIdExists(padId);
|
||
|
}
|
||
|
|
||
|
function getPadData(padId) {
|
||
|
return backend.getPadDataByWriteId(padId).then(function(data) {
|
||
|
if(data != null)
|
||
|
return utils.extend(JSON.parse(JSON.stringify(data)), { writable: true });
|
||
|
|
||
|
return backend.getPadData(padId).then(function(data) {
|
||
|
if(data != null)
|
||
|
return utils.extend(JSON.parse(JSON.stringify(data)), { writable: false, writeId: null });
|
||
|
|
||
|
throw "This pad does not exist.";
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function createPad(data) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(!data.id || data.id.length == 0)
|
||
|
throw "Invalid read-only ID";
|
||
|
if(!data.writeId || data.writeId.length == 0)
|
||
|
throw "Invalid write-only ID";
|
||
|
if(data.id == data.writeId)
|
||
|
throw "Read-only and write-only ID cannot be the same.";
|
||
|
|
||
|
return Promise.all([
|
||
|
padIdExists(data.id).then(function(exists) {
|
||
|
if(exists)
|
||
|
throw "ID '" + data.id + "' is already taken.";
|
||
|
}),
|
||
|
padIdExists(data.writeId).then(function(exists) {
|
||
|
if(exists)
|
||
|
throw "ID '" + data.writeId + "' is already taken.";
|
||
|
})
|
||
|
])
|
||
|
}).then(function() {
|
||
|
return backend.createPad(data);
|
||
|
}).then(function(newData) {
|
||
|
data = newData;
|
||
|
|
||
|
return Promise.all(DEFAULT_TYPES.map(function(it) {
|
||
|
return backend.createType(data.id, it);
|
||
|
}));
|
||
|
}).then(function() {
|
||
|
return utils.extend(JSON.parse(JSON.stringify(data)), { writable: true });
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function updatePadData(padId, data) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(data.id != null && data.id != padId && data.id.length == 0)
|
||
|
throw "Invalid read-only ID";
|
||
|
|
||
|
var existsPromises = [ ];
|
||
|
|
||
|
if(data.id != null && data.id != padId) {
|
||
|
existsPromises.push(padIdExists(data.id).then(function(exists) {
|
||
|
if(exists)
|
||
|
throw "ID '" + data.id + "' is already taken.";
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
if(data.writeId != null) {
|
||
|
existsPromises.push(backend.getPadData(padId).then(function(padData) {
|
||
|
if(data.writeId != padData.writeId) {
|
||
|
if(data.writeId.length == 0)
|
||
|
throw "Invalid write-only ID";
|
||
|
if(data.writeId == (data.id != null ? data.id : padId))
|
||
|
throw "Read-only and write-only ID cannot be the same.";
|
||
|
|
||
|
return padIdExists(data.writeId).then(function(exists) {
|
||
|
if(exists)
|
||
|
throw "ID '" + data.writeId + "' is already taken.";
|
||
|
});
|
||
|
}
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
return Promise.all(existsPromises);
|
||
|
}).then(function() {
|
||
|
return backend.updatePadData(padId, data);
|
||
|
}).then(function(newData) {
|
||
|
listeners.notifyPadListeners(padId, "padData", function(listener) {
|
||
|
var dataClone = JSON.parse(JSON.stringify(newData));
|
||
|
if(!listener.writable)
|
||
|
dataClone.writeId = null;
|
||
|
|
||
|
return dataClone;
|
||
|
});
|
||
|
|
||
|
if(data.id != null && data.id != padId)
|
||
|
listeners.changePadId(padId, data.id);
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getViews(padId) {
|
||
|
return backend.getViews(padId);
|
||
|
}
|
||
|
|
||
|
function createView(padId, data) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(data.name == null || data.name.trim().length == 0)
|
||
|
throw "No name provided.";
|
||
|
|
||
|
return backend.createView(padId, data);
|
||
|
}).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "view", data);
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function updateView(viewId, data) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(data.name == null || data.name.trim().length == 0)
|
||
|
throw "No name provided.";
|
||
|
|
||
|
return backend.updateView(viewId, data);
|
||
|
}).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "view", data);
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function deleteView(viewId) {
|
||
|
return backend.deleteView(viewId).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "deleteView", { id: data.id });
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getTypes(padId) {
|
||
|
return backend.getTypes(padId);
|
||
|
}
|
||
|
|
||
|
function createType(padId, data) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(data.name == null || data.name.trim().length == 0)
|
||
|
throw "No name provided.";
|
||
|
|
||
|
return backend.createType(padId, data);
|
||
|
}).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "type", data);
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function updateType(typeId, data) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(data.name == null || data.name.trim().length == 0)
|
||
|
throw "No name provided.";
|
||
|
|
||
|
return backend.updateType(typeId, data);
|
||
|
}).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "type", data);
|
||
|
|
||
|
return _updateObjectStyles(data.type == "line" ? backend.getPadLinesByType(data.padId, typeId) : backend.getPadMarkersByType(data.padId, typeId), data.type == "line").then(function() {
|
||
|
return data;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function _optionsToObj(options, idx) {
|
||
|
var ret = { };
|
||
|
if(options) {
|
||
|
for(var i=0; i<options.length; i++) {
|
||
|
ret[options[i].key] = options[i][idx];
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
function deleteType(typeId) {
|
||
|
return backend.isTypeUsed(typeId).then(function(isUsed) {
|
||
|
if(isUsed)
|
||
|
throw "This type is in use.";
|
||
|
|
||
|
return backend.deleteType(typeId);
|
||
|
}).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "deleteType", { id: data.id });
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getPadMarkers(padId, bbox) {
|
||
|
return backend.getPadMarkers(padId, bbox);
|
||
|
}
|
||
|
|
||
|
function createMarker(padId, data) {
|
||
|
return backend.createMarker(padId, data).then(function(data) {
|
||
|
listeners.notifyPadListeners(padId, "marker", _getMarkerDataFunc(data));
|
||
|
|
||
|
return _updateObjectStyles(data, false).then(function() {
|
||
|
return data;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function updateMarker(markerId, data) {
|
||
|
return _updateMarker(markerId, data).then(function(data) {
|
||
|
return _updateObjectStyles(data, false).then(function() {
|
||
|
return data;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function _updateMarker(markerId, data) {
|
||
|
return backend.updateMarker(markerId, data).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "marker", _getMarkerDataFunc(data));
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function deleteMarker(markerId) {
|
||
|
return backend.deleteMarker(markerId).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "deleteMarker", { id: data.id });
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function _updateObjectStyles(objectStream, isLine) {
|
||
|
if(!(objectStream instanceof stream.Readable))
|
||
|
objectStream = new utils.ArrayStream([ objectStream ]);
|
||
|
|
||
|
var types = { };
|
||
|
return utils.streamEachPromise(objectStream, function(object) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(!types[object.typeId]) {
|
||
|
return backend.getType(object.typeId).then(function(type) {
|
||
|
if(type == null)
|
||
|
throw "Type "+object.typeId+" does not exist.";
|
||
|
|
||
|
types[object.typeId] = type;
|
||
|
});
|
||
|
}
|
||
|
}).then(function() {
|
||
|
return Promise.all(types[object.typeId].fields.map(function(field) {
|
||
|
return Promise.resolve().then(function() {
|
||
|
if(field.type == "dropdown" && (field.controlColour || (isLine && field.controlWidth))) {
|
||
|
var _find = function(value) {
|
||
|
for(var j=0; j<(field.options || []).length; j++) {
|
||
|
if(field.options[j].key == value)
|
||
|
return field.options[j];
|
||
|
}
|
||
|
return null;
|
||
|
};
|
||
|
|
||
|
var option = _find(object.data[field.name]) || _find(field.default) || field.options[0];
|
||
|
|
||
|
var update = { };
|
||
|
if(option != null) {
|
||
|
if(field.controlColour && object.colour != option.colour)
|
||
|
update.colour = option.colour;
|
||
|
if(isLine && field.controlWidth && object.width != option.width)
|
||
|
update.width = option.width;
|
||
|
}
|
||
|
|
||
|
utils.extend(object, update);
|
||
|
|
||
|
if(Object.keys(update).length > 0 && object.id) // Objects from getLineTemplate() do not have an ID
|
||
|
return (isLine ? _updateLine : _updateMarker)(object.id, update);
|
||
|
else
|
||
|
return Promise.resolve();
|
||
|
}
|
||
|
});
|
||
|
}));
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getPadLines(padId) {
|
||
|
return backend.getPadLines(padId);
|
||
|
}
|
||
|
|
||
|
function getPadLinesWithPoints(padId, bboxWithZoom) {
|
||
|
return utils.filterStreamPromise(backend.getPadLines(padId), function(data) {
|
||
|
return _getLinePoints(data.id, bboxWithZoom).then(function(trackPoints) {
|
||
|
data.trackPoints = trackPoints;
|
||
|
return data;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getLineTemplate(data) {
|
||
|
return backend.getLineTemplate(data).then(function(line) {
|
||
|
return _updateObjectStyles(line, true).then(function() {
|
||
|
return line;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function createLine(padId, data) {
|
||
|
var calculateRoutingP = _calculateRouting(data);
|
||
|
|
||
|
var createLineP = calculateRoutingP.then(function() {
|
||
|
return _createLine(padId, data);
|
||
|
});
|
||
|
|
||
|
var setLinePointsP = Promise.all([ calculateRoutingP, createLineP ]).then(function(res) {
|
||
|
return _setLinePoints(padId, res[1].id, res[0]);
|
||
|
});
|
||
|
|
||
|
var updateLineStyleP = createLineP.then(function(lineData) {
|
||
|
return _updateObjectStyles(lineData, true);
|
||
|
});
|
||
|
|
||
|
return Promise.all([ calculateRoutingP, createLineP, setLinePointsP, updateLineStyleP ]).then(function(res) {
|
||
|
return res[1];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function updateLine(lineId, data) {
|
||
|
var originalLineP = backend.getLine(lineId);
|
||
|
|
||
|
var calculateRoutingP = originalLineP.then(function(originalLine) {
|
||
|
if(data.routePoints == null)
|
||
|
data.routePoints = originalLine.routePoints;
|
||
|
|
||
|
if(data.mode == null)
|
||
|
data.mode = originalLine.mode || "";
|
||
|
|
||
|
if((data.mode == "track" && data.trackPoints) || !underscore.isEqual(data.routePoints, originalLine.routePoints) || data.mode != originalLine.mode)
|
||
|
return _calculateRouting(data); // Also sets data.distance and data.time
|
||
|
});
|
||
|
|
||
|
var updateLineP = calculateRoutingP.then(function() {
|
||
|
return _updateLine(lineId, data);
|
||
|
});
|
||
|
|
||
|
var updateLineStyleP = updateLineP.then(function(newLine) {
|
||
|
return _updateObjectStyles(newLine, true); // Modifies res.updateLine
|
||
|
});
|
||
|
|
||
|
var setLinePointsP = Promise.all([ originalLineP, calculateRoutingP ]).then(function(res) {
|
||
|
if(res[1])
|
||
|
return _setLinePoints(res[0].padId, lineId, res[1]);
|
||
|
});
|
||
|
|
||
|
return Promise.all([ originalLineP, calculateRoutingP, updateLineP, updateLineStyleP, setLinePointsP ]).then(function(res) {
|
||
|
return res[2];
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function _createLine(padId, data) {
|
||
|
var dataCopy = utils.extend({ }, data);
|
||
|
delete dataCopy.trackPoints; // They came if mode is track
|
||
|
|
||
|
return backend.createLine(padId, dataCopy).then(function(newData) {
|
||
|
listeners.notifyPadListeners(newData.padId, "line", newData);
|
||
|
|
||
|
return newData;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function _updateLine(lineId, data) {
|
||
|
var dataCopy = utils.extend({ }, data);
|
||
|
delete dataCopy.trackPoints; // They came if mode is track
|
||
|
|
||
|
return backend.updateLine(lineId, dataCopy).then(function(newData) {
|
||
|
listeners.notifyPadListeners(newData.padId, "line", newData);
|
||
|
|
||
|
return newData;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function _setLinePoints(padId, lineId, trackPoints) {
|
||
|
return backend.setLinePoints(lineId, trackPoints).then(function() {
|
||
|
listeners.notifyPadListeners(padId, "linePoints", function(listener) {
|
||
|
return { reset: true, id: lineId, trackPoints : (listener && listener.bbox ? routing.prepareForBoundingBox(trackPoints, listener.bbox) : [ ]) };
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function deleteLine(lineId) {
|
||
|
return backend.deleteLine(lineId).then(function(data) {
|
||
|
return backend.setLinePoints(lineId, [ ]).then(function() {
|
||
|
return data;
|
||
|
});
|
||
|
}).then(function(data) {
|
||
|
listeners.notifyPadListeners(data.padId, "deleteLine", { id: data.id });
|
||
|
|
||
|
return data;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getLinePoints(padId, bboxWithZoom) {
|
||
|
return utils.filterStreamPromise(backend.getPadLines(padId, "id"), function(data) {
|
||
|
return _getLinePoints(data.id, bboxWithZoom).then(function(trackPoints) {
|
||
|
if(trackPoints.length >= 2)
|
||
|
return { id: data.id, trackPoints: trackPoints };
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/*function copyPad(fromPadId, toPadId, callback) {
|
||
|
function _handleStream(stream, next, cb) {
|
||
|
stream.on("data", function(data) {
|
||
|
stream.pause();
|
||
|
cb(data, function() {
|
||
|
stream.resume();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
stream.on("error", next);
|
||
|
stream.on("end", next);
|
||
|
}
|
||
|
|
||
|
async.auto({
|
||
|
fromPadData : function(next) {
|
||
|
backend.getPadData(fromPadId, next);
|
||
|
},
|
||
|
toPadData : function(next) {
|
||
|
getPadData(toPadId, next);
|
||
|
},
|
||
|
padsExist : [ "fromPadData", "toPadData", function(r, next) {
|
||
|
if(!r.fromPadData)
|
||
|
return next(new Error("Pad "+fromPadId+" does not exist."));
|
||
|
if(!r.toPadData.writable)
|
||
|
return next(new Error("Destination pad is read-only."));
|
||
|
|
||
|
toPadId = r.toPadData.id;
|
||
|
|
||
|
next();
|
||
|
}],
|
||
|
copyMarkers : [ "padsExist", function(r, next) {
|
||
|
_handleStream(getPadMarkers(fromPadId, null), next, function(marker, cb) {
|
||
|
createMarker(toPadId, marker, cb);
|
||
|
});
|
||
|
}],
|
||
|
copyLines : [ "padsExist", function(r, next) {
|
||
|
_handleStream(getPadLines(fromPadId), next, function(line, cb) {
|
||
|
async.auto({
|
||
|
createLine : function(next) {
|
||
|
_createLine(toPadId, line, next);
|
||
|
},
|
||
|
getLinePoints : function(next) {
|
||
|
backend.getLinePoints(line.id, next);
|
||
|
},
|
||
|
setLinePoints : [ "createLine", "getLinePoints", function(r, next) {
|
||
|
_setLinePoints(toPadId, r.createLine.id, r.getLinePoints, next);
|
||
|
} ]
|
||
|
}, cb);
|
||
|
});
|
||
|
}],
|
||
|
copyViews : [ "padsExist", function(r, next) {
|
||
|
_handleStream(getViews(fromPadId), next, function(view, cb) {
|
||
|
createView(toPadId, view, function(err, newView) {
|
||
|
if(err)
|
||
|
return cb(err);
|
||
|
|
||
|
if(r.fromPadData.defaultView && r.fromPadData.defaultView.id == view.id && r.toPadData.defaultView == null)
|
||
|
updatePadData(toPadId, { defaultView: newView.id }, cb);
|
||
|
else
|
||
|
cb();
|
||
|
});
|
||
|
});
|
||
|
}]
|
||
|
}, callback);
|
||
|
}*/
|
||
|
|
||
|
function _calculateRouting(line) {
|
||
|
if(line.mode == "track" && line.trackPoints && line.trackPoints.length >= 2) {
|
||
|
line.distance = utils.calculateDistance(line.trackPoints);
|
||
|
line.time = null;
|
||
|
|
||
|
routing._calculateZoomLevels(line.trackPoints);
|
||
|
|
||
|
for(var i=0; i<line.trackPoints.length; i++)
|
||
|
line.trackPoints[i].idx = i;
|
||
|
|
||
|
return Promise.resolve(line.trackPoints);
|
||
|
} else if(line.routePoints && line.routePoints.length >= 2 && line.mode && line.mode != "track") {
|
||
|
return routing.calculateRouting(line.routePoints, line.mode).then(function(routeData) {
|
||
|
line.distance = routeData.distance;
|
||
|
line.time = routeData.time;
|
||
|
for(var i=0; i<routeData.trackPoints.length; i++)
|
||
|
routeData.trackPoints[i].idx = i;
|
||
|
return routeData.trackPoints;
|
||
|
});
|
||
|
} else {
|
||
|
line.distance = utils.calculateDistance(line.routePoints);
|
||
|
line.time = null;
|
||
|
|
||
|
var trackPoints = [ ];
|
||
|
for(var i=0; i<line.routePoints.length; i++) {
|
||
|
trackPoints.push(utils.extend({ }, line.routePoints[i], { zoom: 1, idx: i }));
|
||
|
}
|
||
|
return Promise.resolve(trackPoints);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function _getMarkerDataFunc(marker) {
|
||
|
return function(listener) {
|
||
|
if(!listener || !listener.bbox || !utils.isInBbox(marker, listener.bbox))
|
||
|
return null;
|
||
|
|
||
|
return marker;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function _getLinePoints(lineId, bboxWithZoom) {
|
||
|
return backend.getLinePointsByBbox(lineId, bboxWithZoom).then(function(data) {
|
||
|
// Get one more point outside of the bbox for each segment
|
||
|
var indexes = [ ];
|
||
|
for(var i=0; i<data.length; i++) {
|
||
|
if(i == 0 || data[i-1].idx != data[i].idx-1) // Beginning of segment
|
||
|
indexes.push(data[i].idx-1);
|
||
|
|
||
|
indexes.push(data[i].idx);
|
||
|
|
||
|
if(i == data.length-1 || data[i+1].idx != data[i].idx+1) // End of segment
|
||
|
indexes.push(data[i].idx+1);
|
||
|
}
|
||
|
|
||
|
if(indexes.length == 0)
|
||
|
return [ ];
|
||
|
|
||
|
return backend.getLinePointsByIdx(lineId, indexes);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
connect : backend.connect,
|
||
|
getPadData : getPadData,
|
||
|
padIdExists : padIdExists,
|
||
|
createPad : createPad,
|
||
|
updatePadData : updatePadData,
|
||
|
getViews : getViews,
|
||
|
createView : createView,
|
||
|
updateView : updateView,
|
||
|
deleteView : deleteView,
|
||
|
getTypes : getTypes,
|
||
|
createType : createType,
|
||
|
updateType : updateType,
|
||
|
deleteType : deleteType,
|
||
|
getPadMarkers : getPadMarkers,
|
||
|
createMarker : createMarker,
|
||
|
updateMarker : updateMarker,
|
||
|
deleteMarker : deleteMarker,
|
||
|
getPadLines : getPadLines,
|
||
|
getPadLinesWithPoints : getPadLinesWithPoints,
|
||
|
getLineTemplate : getLineTemplate,
|
||
|
createLine : createLine,
|
||
|
updateLine : updateLine,
|
||
|
deleteLine : deleteLine,
|
||
|
getLinePoints : getLinePoints,
|
||
|
//copyPad : copyPad,
|
||
|
_defaultTypes : DEFAULT_TYPES
|
||
|
};
|