diff --git a/flask_project/campaign_manager/templates/campaign_detail.html b/flask_project/campaign_manager/templates/campaign_detail.html index 93f468fc6..d994053af 100644 --- a/flask_project/campaign_manager/templates/campaign_detail.html +++ b/flask_project/campaign_manager/templates/campaign_detail.html @@ -200,7 +200,9 @@

-
+
+ {% include 'campaign_detail/map.html' %} +
@@ -354,6 +356,51 @@

}); } + function addMapElements(activeType) { + var url_vt = "{{ s3_campaign_url | safe }}/render/" + activeType + "/tiles/{z}/{x}/{y}.pbf"; + + map.addSource('tiles', { + "type": "vector", + "minzoom": 10, + "maxzoom": 17, + "tiles": [url_vt] + }); + + map.addLayer({ + "id": "campaign-polygons", + "type": "fill", + "source": "tiles", + "source-layer": "campaign", + "filter": ["in", "$type", "Polygon"], + "paint": { + "fill-color": ['get', 'completeness_color'], + "fill-opacity": 1 + } + }); + map.addLayer({ + "id": "campaign-lines", + "type": "line", + "source": "tiles", + "source-layer": "campaign", + "filter": ["in", "$type", "LineString"], + "paint": { + "line-color": ['get', 'completeness_color'], + "line-opacity": 0.7 + } + }); + map.addLayer({ + "id": "campaign-points", + "type": "circle", + "source": "tiles", + "source-layer": "campaign", + "filter": ["in", "$type", "Point"], + "paint": { + "circle-color": ['get', 'completeness_color'], + } + }); + + } + function loadType(activeType, s3CampaignUrl) { activeType = activeType.replace(/ /g, '_') var typeUrl = getTypeUrl(activeType, s3CampaignUrl); @@ -379,23 +426,28 @@

$('.content-wrapper').html(data); }); - $.get({ - url: typeUrl + 'map.html', - cache: false - }).then(function(data) { - $('.map-wrapper').html(data) - }); - - $.get({ - url: typeUrl + 'errors.html', - cache: false - }).then(function(data) { - $('.errors-wrapper').html(data) - }); + $.get({ + url: typeUrl + 'errors.html', + cache: false + }).then(function(data) { + $('.errors-wrapper').html(data) + }); + + window.setTimeout(function() { + setMapHeight(); + }, 2000); + + // Load data into map. + let source = map.getSource('tiles'); + + if (typeof source !== 'undefined') { + map.removeLayer("campaign-polygons"); + map.removeLayer("campaign-lines"); + map.removeLayer("campaign-points"); + map.removeSource('tiles'); + } - window.setTimeout(function() { - setMapHeight(); - }, 2000); + addMapElements(activeType); } diff --git a/flask_project/campaign_manager/templates/campaign_detail/map.html b/flask_project/campaign_manager/templates/campaign_detail/map.html new file mode 100644 index 000000000..28e14d1ab --- /dev/null +++ b/flask_project/campaign_manager/templates/campaign_detail/map.html @@ -0,0 +1,136 @@ +
+
+ Completeness
+ 100%
+ 75%
+ 50%
+ 25%
+ 0%
+
+
+ + \ No newline at end of file diff --git a/flask_project/campaign_manager/utilities.py b/flask_project/campaign_manager/utilities.py index a2fd15741..b292a7192 100644 --- a/flask_project/campaign_manager/utilities.py +++ b/flask_project/campaign_manager/utilities.py @@ -24,7 +24,9 @@ from campaign_manager.models.survey import Survey from glob import glob +from datetime import datetime +import xml.etree.ElementTree as ET def module_path(*args): """Get an absolute path for a file that is relative to the root. @@ -344,3 +346,38 @@ def get_contribs(url, ctype): data = [item for sublist in data for item in sublist] return data + + +def geojson_to_gpx(geojson): + root = ET.Element( + "gpx", + attrib=dict( + xmlns="http://www.topografix.com/GPX/1/1", + version="1.1", + creator="HOT MapCampaigner", + ), + ) + # Create GPX Metadata element + metadata = ET.Element("metadata") + link = ET.SubElement( + metadata, + "link", + attrib={'href': "https://github.com/hotosm/mapcampaigner"}, + ) + ET.SubElement(link, "text").text = "HOT MapCampaigner" + ET.SubElement(metadata, "time").text = datetime.today().isoformat() + root.append(metadata) + # Create trk element + trk = ET.Element("trk") + root.append(trk) + + trkseg = ET.SubElement(trk, "trkseg") + for coord in geojson['geometry']['coordinates'][0]: + coord_dict = dict(lon=str(coord[0]), lat=str(coord[1])) + ET.SubElement(trkseg, "trkpt", attrib=coord_dict,) + + # Append wpt elements to end of doc + wpt = ET.Element("wpt", attrib=coord_dict) + root.append(wpt) + + return ET.tostring(root, encoding="utf8") diff --git a/flask_project/campaign_manager/views.py b/flask_project/campaign_manager/views.py index 550c1b0b5..f2f0f4ad5 100644 --- a/flask_project/campaign_manager/views.py +++ b/flask_project/campaign_manager/views.py @@ -1,3 +1,4 @@ +import base64 import csv import inspect import json @@ -46,7 +47,7 @@ from campaign_manager.data_providers.overpass_provider import OverpassProvider from reporter import config from campaign_manager.utilities import ( - load_osm_document_cached, get_contribs + load_osm_document_cached, get_contribs, geojson_to_gpx ) from reporter import LOGGER from reporter.static_files import static_file @@ -54,6 +55,7 @@ from xml.sax.saxutils import escape + try: from secret import OAUTH_CONSUMER_KEY, OAUTH_SECRET except ImportError: @@ -437,6 +439,25 @@ def participate(): abort(404) +@campaign_manager.route('/gpx/', methods=['GET']) +def generate_gpx(json_data): + # decoding to geojson + try: + decoded_json = base64.b64decode(json_data).decode('utf-8') + except UnicodeDecodeError: + abort(400) + + geojson = json.loads(decoded_json) + xml_gpx = geojson_to_gpx(geojson) + + resp = Response(xml_gpx, mimetype='text/xml', status=200) + + # Disable CORS. + resp.headers['Access-Control-Allow-Origin'] = '*' + + return resp + + @campaign_manager.route('/generate_josm', methods=['POST']) def generate_josm(): """Get overpass xml data from ids store it to temporary folder."""