Added drawing and saving objects on the map
This commit is contained in:
parent
b521402aa0
commit
4d31ae22ea
@ -12,11 +12,21 @@ http {
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
# server_name yourdomain.com www.yourdomain.com;
|
||||
|
||||
# Redirect all HTTP requests to HTTPS
|
||||
return 301 https://$host$request_uri;
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
# server_name yourdomain.com www.yourdomain.com;
|
||||
|
||||
|
||||
ssl_certificate /certs/cert.pem;
|
||||
ssl_certificate_key /certs/key.pem;
|
||||
|
29
sar_calls.py
29
sar_calls.py
@ -240,3 +240,32 @@ def get_gpx(gpx_id):
|
||||
gpx_file = GPSTrack.query.get_or_404(gpx_id)
|
||||
return Response(gpx_file.gpx_data, mimetype='application/gpx+xml',
|
||||
headers={'Content-Disposition': 'attachment;filename=' + gpx_file.file_name + '.gpx'})
|
||||
|
||||
|
||||
|
||||
@app.route('/save_track', methods=['POST'])
|
||||
@login_required
|
||||
def save_track():
|
||||
# Get the track data from the POST request
|
||||
track_data = request.form.get('track_data') # Replace with the actual field name for the track data
|
||||
|
||||
# Get the track name and comment from the POST request
|
||||
track_name = request.form.get('track_name')
|
||||
track_comment = request.form.get('track_comment')
|
||||
sar_id = request.form.get('sar_call_id')
|
||||
|
||||
|
||||
# Create a new Comment instance associated with the track and save it to the database
|
||||
new_comment = Comment(sar_call_id=sar_id, user_id=current_user.id, text=track_comment)
|
||||
# comment = Comment(text=text, user_id=current_user.id, sar_call_id=sar_call_id)
|
||||
|
||||
db.session.add(new_comment)
|
||||
db.session.commit()
|
||||
print ("new comment id={}", new_comment.id)
|
||||
|
||||
# Create a new GPXTrack instance and save it to the database
|
||||
new_track = GPSTrack(comment_id=new_comment.id, sar_call_id=sar_id, file_name=track_name, gpx_data=track_data)
|
||||
db.session.add(new_track)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify(success=True, message="Track saved successfully")
|
||||
|
547
static/togpx.js
Normal file
547
static/togpx.js
Normal file
@ -0,0 +1,547 @@
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.togpx = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
var JXON = require("jxon");
|
||||
JXON.config({attrPrefix: '@'});
|
||||
|
||||
function togpx( geojson, options ) {
|
||||
options = (function (defaults, options) {
|
||||
for (var k in defaults) {
|
||||
if (options.hasOwnProperty(k))
|
||||
defaults[k] = options[k];
|
||||
}
|
||||
return defaults;
|
||||
})({
|
||||
creator: "togpx",
|
||||
metadata: undefined,
|
||||
featureTitle: get_feature_title,
|
||||
featureDescription: get_feature_description,
|
||||
featureLink: undefined,
|
||||
featureCoordTimes: get_feature_coord_times,
|
||||
}, options || {});
|
||||
|
||||
// is featureCoordTimes is a string -> look for the specified property
|
||||
if (typeof options.featureCoordTimes === 'string') {
|
||||
var customTimesFieldKey = options.featureCoordTimes;
|
||||
options.featureCoordTimes = function (feature) {
|
||||
return feature.properties[customTimesFieldKey];
|
||||
}
|
||||
}
|
||||
|
||||
function get_feature_title(props) {
|
||||
// a simple default heuristic to determine a title for a given feature
|
||||
// uses a nested `tags` object or the feature's `properties` if present
|
||||
// and then searchs for the following properties to construct a title:
|
||||
// `name`, `ref`, `id`
|
||||
if (!props) return "";
|
||||
if (typeof props.tags === "object") {
|
||||
var tags_title = get_feature_title(props.tags);
|
||||
if (tags_title !== "")
|
||||
return tags_title;
|
||||
}
|
||||
if (props.name)
|
||||
return props.name;
|
||||
if (props.ref)
|
||||
return props.ref;
|
||||
if (props.id)
|
||||
return props.id;
|
||||
return "";
|
||||
}
|
||||
function get_feature_description(props) {
|
||||
// constructs a description for a given feature
|
||||
// uses a nested `tags` object or the feature's `properties` if present
|
||||
// and then concatenates all properties to construct a description.
|
||||
if (!props) return "";
|
||||
if (typeof props.tags === "object")
|
||||
return get_feature_description(props.tags);
|
||||
var res = "";
|
||||
for (var k in props) {
|
||||
if (typeof props[k] === "object")
|
||||
continue;
|
||||
res += k+"="+props[k]+"\n";
|
||||
}
|
||||
return res.substr(0,res.length-1);
|
||||
}
|
||||
function get_feature_coord_times(feature) {
|
||||
if (!feature.properties) return null;
|
||||
return feature.properties.times || feature.properties.coordTimes || null;
|
||||
}
|
||||
function add_feature_link(o, f) {
|
||||
if (options.featureLink)
|
||||
o.link = { "@href": options.featureLink(f.properties) }
|
||||
}
|
||||
// make gpx object
|
||||
var gpx = {"gpx": {
|
||||
"@xmlns":"http://www.topografix.com/GPX/1/1",
|
||||
"@xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
|
||||
"@xsi:schemaLocation":"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd",
|
||||
"@version":"1.1",
|
||||
"metadata": null,
|
||||
"wpt": [],
|
||||
"trk": [],
|
||||
}};
|
||||
if (options.creator)
|
||||
gpx.gpx["@creator"] = options.creator;
|
||||
if (options.metadata)
|
||||
gpx.gpx["metadata"] = options.metadata;
|
||||
else
|
||||
delete options.metadata;
|
||||
|
||||
var features;
|
||||
if (geojson.type === "FeatureCollection")
|
||||
features = geojson.features;
|
||||
else if (geojson.type === "Feature")
|
||||
features = [geojson];
|
||||
else
|
||||
features = [{type:"Feature", properties: {}, geometry: geojson}];
|
||||
features.forEach(function mapFeature(f) {
|
||||
switch (f.geometry.type) {
|
||||
// POIs
|
||||
case "Point":
|
||||
case "MultiPoint":
|
||||
var coords = f.geometry.coordinates;
|
||||
if (f.geometry.type == "Point") coords = [coords];
|
||||
coords.forEach(function (coordinates) {
|
||||
o = {
|
||||
"@lat": coordinates[1],
|
||||
"@lon": coordinates[0],
|
||||
"name": options.featureTitle(f.properties),
|
||||
"desc": options.featureDescription(f.properties)
|
||||
};
|
||||
if (coordinates[2] !== undefined) {
|
||||
o.ele = coordinates[2];
|
||||
}
|
||||
add_feature_link(o,f);
|
||||
gpx.gpx.wpt.push(o);
|
||||
});
|
||||
break;
|
||||
// LineStrings
|
||||
case "LineString":
|
||||
case "MultiLineString":
|
||||
var coords = f.geometry.coordinates;
|
||||
var times = options.featureCoordTimes(f);
|
||||
if (f.geometry.type == "LineString") coords = [coords];
|
||||
o = {
|
||||
"name": options.featureTitle(f.properties),
|
||||
"desc": options.featureDescription(f.properties)
|
||||
};
|
||||
add_feature_link(o,f);
|
||||
o.trkseg = [];
|
||||
coords.forEach(function(coordinates) {
|
||||
var seg = {trkpt: []};
|
||||
coordinates.forEach(function(c, i) {
|
||||
var o = {
|
||||
"@lat": c[1],
|
||||
"@lon":c[0]
|
||||
};
|
||||
if (c[2] !== undefined) {
|
||||
o.ele = c[2];
|
||||
}
|
||||
if (times && times[i]) {
|
||||
o.time = times[i];
|
||||
}
|
||||
seg.trkpt.push(o);
|
||||
});
|
||||
o.trkseg.push(seg);
|
||||
});
|
||||
gpx.gpx.trk.push(o);
|
||||
break;
|
||||
// Polygons / Multipolygons
|
||||
case "Polygon":
|
||||
case "MultiPolygon":
|
||||
o = {
|
||||
"name": options.featureTitle(f.properties),
|
||||
"desc": options.featureDescription(f.properties)
|
||||
};
|
||||
add_feature_link(o,f);
|
||||
o.trkseg = [];
|
||||
var coords = f.geometry.coordinates;
|
||||
var times = options.featureCoordTimes(f);
|
||||
if (f.geometry.type == "Polygon") coords = [coords];
|
||||
coords.forEach(function(poly) {
|
||||
poly.forEach(function(ring) {
|
||||
var seg = {trkpt: []};
|
||||
var i = 0;
|
||||
ring.forEach(function(c) {
|
||||
var o = {
|
||||
"@lat": c[1],
|
||||
"@lon":c[0]
|
||||
};
|
||||
if (c[2] !== undefined) {
|
||||
o.ele = c[2];
|
||||
}
|
||||
if (times && times[i]) {
|
||||
o.time = times[i];
|
||||
}
|
||||
i++;
|
||||
seg.trkpt.push(o);
|
||||
});
|
||||
o.trkseg.push(seg);
|
||||
});
|
||||
});
|
||||
gpx.gpx.trk.push(o);
|
||||
break;
|
||||
case "GeometryCollection":
|
||||
f.geometry.geometries.forEach(function (geometry) {
|
||||
var pseudo_feature = {
|
||||
"properties": f.properties,
|
||||
"geometry": geometry
|
||||
};
|
||||
mapFeature(pseudo_feature);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("warning: unsupported geometry type: "+f.geometry.type);
|
||||
}
|
||||
});
|
||||
gpx_str = JXON.stringify(gpx);
|
||||
return gpx_str;
|
||||
};
|
||||
|
||||
module.exports = togpx;
|
||||
|
||||
},{"jxon":2}],2:[function(require,module,exports){
|
||||
/*
|
||||
* JXON framework - Copyleft 2011 by Mozilla Developer Network
|
||||
*
|
||||
* Revision #1 - September 5, 2014
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/JXON
|
||||
*
|
||||
* This framework is released under the GNU Public License, version 3 or later.
|
||||
* http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
||||
*
|
||||
* small modifications performed by the iD project:
|
||||
* https://github.com/openstreetmap/iD/commits/18aa33ba97b52cacf454e95c65d154000e052a1f/js/lib/jxon.js
|
||||
*
|
||||
* small modifications performed by user @bugreport0
|
||||
* https://github.com/tyrasd/JXON/pull/2/commits
|
||||
*
|
||||
* some additions and modifications by user @igord
|
||||
* https://github.com/tyrasd/JXON/pull/5/commits
|
||||
*
|
||||
* bugfixes and code cleanup by user @laubstein
|
||||
* https://github.com/tyrasd/jxon/pull/32
|
||||
*
|
||||
* adapted for nodejs and npm by @tyrasd (Martin Raifer <tyr.asd@gmail.com>)
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory(window));
|
||||
} else if (typeof exports === 'object') {
|
||||
if (typeof window === 'object' && window.DOMImplementation && window.XMLSerializer && window.DOMParser) {
|
||||
// Browserify. hardcode usage of browser's own XMLDom implementation
|
||||
// see https://github.com/tyrasd/jxon/issues/18
|
||||
|
||||
module.exports = factory(window);
|
||||
} else {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
|
||||
module.exports = factory(require('xmldom'), true);
|
||||
}
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
|
||||
root.JXON = factory(window);
|
||||
}
|
||||
}(this, function(xmlDom, isNodeJs) {
|
||||
var opts = {
|
||||
valueKey: '_',
|
||||
attrKey: '$',
|
||||
attrPrefix: '$',
|
||||
lowerCaseTags: false,
|
||||
trueIsEmpty: false,
|
||||
autoDate: false,
|
||||
ignorePrefixedNodes: false,
|
||||
parseValues: false
|
||||
};
|
||||
var aCache = [];
|
||||
var rIsNull = /^\s*$/;
|
||||
var rIsBool = /^(?:true|false)$/i;
|
||||
var DOMParser;
|
||||
|
||||
return new (function() {
|
||||
|
||||
this.config = function(cfg) {
|
||||
for (var k in cfg) {
|
||||
|
||||
opts[k] = cfg[k];
|
||||
}
|
||||
if (opts.parserErrorHandler) {
|
||||
DOMParser = new xmlDom.DOMParser({
|
||||
errorHandler: opts.parserErrorHandler,
|
||||
locator: {}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function parseText(sValue) {
|
||||
if (!opts.parseValues) {
|
||||
return sValue;
|
||||
}
|
||||
|
||||
if (rIsNull.test(sValue)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rIsBool.test(sValue)) {
|
||||
return sValue.toLowerCase() === 'true';
|
||||
}
|
||||
|
||||
if (isFinite(sValue)) {
|
||||
return parseFloat(sValue);
|
||||
}
|
||||
|
||||
if (opts.autoDate && isFinite(Date.parse(sValue))) {
|
||||
return new Date(sValue);
|
||||
}
|
||||
|
||||
return sValue;
|
||||
}
|
||||
function EmptyTree() {
|
||||
}
|
||||
EmptyTree.prototype.toString = function() {
|
||||
return 'null';
|
||||
};
|
||||
|
||||
EmptyTree.prototype.valueOf = function() {
|
||||
return null;
|
||||
};
|
||||
|
||||
function objectify(vValue) {
|
||||
return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
|
||||
}
|
||||
|
||||
function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
|
||||
var CDATA = 4,
|
||||
TEXT = 3,
|
||||
ELEMENT = 1,
|
||||
nLevelStart = aCache.length,
|
||||
bChildren = oParentNode.hasChildNodes(),
|
||||
bAttributes = oParentNode.nodeType === oParentNode.ELEMENT_NODE && oParentNode.hasAttributes(),
|
||||
bHighVerb = Boolean(nVerb & 2),
|
||||
nLength = 0,
|
||||
sCollectedTxt = '',
|
||||
vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ (opts.trueIsEmpty ? true : ''),
|
||||
sProp,
|
||||
vContent;
|
||||
|
||||
if (bChildren) {
|
||||
for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
|
||||
|
||||
oNode = oParentNode.childNodes.item(nItem);
|
||||
if (oNode.nodeType === CDATA) {
|
||||
sCollectedTxt += oNode.nodeValue;
|
||||
} /* nodeType is "CDATASection" (4) */
|
||||
else if (oNode.nodeType === TEXT) {
|
||||
sCollectedTxt += oNode.nodeValue.trim();
|
||||
} /* nodeType is "Text" (3) */
|
||||
else if (oNode.nodeType === ELEMENT && !(opts.ignorePrefixedNodes && oNode.prefix)) {
|
||||
aCache.push(oNode);
|
||||
}
|
||||
/* nodeType is "Element" (1) */
|
||||
}
|
||||
}
|
||||
|
||||
var nLevelEnd = aCache.length,
|
||||
vBuiltVal = parseText(sCollectedTxt);
|
||||
|
||||
if (!bHighVerb && (bChildren || bAttributes)) {
|
||||
vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
|
||||
}
|
||||
|
||||
for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
|
||||
|
||||
sProp = aCache[nElId].nodeName;
|
||||
if (opts.lowerCaseTags) {
|
||||
sProp = sProp.toLowerCase();
|
||||
}
|
||||
|
||||
vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
|
||||
if (vResult.hasOwnProperty(sProp)) {
|
||||
if (vResult[sProp].constructor !== Array) {
|
||||
vResult[sProp] = [vResult[sProp]];
|
||||
}
|
||||
|
||||
vResult[sProp].push(vContent);
|
||||
} else {
|
||||
vResult[sProp] = vContent;
|
||||
|
||||
nLength++;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAttributes) {
|
||||
var nAttrLen = oParentNode.attributes.length,
|
||||
sAPrefix = bNesteAttr ? '' : opts.attrPrefix,
|
||||
oAttrParent = bNesteAttr ? {} : vResult;
|
||||
|
||||
for (var oAttrib, oAttribName, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
|
||||
|
||||
oAttrib = oParentNode.attributes.item(nAttrib);
|
||||
|
||||
oAttribName = oAttrib.name;
|
||||
if (opts.lowerCaseTags) {
|
||||
oAttribName = oAttribName.toLowerCase();
|
||||
}
|
||||
|
||||
oAttrParent[sAPrefix + oAttribName] = parseText(oAttrib.value.trim());
|
||||
}
|
||||
|
||||
if (bNesteAttr) {
|
||||
if (bFreeze) {
|
||||
Object.freeze(oAttrParent);
|
||||
}
|
||||
|
||||
vResult[opts.attrKey] = oAttrParent;
|
||||
|
||||
nLength -= nAttrLen - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
|
||||
vResult[opts.valueKey] = vBuiltVal;
|
||||
} else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
|
||||
vResult = vBuiltVal;
|
||||
}
|
||||
if (bFreeze && (bHighVerb || nLength > 0)) {
|
||||
Object.freeze(vResult);
|
||||
}
|
||||
|
||||
aCache.length = nLevelStart;
|
||||
|
||||
return vResult;
|
||||
}
|
||||
function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
|
||||
var vValue,
|
||||
oChild,
|
||||
elementNS;
|
||||
|
||||
if (oParentObj.constructor === String || oParentObj.constructor === Number || oParentObj.constructor === Boolean) {
|
||||
oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 or 1 */
|
||||
if (oParentObj === oParentObj.valueOf()) {
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (oParentObj.constructor === Date) {
|
||||
oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toISOString()));
|
||||
}
|
||||
for (var sName in oParentObj) {
|
||||
|
||||
vValue = oParentObj[sName];
|
||||
if ( vValue === undefined ) {
|
||||
continue;
|
||||
}
|
||||
if ( vValue === null ) {
|
||||
vValue = {};
|
||||
}
|
||||
|
||||
if (isFinite(sName) || vValue instanceof Function) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* verbosity level is 0 */
|
||||
if (sName === opts.valueKey) {
|
||||
if (vValue !== null && vValue !== true) {
|
||||
oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toISOString() : String(vValue)));
|
||||
}
|
||||
|
||||
} else if (sName === opts.attrKey) { /* verbosity level is 3 */
|
||||
for (var sAttrib in vValue) {
|
||||
oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
|
||||
}
|
||||
} else if (sName === opts.attrPrefix + 'xmlns') {
|
||||
if (isNodeJs) {
|
||||
oParentEl.setAttribute(sName.slice(1), vValue);
|
||||
}
|
||||
// do nothing: special handling of xml namespaces is done via createElementNS()
|
||||
} else if (sName.charAt(0) === opts.attrPrefix) {
|
||||
oParentEl.setAttribute(sName.slice(1), vValue);
|
||||
} else if (vValue.constructor === Array) {
|
||||
for (var nItem in vValue) {
|
||||
if (!vValue.hasOwnProperty(nItem)) continue;
|
||||
elementNS = (vValue[nItem] && vValue[nItem][opts.attrPrefix + 'xmlns']) || oParentEl.namespaceURI;
|
||||
if (elementNS) {
|
||||
oChild = oXMLDoc.createElementNS(elementNS, sName);
|
||||
} else {
|
||||
oChild = oXMLDoc.createElement(sName);
|
||||
}
|
||||
|
||||
loadObjTree(oXMLDoc, oChild, vValue[nItem] || {});
|
||||
oParentEl.appendChild(oChild);
|
||||
}
|
||||
} else {
|
||||
elementNS = (vValue || {})[opts.attrPrefix + 'xmlns'] || oParentEl.namespaceURI;
|
||||
if (elementNS) {
|
||||
oChild = oXMLDoc.createElementNS(elementNS, sName);
|
||||
} else {
|
||||
oChild = oXMLDoc.createElement(sName);
|
||||
}
|
||||
if (vValue instanceof Object) {
|
||||
loadObjTree(oXMLDoc, oChild, vValue);
|
||||
} else if (vValue !== null && (vValue !== true || !opts.trueIsEmpty)) {
|
||||
oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
|
||||
}
|
||||
oParentEl.appendChild(oChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.xmlToJs = this.build = function(oXMLParent, nVerbosity /* optional */ , bFreeze /* optional */ , bNesteAttributes /* optional */ ) {
|
||||
var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
|
||||
return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
|
||||
};
|
||||
|
||||
this.jsToXml = this.unbuild = function(oObjTree, sNamespaceURI /* optional */ , sQualifiedName /* optional */ , oDocumentType /* optional */ ) {
|
||||
var documentImplementation = xmlDom.document && xmlDom.document.implementation || new xmlDom.DOMImplementation();
|
||||
var oNewDoc = documentImplementation.createDocument(sNamespaceURI || null, sQualifiedName || '', oDocumentType || null);
|
||||
loadObjTree(oNewDoc, oNewDoc.documentElement || oNewDoc, oObjTree);
|
||||
return oNewDoc;
|
||||
};
|
||||
|
||||
this.stringToXml = function(xmlStr) {
|
||||
if (!DOMParser) {
|
||||
DOMParser = new xmlDom.DOMParser();
|
||||
}
|
||||
|
||||
return DOMParser.parseFromString(xmlStr, 'application/xml');
|
||||
};
|
||||
|
||||
this.xmlToString = function(xmlObj) {
|
||||
if (typeof xmlObj.xml !== 'undefined') {
|
||||
return xmlObj.xml;
|
||||
} else {
|
||||
return (new xmlDom.XMLSerializer()).serializeToString(xmlObj);
|
||||
}
|
||||
};
|
||||
|
||||
this.stringToJs = function(str) {
|
||||
var xmlObj = this.stringToXml(str);
|
||||
return this.xmlToJs(xmlObj);
|
||||
};
|
||||
|
||||
this.jsToString = this.stringify = function(oObjTree, sNamespaceURI /* optional */ , sQualifiedName /* optional */ , oDocumentType /* optional */ ) {
|
||||
return this.xmlToString(
|
||||
this.jsToXml(oObjTree, sNamespaceURI, sQualifiedName, oDocumentType)
|
||||
);
|
||||
};
|
||||
|
||||
this.each = function(arr, func, thisArg) {
|
||||
if (arr instanceof Array) {
|
||||
arr.forEach(func, thisArg);
|
||||
} else {
|
||||
[arr].forEach(func, thisArg);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
}
|
||||
|
||||
));
|
||||
|
||||
},{"xmldom":3}],3:[function(require,module,exports){
|
||||
|
||||
},{}]},{},[1])(1)
|
||||
});
|
@ -7,12 +7,18 @@
|
||||
{% block content %}
|
||||
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
|
||||
<!-- Leaflet.draw CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css">
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
|
||||
<!-- Leaflet.draw JS -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
|
||||
<!-- Include leaflet-gpx plugin -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.7.0/gpx.min.js"></script>
|
||||
|
||||
<script src="/static/togpx.js"></script>
|
||||
|
||||
<style>
|
||||
#map {
|
||||
height: 400px;
|
||||
@ -205,6 +211,48 @@
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Button to trigger the modal -->
|
||||
<button id="save-track-button" type="button" class="btn btn-primary" data-toggle="modal"
|
||||
data-target="#saveTrackModal">
|
||||
Save Track
|
||||
</button>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div class="modal fade" id="saveTrackModal" tabindex="-1" role="dialog" aria-labelledby="saveTrackModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="saveTrackModalLabel">Save Track</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="uploadTRKForm" method="post" enctype="multipart/form-data">
|
||||
<div class="modal-body">
|
||||
<!-- Form inputs for track name and comment -->
|
||||
<div class="form-group">
|
||||
<label for="track-name">Track Name:</label>
|
||||
<input type="text" class="form-control" id="track-name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="track-comment">Comment:</label>
|
||||
<textarea class="form-control" id="track-comment"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="sarIdForTRK" name="sarId" value="{{ sar.SARCall.id }}">
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="confirm-save-track">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
// Load GPX file and add to map
|
||||
@ -235,10 +283,45 @@
|
||||
}).addTo(map);
|
||||
L.marker([{{ sar.SARCall.latitude }}, {{ sar.SARCall.longitude }}]).addTo(map);
|
||||
|
||||
|
||||
gpxData.forEach(function (id) {
|
||||
loadAndDisplayGPX(id);
|
||||
});
|
||||
|
||||
// Initialize drawing capabilities
|
||||
var drawnItems = new L.FeatureGroup();
|
||||
map.addLayer(drawnItems);
|
||||
|
||||
var drawControl = new L.Control.Draw({
|
||||
edit: {
|
||||
featureGroup: drawnItems,
|
||||
},
|
||||
draw: {
|
||||
polygon: true,
|
||||
polyline: true,
|
||||
rectangle: false,
|
||||
circle: true,
|
||||
marker: true,
|
||||
},
|
||||
});
|
||||
map.addControl(drawControl);
|
||||
|
||||
|
||||
map.on('draw:created', function (e) {
|
||||
var layer = e.layer;
|
||||
|
||||
// Add the drawn layer to the map and drawnItems feature group
|
||||
map.addLayer(layer);
|
||||
drawnItems.addLayer(layer);
|
||||
|
||||
// Get the GeoJSON representation of the drawn shape
|
||||
var geoJSON = layer.toGeoJSON();
|
||||
|
||||
// Send the GeoJSON data to your server to save as a comment
|
||||
// You can use AJAX or any method you prefer for this part
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@ -314,7 +397,62 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function getTrackData() {
|
||||
// Assuming you are using a Leaflet.Draw FeatureGroup for drawn items
|
||||
var drItems = drawnItems.toGeoJSON();
|
||||
|
||||
// Check if there are any features (tracks) drawn
|
||||
if (drItems.features.length === 0) {
|
||||
return null; // No tracks drawn
|
||||
}
|
||||
|
||||
// Convert GeoJSON to GPX format (you can use a library like togeojson or turf.js)
|
||||
var gpxData = geoJSONToGPX(drItems);
|
||||
|
||||
return gpxData;
|
||||
}
|
||||
|
||||
// Function to convert GeoJSON to GPX
|
||||
function geoJSONToGPX(geoJSONData) {
|
||||
var gpx = togpx(geoJSONData);
|
||||
return gpx;
|
||||
}
|
||||
|
||||
|
||||
// JavaScript to handle the confirmation and capture data
|
||||
document.getElementById('confirm-save-track').addEventListener('click', function () {
|
||||
var trackName = document.getElementById('track-name').value;
|
||||
var trackComment = document.getElementById('track-comment').value;
|
||||
var sarID = document.getElementById('sarIdForGPX').value;
|
||||
|
||||
// Get the track data (replace 'getTrackData()' with your method to obtain the track data)
|
||||
var trackData = getTrackData();
|
||||
|
||||
// Send trackName, trackComment, and trackData to the server
|
||||
$.ajax({
|
||||
url: '/save_track',
|
||||
method: 'POST',
|
||||
data: {
|
||||
track_data: trackData,
|
||||
track_name: trackName,
|
||||
track_comment: trackComment,
|
||||
sar_call_id: sarID,
|
||||
},
|
||||
success: function (response) {
|
||||
// Handle success response
|
||||
alert(response.message);
|
||||
},
|
||||
error: function (error) {
|
||||
// Handle error response
|
||||
alert('An error occurred while saving the track.');
|
||||
}
|
||||
});
|
||||
|
||||
// Close the modal
|
||||
$('#saveTrackModal').modal('hide');
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue
Block a user