From 9299d74667032a64d6d1cfc2501dff3235cea1c3 Mon Sep 17 00:00:00 2001 From: Sasha Boginsky <41092741+sashadev-sky@users.noreply.github.com> Date: Wed, 3 Jul 2019 18:12:25 -0400 Subject: [PATCH] Connect LDI exports downstream (#782) * update ldi and leaflt-toolbar dependencies get collection group API working custom export action working clean up more cleanup * fix for some /this/ scope issues * selection working * LDI v0.5.2 with correct ordering, timestamp logging * test * removed package-lock.json * completion alert * JSON.parse(data) for returned status * null as non-string * response fix * prompt for scale * change status_url to export_url in test --- app/assets/javascripts/mapknitter/Map.js | 681 +++++++++++++++++----- app/controllers/export_controller.rb | 10 + app/views/layouts/knitter2.html.erb | 1 - app/views/map/leaflet.html.erb | 18 +- config/routes.rb | 1 + package.json | 3 +- test/functional/export_controller_test.rb | 6 + yarn.lock | 40 +- 8 files changed, 565 insertions(+), 195 deletions(-) diff --git a/app/assets/javascripts/mapknitter/Map.js b/app/assets/javascripts/mapknitter/Map.js index 466af4494..c3d4fb373 100644 --- a/app/assets/javascripts/mapknitter/Map.js +++ b/app/assets/javascripts/mapknitter/Map.js @@ -1,21 +1,23 @@ MapKnitter.Map = MapKnitter.Class.extend({ - initialize: function(options) { + initialize: function (options) { this._zoom = options.zoom || 0; this._latlng = L.latLng(options.latlng); this.logged_in = options.logged_in; this.anonymous = options.anonymous; + var mapknitter = this; + L.Icon.Default.imagePath = '/assets/leaflet/dist/images/'; /* Initialize before map in order to add to layers; probably it can be done later too */ - var google = new L.Google("SATELLITE",{ + var google = new L.Google("SATELLITE", { maxZoom: 24, maxNativeZoom: 20, - opacity:0.5 + opacity: 0.5 }); - this._map = new L.map('knitter-map-pane', { + this._map = L.map('knitter-map-pane', { zoomControl: false, }).setView(this._latlng, this._zoom); @@ -23,8 +25,8 @@ MapKnitter.Map = MapKnitter.Class.extend({ map = this._map if (!options.readOnly) { - saveBtn = L.easyButton('fa-check-circle fa-green mk-save', - function() {}, + saveBtn = L.easyButton('fa-check-circle fa-green mk-save', + function () { }, 'Save status', this._map, this @@ -36,49 +38,55 @@ MapKnitter.Map = MapKnitter.Class.extend({ /* Set up basemap and drawing toolbars. */ this.setupMap(); + var exportA = mapknitter.customExportAction(); + + var imgGroup = L.distortableCollection({ + actions: [exportA] + }).addTo(map); + /* Load warpables data via AJAX request. */ this._warpablesUrl = options.warpablesUrl; - this.withWarpables(function(warpables){ - $.each(warpables,function(i,warpable) { + this.withWarpables(function (warpables) { + $.each(warpables, function (i, warpable) { // only already-placed images: if (warpable.nodes.length > 0) { var downloadEl = $('.img-download-' + warpable.id), - imgEl = $('#full-img-' + warpable.id); + imgEl = $('#full-img-' + warpable.id); - downloadEl.click(function() { + downloadEl.click(function () { downloadEl.html(""); - imgEl[0].onload = function() { + imgEl[0].onload = function () { var height = imgEl.height(), - width = imgEl.width(), - nw = map.latLngToContainerPoint(warpable.nodes[0]), - ne = map.latLngToContainerPoint(warpable.nodes[1]), - se = map.latLngToContainerPoint(warpable.nodes[2]), - sw = map.latLngToContainerPoint(warpable.nodes[3]), - offsetX = nw.x, - offsetY = nw.y, - displayedWidth = $('#warpable-img-' + warpable.id).width(), - ratio = width / displayedWidth; + width = imgEl.width(), + nw = map.latLngToContainerPoint(warpable.nodes[0]), + ne = map.latLngToContainerPoint(warpable.nodes[1]), + se = map.latLngToContainerPoint(warpable.nodes[2]), + sw = map.latLngToContainerPoint(warpable.nodes[3]), + offsetX = nw.x, + offsetY = nw.y, + displayedWidth = $('#warpable-img-' + warpable.id).width(), + ratio = width / displayedWidth; nw.x -= offsetX; ne.x -= offsetX; se.x -= offsetX; sw.x -= offsetX; - + nw.y -= offsetY; ne.y -= offsetY; se.y -= offsetY; sw.y -= offsetY; - + warpWebGl( - 'full-img-' + warpable.id, - [ 0, 0, width, 0, width, height, 0, height ], - [ nw.x, nw.y, ne.x, ne.y, se.x, se.y, sw.x, sw.y ], + 'full-img-' + warpable.id, + [0, 0, width, 0, width, height, 0, height], + [nw.x, nw.y, ne.x, ne.y, se.x, se.y, sw.x, sw.y], true // trigger download - ) + ) downloadEl.html(""); @@ -88,24 +96,27 @@ MapKnitter.Map = MapKnitter.Class.extend({ }); - var corners = [ - new L.latLng(warpable.nodes[0].lat, - warpable.nodes[0].lon), - new L.latLng(warpable.nodes[1].lat, - warpable.nodes[1].lon), - new L.latLng(warpable.nodes[3].lat, - warpable.nodes[3].lon), - new L.latLng(warpable.nodes[2].lat, - warpable.nodes[2].lon) + var corners = [ + L.latLng(warpable.nodes[0].lat, + warpable.nodes[0].lon), + L.latLng(warpable.nodes[1].lat, + warpable.nodes[1].lon), + L.latLng(warpable.nodes[3].lat, + warpable.nodes[3].lon), + L.latLng(warpable.nodes[2].lat, + warpable.nodes[2].lon) ]; - var img = new L.DistortableImageOverlay( + var img = L.distortableImageOverlay( warpable.srcmedium, - { + { keymapper: false, + actions: mapknitter.imgActionArray(), corners: corners, mode: 'lock' - }).addTo(window.mapKnitter._map); + }).addTo(map); + + imgGroup.addLayer(img); bounds = bounds.concat(corners); window.mapKnitter._map.fitBounds(bounds); @@ -119,8 +130,9 @@ MapKnitter.Map = MapKnitter.Class.extend({ L.DomEvent.on(img._image, 'click', window.mapKnitter.selectImage, img); img.on('deselect', window.mapKnitter.saveImageIfChanged, img) L.DomEvent.on(img._image, 'dblclick', window.mapKnitter.dblClickImage, img); - L.DomEvent.on(img._image, 'load', function() { - var img = this + L.DomEvent.on(imgGroup, 'layeradd', mapknitter.setupEvents, this); + L.DomEvent.on(img._image, 'load', function () { /* never hitting this?? */ + var img = this; window.mapKnitter.setupToolbar(img) }, img); } @@ -131,7 +143,7 @@ MapKnitter.Map = MapKnitter.Class.extend({ /* Deselect images if you click on the sidebar, * otherwise hotkeys still fire as you type. */ - $('.sidebar').click(function(){ $.each(images,function(i,img){ img.editing.disable() }) }) + $('.sidebar').click(function () { $.each(images, function (i, img) { img.editing.disable() }) }) /* Deselect images if you click on the map. */ //this._map.on('click',function(){ $.each(images,function(i,img){ img.editing.disable() }) }) @@ -139,21 +151,94 @@ MapKnitter.Map = MapKnitter.Class.extend({ //img._image.src = img._image.src.split('_medium').join('') }, + customExportAction: function () { + var action = L.EditAction.extend({ + initialize: function (map, group, options) { + + var use = ''; + var symbol = '' + + options = options || {}; + options.toolbarIcon = { + html: '' + use + symbol + '', + tooltip: 'Export Images' + }; + + L.EditAction.prototype.initialize.call(this, map, group, options); + }, + + addHooks: function () { + var group = this._overlay; + var exportInterval; + + var updateUI = function updateUI(data) { + data = JSON.parse(data); + console.log("in updateui: " + data); + if (data.status == 'complete') clearInterval(exportInterval); + if (data.status == 'complete' && data.jpg.match('.jpg')) alert("Export succeeded. http://export.mapknitter.org/" + data.jpg); + } + + var addUrlToModel = function addUrlToModel(data) { + var statusUrl = "//export.mapknitter.org" + data; + console.log("statusUrl: " + statusUrl); + + // repeatedly fetch the status.json + var updateInterval = function updateInterval() { + exportInterval = setInterval(function intervalUpdater() { + $.ajax(statusUrl + "?" + Date.now(), { // bust cache with timestamp; + type: "GET", + crossDomain: true + }).done(function (data) { + updateUI(data); + }); + }, 3000); + } + + /** + * TODO: update API to say if you pass in a custom `handleStatusUrl` you must also build your own updater + * and also create your own a frequency + * or fix this part + */ + $.ajax({ + url: "/export", + type: 'POST', + data: { status_url: statusUrl } + }).done(function (data) { + console.log('success!! ' + data); + updateInterval(); + }); + } + + + group.startExport({ handleStatusUrl: addUrlToModel, updater: updateUI, scale: prompt("Choose a scale or use the default (cm per pixel):", 100)}); + } + }); + + return action; + }, + + setupEvents: function (e) { + var img = e.layer; + + L.DomEvent.on(img._image, 'mouseup', window.mapKnitter.saveImageIfChanged, img); + L.DomEvent.on(img._image, 'touchend', window.mapKnitter.saveImageIfChanged, img); + }, + /* * Setup toolbar and events */ - setupToolbar: function(img) { + setupToolbar: function (img) { img.on('edit', window.mapKnitter.saveImageIfChanged, img); img.on('delete', window.mapKnitter.deleteImage, img) // Override default delete to add a confirm() - img.on('toolbar:created', - function() { - this.editing.toolbar.options.actions[1].prototype.addHooks = function() { - var map = this._map; - this._overlay.fire('delete'); - } - }, img) + img.on('toolbar:created', + function () { + this.editing.toolbar.options.actions[1].prototype.addHooks = function () { + var map = this._map; + this._overlay.fire('delete'); + } + }, img) L.DomEvent.on(img._image, 'mouseup', window.mapKnitter.saveImageIfChanged, img); L.DomEvent.on(img._image, 'touchend', window.mapKnitter.saveImageIfChanged, img); @@ -162,78 +247,90 @@ MapKnitter.Map = MapKnitter.Class.extend({ /* Add a new, unplaced, but already uploaded image to the map. * and are optional. */ addImage: function(url,id,lat,lng,angle,altitude) { - var img = new L.DistortableImageOverlay( + var img = L.distortableImageOverlay( url, { + actions: this.imgActionArray(), keymapper: false } ); - img.geocoding = { lat: lat, - lng: lng, - altitude: altitude, - angle: angle}; + img.geocoding = { + lat: lat, + lng: lng, + altitude: altitude, + angle: angle + }; images.push(img); img.warpable_id = id img.addTo(map); + + var exportA = this.customExportAction(); + + var imgGroup = L.distortableCollection({ + actions: [exportA] + }).addTo(map); + L.DomEvent.on(img._image, 'click', window.mapKnitter.selectImage, img); img.on('deselect', window.mapKnitter.saveImageIfChanged, img) L.DomEvent.on(img._image, 'dblclick', window.mapKnitter.dblClickImage, img); L.DomEvent.on(img._image, 'load', img.editing.enable, img.editing); - L.DomEvent.on(img._image, 'load', function() { + L.DomEvent.on(img._image, 'load', function () { var img = this - window.mapKnitter.setupToolbar(img) + imgGroup.addLayer(img); + window.mapKnitter.setupToolbar(img); /* use geodata */ if (img.geocoding && img.geocoding.lat) { /* move the image to this newly discovered location */ var center = L.latLngBounds(img._corners).getCenter(), - latBy = img.geocoding.lat-center.lat, - lngBy = img.geocoding.lng-center.lng - - for (var i=0;i<4;i++) { + latBy = img.geocoding.lat - center.lat, + lngBy = img.geocoding.lng - center.lng + + for (var i = 0; i < 4; i++) { img._corners[i].lat += latBy; img._corners[i].lng += lngBy; } img.editing._rotateBy(img.geocoding.angle); - + /* Attempt to convert altitude to scale factor based on Leaflet zoom; * for correction based on altitude we need the original dimensions of the image. * This may work only at sea level unless we factor in ground level. * We may also need to get camera field of view to get this even closer. * We could also fall back to the scale of the last-placed image. */ - if (img.geocoding.altitude && img.geocoding.altitude != 0) { + if (img.geocoding.altitude && img.geocoding.altitude != 0) { var width = img._image.width, height = img._image.height //scale = ( (act_height/img_height) * (act_width/img_width) ) / img.geocoding.altitude; //img.editing._scaleBy(scale); - var elevator = new google.maps.ElevationService(), - lat = mapKnitter._map.getCenter().lat, - lng = mapKnitter._map.getCenter().lng; - - elevator.getElevationForLocations({'locations':[ {lat: lat, lng: lng} ] - },function(results, status) { - console.log("Photo taken from " + img.geocoding.altitude + " meters above sea level"); - console.log("Ground is " + results[0].elevation + " meters above sea level"); - console.log("Photo taken from " + (img.geocoding.altitude-results[0].elevation) + " meters"); - var a = img.geocoding.altitude-results[0].elevation, - fov = 50, - A = fov * (Math.PI/180), - width = 2 * (a / Math.tan(A)) - currentWidth = (img._corners[2].distanceTo(img._corners[1]) + - img._corners[1].distanceTo(img._corners[2])) / 2 - - console.log("Photo should be " + width + " meters wide"); - img.editing._scaleBy(width/currentWidth); - img.fire('update'); + var elevator = new google.maps.ElevationService(), + lat = mapKnitter._map.getCenter().lat, + lng = mapKnitter._map.getCenter().lng; + + elevator.getElevationForLocations({ + 'locations': [{ lat: lat, lng: lng }] + }, function (results, status) { + console.log("Photo taken from " + img.geocoding.altitude + " meters above sea level"); + console.log("Ground is " + results[0].elevation + " meters above sea level"); + console.log("Photo taken from " + (img.geocoding.altitude - results[0].elevation) + " meters"); + var a = img.geocoding.altitude - results[0].elevation, + fov = 50, + A = fov * (Math.PI / 180), + width = 2 * (a / Math.tan(A)) + currentWidth = (img._corners[2].distanceTo(img._corners[1]) + + img._corners[1].distanceTo(img._corners[2])) / 2 + + console.log("Photo should be " + width + " meters wide"); + img.editing._scaleBy(width / currentWidth); + img.fire('update'); - } + } ) } img.fire('update'); - + /* pan the map there too */ window.mapKnitter._map.fitBounds(L.latLngBounds(img._corners)); @@ -243,32 +340,32 @@ MapKnitter.Map = MapKnitter.Class.extend({ return img; }, - geocodeImageFromId: function(dom_id,id,url) { + geocodeImageFromId: function (dom_id, id, url) { window.mapKnitter.geocodeImage( $(dom_id)[0], - function(lat,lng,id,angle,altitude) { + function (lat, lng, id, angle, altitude) { /* Display button to place this image with GPS tags. */ - $('.add-image-gps-'+id).attr('data-lat',lat); - $('.add-image-gps-'+id).attr('data-lng',lng); - if (angle) $('.add-image-gps-'+id).attr('data-angle',angle); - if (altitude) $('.add-image-gps-'+id).attr('data-altitude',altitude); - $('.add-image-gps-'+id).show(); - $('.add-image-gps-'+id).on('click',function() { - $('.add-image-'+id).hide(); + $('.add-image-gps-' + id).attr('data-lat', lat); + $('.add-image-gps-' + id).attr('data-lng', lng); + if (angle) $('.add-image-gps-' + id).attr('data-angle', angle); + if (altitude) $('.add-image-gps-' + id).attr('data-altitude', altitude); + $('.add-image-gps-' + id).show(); + $('.add-image-gps-' + id).on('click', function () { + $('.add-image-' + id).hide(); $('#uploadModal').modal('hide'); window.mapKnitter._map.setZoom(18); window.mapKnitter._map.setView( [$(this).attr('data-lat'), - $(this).attr('data-lng')]); + $(this).attr('data-lng')]); var angle = $(this).attr('data-angle') || 0; var altitude = $(this).attr('data-altitude') || 0; img = window.mapKnitter.addImage(url, - id, - $(this).attr('data-lat'), - $(this).attr('data-lng'), - angle, - altitude); - $('#warpable-'+id+' a').hide(); + id, + $(this).attr('data-lat'), + $(this).attr('data-lng'), + angle, + altitude); + $('#warpable-' + id + ' a').hide(); }) }, id @@ -281,39 +378,39 @@ MapKnitter.Map = MapKnitter.Class.extend({ * Adapting from: https://github.com/publiclab/mapknitter/blob/6e88c7725d3c013f402526289e806b8be4fcc23c/public/cartagen/cartagen.js#L9378 */ - geocodeImage: function(img,fn,id) { - EXIF.getData(img, function() { + geocodeImage: function (img, fn, id) { + EXIF.getData(img, function () { var GPS = EXIF.getAllTags(img) - + /* If the lat/lng is available. */ - if (typeof GPS["GPSLatitude"] !== 'undefined' && typeof GPS["GPSLongitude"] !== 'undefined'){ + if (typeof GPS["GPSLatitude"] !== 'undefined' && typeof GPS["GPSLongitude"] !== 'undefined') { // sadly, encoded in [degrees,minutes,seconds] - var lat = (GPS["GPSLatitude"][0]) + - (GPS["GPSLatitude"][1]/60) + - (GPS["GPSLatitude"][2]/3600); - var lng = (GPS["GPSLongitude"][0]) + - (GPS["GPSLongitude"][1]/60) + - (GPS["GPSLongitude"][2]/3600); - - if (GPS["GPSLatitudeRef"] != "N") lat = lat*-1 - if (GPS["GPSLongitudeRef"] == "W") lng = lng*-1 + var lat = (GPS["GPSLatitude"][0]) + + (GPS["GPSLatitude"][1] / 60) + + (GPS["GPSLatitude"][2] / 3600); + var lng = (GPS["GPSLongitude"][0]) + + (GPS["GPSLongitude"][1] / 60) + + (GPS["GPSLongitude"][2] / 3600); + + if (GPS["GPSLatitudeRef"] != "N") lat = lat * -1 + if (GPS["GPSLongitudeRef"] == "W") lng = lng * -1 } // Attempt to use GPS compass heading; will require // some trig to calc corner points, which you can find below: - var angle = 0; + var angle = 0; // "T" refers to "True north", so -90. if (GPS["GPSImgDirectionRef"] == "T") - angle = (Math.PI / 180) * (GPS.GPSImgDirection["numerator"]/GPS.GPSImgDirection["denominator"] - 90); + angle = (Math.PI / 180) * (GPS.GPSImgDirection["numerator"] / GPS.GPSImgDirection["denominator"] - 90); // "M" refers to "Magnetic north" else if (GPS["GPSImgDirectionRef"] == "M") - angle = (Math.PI / 180) * (GPS.GPSImgDirection["numerator"]/GPS.GPSImgDirection["denominator"] - 90); + angle = (Math.PI / 180) * (GPS.GPSImgDirection["numerator"] / GPS.GPSImgDirection["denominator"] - 90); else console.log("No compass data found"); - console.log("Orientation:",GPS["Orientation"]) + console.log("Orientation:", GPS["Orientation"]) /* If there is orientation data -- i.e. landscape/portrait etc */ if (GPS["Orientation"] == 6) { //CCW @@ -323,27 +420,28 @@ MapKnitter.Map = MapKnitter.Class.extend({ } else if (GPS["Orientation"] == 3) { //180 angle += (Math.PI / 180) * 180 } - + /* If there is altitude data */ - if (typeof GPS["GPSAltitude"] !== 'undefined' && typeof GPS["GPSAltitudeRef"] !== 'undefined'){ + if (typeof GPS["GPSAltitude"] !== 'undefined' && typeof GPS["GPSAltitudeRef"] !== 'undefined') { // Attempt to use GPS altitude: // (may eventually need to find EXIF field of view for correction) - if (typeof GPS.GPSAltitude !== 'undefined' && - typeof GPS.GPSAltitudeRef !== 'undefined') { - altitude = (GPS.GPSAltitude["numerator"]/GPS.GPSAltitude["denominator"]+GPS.GPSAltitudeRef); + if (typeof GPS.GPSAltitude !== 'undefined' && + typeof GPS.GPSAltitudeRef !== 'undefined') { + altitude = (GPS.GPSAltitude["numerator"] / GPS.GPSAltitude["denominator"] + GPS.GPSAltitudeRef); } else { altitude = 0; // none } - } + } /* only execute callback if lat (and by * implication lng) exists */ - if (lat) fn(lat,lng,id,angle,altitude); - }); + if (lat) fn(lat, lng, id, angle, altitude); + }); }, - selectImage: function(e){ - var img = this + selectImage: function (e) { + var img = this; + // var img = e.layer; // save state, watch for changes by tracking // stringified corner positions: img._corner_state = JSON.stringify(img._corners) @@ -364,7 +462,7 @@ MapKnitter.Map = MapKnitter.Class.extend({ if (this.editing._mode != "lock") e.stopPropagation() }, - saveImageIfChanged: function() { + saveImageIfChanged: function () { var img = this // check if image state has changed at all before saving! if (img.editing._mode != "lock" && img._corner_state != JSON.stringify(img._corners)) { @@ -372,39 +470,39 @@ MapKnitter.Map = MapKnitter.Class.extend({ } }, - dblClickImage: function (e) { + dblClickImage: function (e) { var img = this window.mapKnitter.selectImage.bind(img) img.editing._enableDragging() img.editing.enable() - img.editing._toggleRotateDistort() + img.editing._toggleRotateScale() e.stopPropagation() }, - saveImage: function() { + saveImage: function () { //console.log('saving') var img = this // reset change state string: img._corner_state = JSON.stringify(img._corners) // send save request - $.ajax('/images/update',{ + $.ajax('/images/update', { type: 'POST', data: { warpable_id: img.warpable_id, locked: (img.editing._mode == 'lock'), - points: - img._corners[0].lng+','+img._corners[0].lat+':'+ - img._corners[1].lng+','+img._corners[1].lat+':'+ - img._corners[3].lng+','+img._corners[3].lat+':'+ - img._corners[2].lng+','+img._corners[2].lat, + points: + img._corners[0].lng + ',' + img._corners[0].lat + ':' + + img._corners[1].lng + ',' + img._corners[1].lat + ':' + + img._corners[3].lng + ',' + img._corners[3].lat + ':' + + img._corners[2].lng + ',' + img._corners[2].lat, }, - beforeSend: function(e) { + beforeSend: function (e) { $('.mk-save').removeClass('fa-check-circle fa-times-circle fa-green fa-red').addClass('fa-spinner fa-spin') }, - complete: function(e) { + complete: function (e) { $('.mk-save').removeClass('fa-spinner fa-spin').addClass('fa-check-circle fa-green') }, - error: function(e) { + error: function (e) { $('.mk-save').removeClass('fa-spinner fa-spin').addClass('fa-times-circle fa-red') } }) @@ -412,28 +510,28 @@ MapKnitter.Map = MapKnitter.Class.extend({ // /maps/newbie/warpables/42, but we'll try /warpables/42 // as it should also be a valid restful route - deleteImage: function() { + deleteImage: function () { var img = this // this should only be possible by logged-in users if (mapKnitter.logged_in) { if (confirm("Are you sure you want to delete this image? You cannot undo this.")) { - $.ajax('/images/'+img.warpable_id,{ + $.ajax('/images/' + img.warpable_id, { dataType: "json", type: 'DELETE', - beforeSend: function(e) { + beforeSend: function (e) { $('.mk-save').removeClass('fa-check-circle fa-times-circle fa-green fa-red').addClass('fa-spinner fa-spin') }, - complete: function(e) { + complete: function (e) { $('.mk-save').removeClass('fa-spinner fa-spin').addClass('fa-check-circle fa-green') // disable interactivity: - img.editing._hideToolbar(); + img.editing._removeToolbar(); img.editing.disable(); // remove from Leaflet map: map.removeLayer(img); // remove from sidebar too: - $('#warpable-'+img.warpable_id).remove() + $('#warpable-' + img.warpable_id).remove() }, - error: function(e) { + error: function (e) { $('.mk-save').removeClass('fa-spinner fa-spin').addClass('fa-times-circle fa-red') } }) @@ -443,44 +541,45 @@ MapKnitter.Map = MapKnitter.Class.extend({ } }, - getMap: function() { + getMap: function () { return this._map; }, /* Fetch JSON list of warpable images */ - withWarpables: function(callback) { + withWarpables: function (callback) { if (this._warpables) { if (callback) { callback(this._warpables); } } else { - jQuery.getJSON(this._warpablesUrl, function(warpablesData) { + jQuery.getJSON(this._warpablesUrl, function (warpablesData) { this._warpables = warpablesData; if (callback) { callback(this._warpables); } - }); + }); } }, /* withWarpable(id, "medium", function(img) { ... }) */ - withWarpable: function(id, size, callback) { - this.withWarpables(function(warpables) { + withWarpable: function (id, size, callback) { + this.withWarpables(function (warpables) { var url = warpables[id][size], img = jQuery("").attr("src", url).attr("data-warpable-id", id); callback(img); }); }, - addKml: function() { + addKml: function () { var url = prompt("Enter a KML URL"); var kml = omnivore.kml(url) - .on('ready', function() { console.log(kml); + .on('ready', function () { + console.log(kml); map.fitBounds(kml.getBounds()); - $.each(kml._layers,function(i,marker) { - marker.bindPopup('

'+marker.feature.properties.__data+"

"); + $.each(kml._layers, function (i, marker) { + marker.bindPopup('

' + marker.feature.properties.__data + "

"); }); }).addTo(map); }, - setupMap: function() { + setupMap: function () { var map = this._map; //L.tileLayer.provider('Esri.WorldImagery').addTo(map); @@ -497,22 +596,280 @@ MapKnitter.Map = MapKnitter.Class.extend({ type: 'satellite', // valid values are 'roadmap', 'satellite', 'terrain' and 'hybrid' maxZoom: 24, maxNativeZoom: 20, - opacity:0.5 + opacity: 0.5 }).addTo(this._map); var baseMaps = { - "OpenStreetMap": mapbox, - "Google Satellite": googleMutant + "OpenStreetMap": mapbox, + "Google Satellite": googleMutant }; // eventually, annotations var overlayMaps = { }; - - var layersControl = new L.Control.Layers(baseMaps,overlayMaps); + + var layersControl = new L.Control.Layers(baseMaps, overlayMaps); this._map.addControl(layersControl); L.control.zoom({ position: 'topright' }).addTo(map); L.control.scale().addTo(map); + }, + + imgActionArray: function() { + return [CToggleTransparency, CToggleOutline, CToggleLock, CToggleRotateScale, CToggleOrder, CEnableEXIF, CRestore, CExport, CDelete]; } +}); + +/** + * ALL BELOW ARE QUICK OVERRIDE FOR SVG EXTERENAL PATH NOT BEING FOUND IN RAILS - + * should be removed on rails 5 implementation and switch to being loaded with webpacker + * */ + +var CToggleTransparency = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var edit = overlay.editing, + href, + tooltip, + symbol; + + if (edit._transparent) { + href = ''; + symbol = ''; + tooltip = 'Make Image Opaque'; + } else { + href = ''; + symbol = ''; + tooltip = 'Make Image Transparent'; + } + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: tooltip + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._toggleTransparency(); + } +}); + +var CToggleOutline = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var edit = overlay.editing, + href, + tooltip, + symbol; + + + if (edit._outlined) { + href = ''; + symbol = ''; + tooltip = 'Remove Border'; + } else { + href = ''; + symbol = '' + tooltip = 'Add Border'; + } + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: tooltip + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._toggleOutline(); + } +}); + +var CDelete = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var href = ''; + var symbol = ''; + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: 'Delete Image' + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._removeOverlay(); + } +}); + +var CToggleLock = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var edit = overlay.editing, + href, + tooltip, + symbol; + + if (edit._mode === 'lock') { + href = ''; + symbol = ''; + tooltip = 'Unlock'; + } else { + href = ''; + symbol = ''; + tooltip = 'Lock'; + } + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: tooltip + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._toggleLock(); + } +}); + +var CToggleRotateScale = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var edit = overlay.editing, + href, + tooltip, + symbol; + + if (edit._mode === 'rotateScale') { + href = ''; + symbol = ''; + tooltip = 'Distort'; + } else { + href = ''; + symbol = ''; + tooltip = 'Rotate+Scale'; + } + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: tooltip + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._toggleRotateScale(); + } +}); + +var CExport = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var href = ''; + var symbol = ''; + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: 'Export Image' + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._getExport(); + } +}); + +var CToggleOrder = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var edit = overlay.editing, + href, + tooltip, + symbol; + + if (edit._toggledImage) { + href = ''; + symbol = ''; + tooltip = 'Stack to Front'; + } else { + href = ''; + symbol = ''; + tooltip = 'Stack to Back'; + } + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: tooltip + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._toggleOrder(); + } +}); + +var CEnableEXIF = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var href = ''; + var symbol = ''; + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: 'Geolocate Image' + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var image = this._overlay.getElement(); + + EXIF.getData(image, L.EXIF(image)); + } +}); + +var CRestore = L.EditAction.extend({ + initialize: function (map, overlay, options) { + var href = ''; + var symbol = ''; + + options = options || {}; + options.toolbarIcon = { + html: '' + href + symbol + '', + tooltip: 'Restore' + }; + + L.EditAction.prototype.initialize.call(this, map, overlay, options); + }, + + addHooks: function () { + var editing = this._overlay.editing; + + editing._restore(); + } }); diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb index 231f18d6c..da7c51711 100644 --- a/app/controllers/export_controller.rb +++ b/app/controllers/export_controller.rb @@ -81,6 +81,16 @@ def status end end + def create + # Saving in export_url column because assuming that is not being used in new Export API + # if it is, instead create a new col like 'status_url' and save the url there + + # mySQL2 error ActiveRecord::StatementInvalid (Mysql2::Error: Field 'bands_string' doesn't have a default value: INSERT INTO `exports` (`export_url`, `created_at`, `updated_at`) VALUES ('//export.mapknitter.org/id/1562102960/status.json', '2019-07-02 21:29:20', '2019-07-02 21:29:20')): + # so adding a default value for now. I think this column will be deprecated? + export = Export.create!(export_url: params[:status_url], bands_string: 'default bands_string') + render json: export.to_json + end + # for demoing remote url functionality during testing def external_url_test render json: Export.last.to_json diff --git a/app/views/layouts/knitter2.html.erb b/app/views/layouts/knitter2.html.erb index 465d7887c..52e8d49e6 100644 --- a/app/views/layouts/knitter2.html.erb +++ b/app/views/layouts/knitter2.html.erb @@ -23,7 +23,6 @@ - diff --git a/app/views/map/leaflet.html.erb b/app/views/map/leaflet.html.erb index 0d26adc45..4fc500a42 100644 --- a/app/views/map/leaflet.html.erb +++ b/app/views/map/leaflet.html.erb @@ -65,19 +65,19 @@ }) <% @map.warpables.each do |warpable| %> - img = new L.DistortableImageOverlay( + img = L.distortableImageOverlay( '<%= warpable.image.url(:medium) %>', { <% unless warpable.nodes == '' %> latlng: [ - new L.latLng(<%= @map.nodes[warpable.id.to_s][0][1] %>, - <%= @map.nodes[warpable.id.to_s][0][0] %>), - new L.latLng(<%= @map.nodes[warpable.id.to_s][1][1] %>, - <%= @map.nodes[warpable.id.to_s][1][0] %>), - new L.latLng(<%= @map.nodes[warpable.id.to_s][3][1] %>, - <%= @map.nodes[warpable.id.to_s][3][0] %>), - new L.latLng(<%= @map.nodes[warpable.id.to_s][2][1] %>, - <%= @map.nodes[warpable.id.to_s][2][0] %>) + L.latLng(<%= @map.nodes[warpable.id.to_s][0][1] %>, + <%= @map.nodes[warpable.id.to_s][0][0] %>), + L.latLng(<%= @map.nodes[warpable.id.to_s][1][1] %>, + <%= @map.nodes[warpable.id.to_s][1][0] %>), + L.latLng(<%= @map.nodes[warpable.id.to_s][3][1] %>, + <%= @map.nodes[warpable.id.to_s][3][0] %>), + L.latLng(<%= @map.nodes[warpable.id.to_s][2][1] %>, + <%= @map.nodes[warpable.id.to_s][2][0] %>) ], <% end %> locked: <%= warpable.locked %> diff --git a/config/routes.rb b/config/routes.rb index 727f6518c..546cc45cd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,6 +57,7 @@ post 'maps/:map_id/warpables' => 'images#create' # deprecate this in favor of resourceful route below; this is just to override maps/:id get 'export/progress/:id' => 'export#progress' get 'export/status/:id' => 'export#status' + post 'export' => 'export#create' get 'exports' => 'export#index' get 'map/:id', to: redirect('/maps/%{id}') get 'embed/:id' => 'maps#embed' diff --git a/package.json b/package.json index 7f6f4400e..cef0248c0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "junction": "theleagueof/junction#*", "leaflet": "Leaflet/Leaflet#^1.0.0", "leaflet-dist": "vperron/leaflet-dist#0.7.x", - "leaflet-distortableimage": "publiclab/Leaflet.DistortableImage#^0.4.3", + "leaflet-distortableimage": "^0.5.2", "leaflet-draw": "Leaflet/Leaflet.draw#0.2.4", "leaflet-easybutton": "CliffCloud/Leaflet.EasyButton#1.0.0", "leaflet-environmental-layers": "^1.3.1", @@ -35,7 +35,6 @@ "leaflet-omnivore": "mapbox/leaflet-omnivore#~0.3.2", "leaflet-providers": "leaflet-extras/leaflet-providers#1.0.6", "leaflet-spin": "makinacorpus/Leaflet.Spin#1.1.0", - "leaflet-toolbar": "Leaflet/Leaflet.Toolbar#~0.3.0", "modalbox": "okonet/modalbox#*", "ol2": "openlayers/ol2#release-2.13.1", "prototypejs-bower": "plynchnlm/prototypejs-bower#1.7.3", diff --git a/test/functional/export_controller_test.rb b/test/functional/export_controller_test.rb index d67b3cb96..c51b92a95 100644 --- a/test/functional/export_controller_test.rb +++ b/test/functional/export_controller_test.rb @@ -16,6 +16,12 @@ def teardown assert assigns[:week] end + test 'create returns json' do + url = 'https://example.json/12345/status.json' + post :create, status_url: url + assert_equal url, Export.last.export_url + end + test 'should create jpg after export' do map = maps(:cubbon) system('mkdir -p public/warps/cubbon-park') diff --git a/yarn.lock b/yarn.lock index a0aebefbb..bc3b65c7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1478,10 +1478,9 @@ lazy-cache@^1.0.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= -"leaflet-blurred-location@git://github.com/publiclab/leaflet-blurred-location.git#main": +"leaflet-blurred-location@git://github.com/publiclab/leaflet-blurred-location#main": version "1.3.0" - uid "1bff37cb96518bd87192277b14c290cba3c138ba" - resolved "git://github.com/publiclab/leaflet-blurred-location.git#1bff37cb96518bd87192277b14c290cba3c138ba" + resolved "git://github.com/publiclab/leaflet-blurred-location#1bff37cb96518bd87192277b14c290cba3c138ba" dependencies: haversine-distance "^1.1.4" jquery "^3.2.1" @@ -1491,24 +1490,26 @@ leaflet-dist@vperron/leaflet-dist#0.7.x: version "0.0.0" resolved "https://codeload.github.com/vperron/leaflet-dist/tar.gz/2ec7a0846eaecdce1321d92b3c45c74f4fd08dd3" -leaflet-distortableimage@publiclab/Leaflet.DistortableImage#^0.4.3: - version "0.4.3" - resolved "https://codeload.github.com/publiclab/Leaflet.DistortableImage/tar.gz/cb1ed3875f9ad07942903a9daa778076ce5d7fd1" +leaflet-distortableimage@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/leaflet-distortableimage/-/leaflet-distortableimage-0.5.2.tgz#080a521cc72ceea676a38f5d2b8fc4b1b3bbb844" + integrity sha512-p93CDwoFy8kUECYYT/sh9swAbKvVWYjSZe2vsy5emMuvm5QxyJ1hfj64DAEoc2gLOu1qppy1DXKWPpUaV4cvEA== dependencies: grunt-cli "^1.2.0" + leaflet-toolbar "0.4.0-alpha.2" leaflet-draw@Leaflet/Leaflet.draw#0.2.4: version "0.2.3" resolved "https://codeload.github.com/Leaflet/Leaflet.draw/tar.gz/07b1448f4eba33982f662398988f0337ee55cc02" leaflet-environmental-layers@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/leaflet-environmental-layers/-/leaflet-environmental-layers-1.3.1.tgz#85202b3071ddb1e0f44c8cb66fb7f82f41f82465" - integrity sha512-jcaN1vkmLr0v8RGDYXBjiJIsD8qZj+ZaDk2ovVmWHzSB8qZHhaMWhbrVtBHEkOQVBjquSv9Ddi092YhEhuyJqQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/leaflet-environmental-layers/-/leaflet-environmental-layers-1.3.2.tgz#fdaead0c7d472f5e10eb3306ecceb0d60779ac01" + integrity sha512-xMwLt0OexCV5rLI+iffZCnESczrgNimxV+KzWk4u96qT7qc9aOxzwOohTsZjBqygp/TAgBRCVmdGC3kzsYIUlA== dependencies: jquery "^3.3.1" leaflet "^1.3.1" - leaflet-blurred-location "git://github.com/publiclab/leaflet-blurred-location.git#main" + leaflet-blurred-location "git://github.com/publiclab/leaflet-blurred-location#main" leaflet-fullhash "github:sagarpreet-chadha/leaflet-fullHash" leaflet-heatmap "1.0.0" leaflet-providers "^1.1.17" @@ -1517,7 +1518,6 @@ leaflet-environmental-layers@^1.3.1: leaflet-fullhash@^1.0.0, "leaflet-fullhash@github:sagarpreet-chadha/leaflet-fullHash": version "1.0.0" - uid "6c547255ef7842281c6af3d6b879a2e994e1c54d" resolved "https://codeload.github.com/sagarpreet-chadha/leaflet-fullHash/tar.gz/6c547255ef7842281c6af3d6b879a2e994e1c54d" leaflet-heatmap@1.0.0: @@ -1566,9 +1566,10 @@ leaflet-spin@makinacorpus/Leaflet.Spin#1.1.0: dependencies: spin.js "^2.3.1" -leaflet-toolbar@Leaflet/Leaflet.Toolbar#~0.3.0: - version "0.3.0" - resolved "https://codeload.github.com/Leaflet/Leaflet.Toolbar/tar.gz/fb935befa99d753bdd0deb0ce2cad20335783459" +leaflet-toolbar@0.4.0-alpha.2: + version "0.4.0-alpha.2" + resolved "https://registry.yarnpkg.com/leaflet-toolbar/-/leaflet-toolbar-0.4.0-alpha.2.tgz#ab58092d65b3dd158fb874c3f7e36b7ebdb994f8" + integrity sha512-g1KeU3DYGUOwXTS0vKIgJ8KgY0h/2B0qENNQUSYMFtSY2yzwaHWMbDyhDbk+aO8LgJ1iOX4fUt2bh1sYZbpmmQ== leaflet.blurred-location-display@^1.1.0: version "1.1.0" @@ -1576,11 +1577,10 @@ leaflet.blurred-location-display@^1.1.0: integrity sha512-l7vtPdwGbo6Xqeds37DstKNMANiI8g7EUvKrQ34CcBebyz1GcBBnWGn2wq44uqWxvKgt3okZDWdm4i6tWwjKpQ== dependencies: jquery "^3.3.1" - leaflet-blurred-location "git://github.com/publiclab/leaflet-blurred-location.git#main" + leaflet-blurred-location "git://github.com/publiclab/leaflet-blurred-location#main" leaflet@*, leaflet@Leaflet/Leaflet#^1.0.0, leaflet@^1.3.1, leaflet@^1.3.3: version "1.5.1" - uid "2e3e0ffbe87f246eb76d86d2633ddd59b262830b" resolved "https://codeload.github.com/Leaflet/Leaflet/tar.gz/2e3e0ffbe87f246eb76d86d2633ddd59b262830b" levn@~0.3.0: @@ -2051,13 +2051,12 @@ process-nextick-args@~2.0.0: prototypejs-bower@plynchnlm/prototypejs-bower#1.7.3: version "0.0.0" - uid "5da3d86b77b94ec7eb5b92b8b5e0a7a241adae43" resolved "https://codeload.github.com/plynchnlm/prototypejs-bower/tar.gz/5da3d86b77b94ec7eb5b92b8b5e0a7a241adae43" psl@^1.1.24: - version "1.1.33" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.33.tgz#5533d9384ca7aab86425198e10e8053ebfeab661" - integrity sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.2.0.tgz#df12b5b1b3a30f51c329eacbdef98f3a6e136dc6" + integrity sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA== punycode@^1.4.1: version "1.4.1" @@ -2383,7 +2382,6 @@ source-map@~0.6.1: sparklines@mariusGundersen/sparkline#~1.2.0: version "1.2.0" - uid ce62ca07d3b72a82eae1e51c60bcd07a1e631d41 resolved "https://codeload.github.com/mariusGundersen/sparkline/tar.gz/ce62ca07d3b72a82eae1e51c60bcd07a1e631d41" spin.js@^2.3.1: