Skip to content

Commit

Permalink
Generate gpx
Browse files Browse the repository at this point in the history
  • Loading branch information
JorgeMartinezG committed Nov 1, 2019
1 parent 1811e6a commit e2c2579
Show file tree
Hide file tree
Showing 4 changed files with 264 additions and 18 deletions.
86 changes: 69 additions & 17 deletions flask_project/campaign_manager/templates/campaign_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ <h3 class="detail-campaign-name">
<div class="content-wrapper"></div>
</div>
<div class='col-lg-8'>
<div class='map-wrapper'></div>
<div class='map-wrapper'>
{% include 'campaign_detail/map.html' %}
</div>
</div>

</div>
Expand Down Expand Up @@ -354,6 +356,51 @@ <h3 class="detail-campaign-name">
});
}

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);
Expand All @@ -379,23 +426,28 @@ <h3 class="detail-campaign-name">
$('.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);

}

Expand Down
136 changes: 136 additions & 0 deletions flask_project/campaign_manager/templates/campaign_detail/map.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<div id="campaign-map-detail">
<div class="legend">
<span><b>Completeness</b></span><br/>
<i style="background-color:#00840d"></i> <span>100%</span><br/>
<i style="background-color:#faff00"></i> <span>75%</span><br/>
<i style="background-color:#ffe500"></i> <span>50%</span><br/>
<i style="background-color:#FD9A08"></i> <span>25%</span><br/>
<i style="background-color:#ff0000"></i> <span>0%</span><br/>
</div>
</div>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js'></script>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoid2lsbGUiLCJhIjoicFNVWk5VWSJ9.hluCd0YGvYHNlFi_utWe2g';
map = new mapboxgl.Map({
container: 'campaign-map-detail',
style: 'mapbox://styles/mapbox/light-v10',
center: [0, 0],
zoom: 1
});
map.addControl(new mapboxgl.NavigationControl());

map.on('load', function() {
map.resize();

map.addSource('aoi', {
"type": "geojson",
"data": {{ geometry | safe }}
});
// load aoi geometry layer
map.addLayer({
"id": "aoi-geometry",
"type": "fill",
"source": "aoi",
"paint": {
"fill-color": "#3388ff",
"fill-opacity": 0.1
}
});
map.addLayer({
"id": "aoi-bounds",
"type": "line",
"source": "aoi",
"paint": {
"line-color": "#003278"
}
});

var popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
map.on('mouseenter', 'aoi-bounds', function(e) {
map.getCanvas().style.cursor = 'pointer';
var description = `
<h4>Area</h4>
<div class="layer-group">
<div class="layer-popup-area">
<b>Area:</b> ${e.features[0].properties.area || '-'}
</div>
<div class="layer-popup-area">
<b>Team:</b> ${e.features[0].properties.team || '-'}
</div>
<div class="layer-popup-area">
<b>Status:</b> ${e.features[0].properties.status || '-'}
</div>
</div>
`;
popup.setLngLat(e.lngLat)
.setHTML(description)
.addTo(map);
});

map.on('click', 'aoi-bounds', function (e) {
let base = 'http://www.openstreetmap.org/edit/#disable_features=boundaries';

// Encode geojson within backend endpoint.
let strPolygon = JSON.stringify(e.features[0].toJSON());
let gpx_url = encodeURIComponent(window.location.origin + '/gpx/' + btoa(strPolygon))
window.open(base + '&gpx=' + gpx_url)

});

map.on('mouseleave', 'aoi-bounds', function() {
map.getCanvas().style.cursor = '';
popup.remove();
});
map.fitBounds(extent({{ geometry | safe }}), {padding: 40});
});


["campaign-polygons", "campaign-lines", "campaign-points"].forEach(
layer => {
map.on('mouseenter', layer, () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', layer, () => {
map.getCanvas().style.cursor = '';
});
map.on('click', layer, e => displayPopup(e));
}
);

function displayPopup(e) {
var feature = e.features[0];
var link = `
<a href="https://www.openstreetmap.org/${feature.properties.type}/${feature.properties.id}" target="_blank">
${feature.properties.type} / ${feature.properties.id}
</a>
`;
var content = `${link}<br />`;

var percentage = `<b>Completeness</b>: ${feature.properties.completeness_pct}`;
content += percentage;

if (feature.properties.errors && feature.properties.errors !== "null") {
var errors = `<div style='color:red'><b>Errors</b>: ${feature.properties.errors}</div>`;
content += errors;
}

if (feature.properties.warnings && feature.properties.warnings !== "null") {
var warnings = `<div style='color:orange'><b>warnings</b>: ${feature.properties.warnings}</div>`
content += warnings;
}

var tags = Object.entries(JSON.parse(feature.properties.tags)).map(
i => `<b>${i[0]}</b>: ${i[1]}`
);
tags = tags.join('<br />')
content += `<p><i>${tags}</i></p>`;

new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML(content)
.addTo(map);
}
</script>
37 changes: 37 additions & 0 deletions flask_project/campaign_manager/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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")
23 changes: 22 additions & 1 deletion flask_project/campaign_manager/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
import csv
import inspect
import json
Expand Down Expand Up @@ -46,14 +47,15 @@
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
from campaign_manager.aws import S3Data

from xml.sax.saxutils import escape


try:
from secret import OAUTH_CONSUMER_KEY, OAUTH_SECRET
except ImportError:
Expand Down Expand Up @@ -437,6 +439,25 @@ def participate():
abort(404)


@campaign_manager.route('/gpx/<json_data>', 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."""
Expand Down

0 comments on commit e2c2579

Please sign in to comment.