Measurement plugin and JS API working

pull/384/head
Piero Toffanin 2018-02-09 12:38:42 -05:00
rodzic e5a6628f4b
commit 66c2178da1
40 zmienionych plików z 853 dodań i 52 usunięć

Wyświetl plik

@ -1,5 +1,5 @@
import logging, os, sys
from abc import ABC, abstractmethod
from abc import ABC
logger = logging.getLogger('app.logger')
@ -7,7 +7,6 @@ class PluginBase(ABC):
def __init__(self):
self.name = self.get_module_name().split(".")[-2]
@abstractmethod
def register(self):
pass
@ -27,6 +26,9 @@ class PluginBase(ABC):
def get_include_js_urls(self):
return ["/plugins/{}/{}".format(self.get_name(), js_file) for js_file in self.include_js_files()]
def get_include_css_urls(self):
return ["/plugins/{}/{}".format(self.get_name(), css_file) for css_file in self.include_css_files()]
def has_public_path(self):
return os.path.isdir(self.get_path("public"))
@ -38,5 +40,13 @@ class PluginBase(ABC):
"""
return []
def include_css_files(self):
"""
Should be overriden by plugins to communicate
which CSS files should be included in the WebODM interface
All paths are relative to a plugin's /public folder.
"""
return []
def __str__(self):
return "[{}]".format(self.get_module_name())

Wyświetl plik

@ -9,6 +9,9 @@ ul#side-menu.nav a,
{
color: theme("primary");
}
.theme-border-primary{
border-color: theme("primary");
}
.tooltip{
.tooltip-inner{
background-color: theme("primary");
@ -162,6 +165,9 @@ footer,
.popover-title{
border-bottom-color: theme("border");
}
.theme-border{
border-color: theme("border");
}
/* Highlight */
.task-list-item:nth-child(odd),

Wyświetl plik

@ -1,26 +0,0 @@
import { EventEmitter } from 'fbemitter';
import Utils from './Utils';
const { assert } = Utils;
if (!window.PluginsAPI){
const events = new EventEmitter();
window.PluginsAPI = {
Map: {
AddPanel: (callback) => {
events.addListener('Map::Loaded', callback);
},
Loaded: (params) => {
assert(params.map !== undefined);
events.emit('Map::Loaded', params);
}
},
events
};
}
export default window.PluginsAPI;

Wyświetl plik

@ -73,6 +73,13 @@ export default {
}
throw message; // Fallback
}
},
getCurrentScriptDir: function(){
let scripts= document.getElementsByTagName('script');
let path= scripts[scripts.length-1].src.split('?')[0]; // remove any ?query
let mydir= path.split('/').slice(0, -1).join('/')+'/'; // remove last filename part of path
return mydir;
}
};

Wyświetl plik

@ -0,0 +1,30 @@
import { EventEmitter } from 'fbemitter';
import ApiFactory from './ApiFactory';
import Map from './Map';
import $ from 'jquery';
import SystemJS from 'SystemJS';
if (!window.PluginsAPI){
const events = new EventEmitter();
const factory = new ApiFactory(events);
SystemJS.config({
baseURL: '/plugins',
map: {
css: '/static/app/js/vendor/css.js'
},
meta: {
'*.css': { loader: 'css' }
}
});
window.PluginsAPI = {
Map: factory.create(Map),
SystemJS,
events
};
}
export default window.PluginsAPI;

Wyświetl plik

@ -0,0 +1,53 @@
import SystemJS from 'SystemJS';
export default class ApiFactory{
// @param events {EventEmitter}
constructor(events){
this.events = events;
}
// @param api {Object}
create(api){
// Adds two functions to obj
// - eventName
// - triggerEventName
// We could just use events, but methods
// are more robust as we can detect more easily if
// things break
const addEndpoint = (obj, eventName, preTrigger = () => {}) => {
obj[eventName] = (callbackOrDeps, callbackOrUndef) => {
if (Array.isArray(callbackOrDeps)){
// Deps
// Load dependencies, then raise event as usual
// by appending the dependencies to the argument list
this.events.addListener(`${api.namespace}::${eventName}`, (...args) => {
Promise.all(callbackOrDeps.map(dep => SystemJS.import(dep)))
.then((...deps) => {
callbackOrUndef(...(Array.from(args).concat(...deps)));
});
});
}else{
// Callback
this.events.addListener(`${api.namespace}::${eventName}`, callbackOrDeps);
}
}
const triggerEventName = "trigger" + eventName[0].toUpperCase() + eventName.slice(1);
obj[triggerEventName] = (...args) => {
preTrigger(...args);
this.events.emit(`${api.namespace}::${eventName}`, ...args);
};
}
const obj = {};
api.endpoints.forEach(endpoint => {
if (!Array.isArray(endpoint)) endpoint = [endpoint];
addEndpoint(obj, ...endpoint);
});
return obj;
}
}

Wyświetl plik

@ -0,0 +1,17 @@
import Utils from '../Utils';
const { assert } = Utils;
const leafletPreCheck = (options) => {
assert(options.map !== undefined);
};
export default {
namespace: "Map",
endpoints: [
["willAddControls", leafletPreCheck],
["didAddControls", leafletPreCheck]
]
};

Wyświetl plik

@ -3,8 +3,6 @@ import '../css/Map.scss';
import 'leaflet/dist/leaflet.css';
import Leaflet from 'leaflet';
import async from 'async';
import 'leaflet-measure/dist/leaflet-measure.css';
import 'leaflet-measure/dist/leaflet-measure';
import '../vendor/leaflet/L.Control.MousePosition.css';
import '../vendor/leaflet/L.Control.MousePosition';
import '../vendor/leaflet/Leaflet.Autolayers/css/leaflet.auto-layers.css';
@ -15,7 +13,7 @@ import SwitchModeButton from './SwitchModeButton';
import ShareButton from './ShareButton';
import AssetDownloads from '../classes/AssetDownloads';
import PropTypes from 'prop-types';
import PluginsAPI from '../classes/PluginsAPI';
import PluginsAPI from '../classes/plugins/API';
class Map extends React.Component {
static defaultProps = {
@ -173,16 +171,22 @@ class Map extends React.Component {
this.map = Leaflet.map(this.container, {
scrollWheelZoom: true,
positionControl: true
positionControl: true,
zoomControl: false
});
const measureControl = Leaflet.control.measure({
primaryLengthUnit: 'meters',
secondaryLengthUnit: 'feet',
primaryAreaUnit: 'sqmeters',
secondaryAreaUnit: 'acres'
PluginsAPI.Map.triggerWillAddControls({
map: this.map
});
measureControl.addTo(this.map);
Leaflet.control.scale({
maxWidth: 250,
}).addTo(this.map);
//add zoom control with your options
Leaflet.control.zoom({
position:'bottomleft'
}).addTo(this.map);
if (showBackground) {
this.basemaps = {
@ -215,10 +219,6 @@ class Map extends React.Component {
}).addTo(this.map);
this.map.fitWorld();
Leaflet.control.scale({
maxWidth: 250,
}).addTo(this.map);
this.map.attributionControl.setPrefix("");
this.loadImageryLayers(true).then(() => {
@ -239,7 +239,7 @@ class Map extends React.Component {
// PluginsAPI.events.addListener('Map::AddPanel', (e) => {
// console.log("Received response: " + e);
// });
PluginsAPI.Map.Loaded({
PluginsAPI.Map.triggerDidAddControls({
map: this.map
});
}
@ -275,6 +275,7 @@ class Map extends React.Component {
return (
<div style={{height: "100%"}} className="map">
<ErrorMessage bind={[this, 'error']} />
<div
style={{height: "100%"}}
ref={(domNode) => (this.container = domNode)}

Wyświetl plik

@ -1,4 +1,6 @@
.map{
position: relative;
.leaflet-popup-content{
.title{
font-weight: bold;
@ -24,7 +26,7 @@
.shareButton{
z-index: 2000;
bottom: -11px;
bottom: 11px;
right: 38px;
}
}

Wyświetl plik

@ -3,7 +3,7 @@
display: block;
&.top{
top: -32px;
top: -54px;
}
&.bottom{
top: 32px;

Wyświetl plik

@ -2,6 +2,6 @@
border-width: 1px;
position: absolute;
z-index: 2000;
bottom: -22px;
bottom: 22px;
right: 12px;
}

Wyświetl plik

@ -1,7 +1,7 @@
import '../css/main.scss';
import './django/csrf';
import ReactDOM from 'react-dom';
import PluginsAPI from './classes/PluginsAPI';
import PluginsAPI from './classes/plugins/API';
// Main is always executed first in the page

Wyświetl plik

@ -0,0 +1,89 @@
var waitSeconds = 100;
var head = document.getElementsByTagName('head')[0];
var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/);
var webkitLoadCheck = function(link, callback) {
setTimeout(function() {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href == link.href)
return callback();
}
webkitLoadCheck(link, callback);
}, 10);
};
var cssIsReloadable = function cssIsReloadable(links) {
// Css loaded on the page initially should be skipped by the first
// systemjs load, and marked for reload
var reloadable = true;
forEach(links, function(link) {
if(!link.hasAttribute('data-systemjs-css')) {
reloadable = false;
link.setAttribute('data-systemjs-css', '');
}
});
return reloadable;
}
var findExistingCSS = function findExistingCSS(url){
// Search for existing link to reload
var links = head.getElementsByTagName('link')
return filter(links, function(link){ return link.href === url; });
}
var noop = function() {};
var loadCSS = function(url, existingLinks) {
return new Promise(function(resolve, reject) {
var timeout = setTimeout(function() {
reject('Unable to load CSS');
}, waitSeconds * 1000);
var _callback = function(error) {
clearTimeout(timeout);
link.onload = link.onerror = noop;
setTimeout(function() {
if (error)
reject(error);
else
resolve('');
}, 7);
};
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = url;
link.setAttribute('data-systemjs-css', '');
if (!isWebkit) {
link.onload = function() {
_callback();
}
} else {
webkitLoadCheck(link, _callback);
}
link.onerror = function(event) {
_callback(event.error || new Error('Error loading CSS file.'));
};
if (existingLinks.length)
head.insertBefore(link, existingLinks[0]);
else
head.appendChild(link);
})
// Remove the old link regardless of loading outcome
.then(function(result){
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
return result;
}, function(err){
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
throw err;
})
};
exports.fetch = function(load) {
// dont reload styles loaded in the head
var links = findExistingCSS(load.address);
if(!cssIsReloadable(links))
return '';
return loadCSS(load.address, links);
};

Wyświetl plik

@ -0,0 +1,70 @@
/*
* Base CSS Plugin Class
*/
function CSSPluginBase(compileCSS) {
this.compileCSS = compileCSS;
this.translate = function(load, opts) {
var loader = this;
if (loader.builder && loader.buildCSS === false) {
load.metadata.build = false;
return;
}
var path = this._nodeRequire && this._nodeRequire('path');
return Promise.resolve(compileCSS.call(loader, load.source, load.address, load.metadata.loaderOptions || {}))
.then(function(result) {
load.metadata.style = result.css;
load.metadata.styleSourceMap = result.map;
if (result.moduleFormat)
load.metadata.format = result.moduleFormat;
return result.moduleSource || '';
});
};
}
var isWin = typeof process != 'undefined' && process.platform.match(/^win/);
function toFileURL(path) {
return 'file://' + (isWin ? '/' : '') + path.replace(/\\/g, '/');
}
var builderPromise;
function getBuilder(loader) {
if (builderPromise)
return builderPromise;
return builderPromise = loader['import']('./css-plugin-base-builder.js', module.id);
}
CSSPluginBase.prototype.bundle = function(loads, compileOpts, outputOpts) {
var loader = this;
return getBuilder(loader)
.then(function(builder) {
return builder.bundle.call(loader, loads, compileOpts, outputOpts);
});
};
CSSPluginBase.prototype.listAssets = function(loads, opts) {
var loader = this;
return getBuilder(loader)
.then(function(builder) {
return builder.listAssets.call(loader, loads, opts);
});
};
/*
* <style> injection browser plugin
*/
// NB hot reloading support here
CSSPluginBase.prototype.instantiate = function(load) {
if (this.builder || typeof document === 'undefined')
return;
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = load.metadata.style;
document.head.appendChild(style);
};
module.exports = CSSPluginBase;

162
app/static/app/js/vendor/css.js vendored 100644
Wyświetl plik

@ -0,0 +1,162 @@
if (typeof window !== 'undefined') {
var waitSeconds = 100;
var head = document.getElementsByTagName('head')[0];
var isWebkit = !!window.navigator.userAgent.match(/AppleWebKit\/([^ ;]*)/);
var webkitLoadCheck = function(link, callback) {
setTimeout(function() {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href == link.href)
return callback();
}
webkitLoadCheck(link, callback);
}, 10);
};
var cssIsReloadable = function cssIsReloadable(links) {
// Css loaded on the page initially should be skipped by the first
// systemjs load, and marked for reload
var reloadable = true;
forEach(links, function(link) {
if(!link.hasAttribute('data-systemjs-css')) {
reloadable = false;
link.setAttribute('data-systemjs-css', '');
}
});
return reloadable;
}
var findExistingCSS = function findExistingCSS(url){
// Search for existing link to reload
var links = head.getElementsByTagName('link')
return filter(links, function(link){ return link.href === url; });
}
var noop = function() {};
var loadCSS = function(url, existingLinks) {
return new Promise(function(resolve, reject) {
var timeout = setTimeout(function() {
reject('Unable to load CSS');
}, waitSeconds * 1000);
var _callback = function(error) {
clearTimeout(timeout);
link.onload = link.onerror = noop;
setTimeout(function() {
if (error)
reject(error);
else
resolve('');
}, 7);
};
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = url;
link.setAttribute('data-systemjs-css', '');
if (!isWebkit) {
link.onload = function() {
_callback();
}
} else {
webkitLoadCheck(link, _callback);
}
link.onerror = function(event) {
_callback(event.error || new Error('Error loading CSS file.'));
};
if (existingLinks.length)
head.insertBefore(link, existingLinks[0]);
else
head.appendChild(link);
})
// Remove the old link regardless of loading outcome
.then(function(result){
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
return result;
}, function(err){
forEach(existingLinks, function(link){link.parentElement.removeChild(link);})
throw err;
})
};
exports.fetch = function(load) {
// dont reload styles loaded in the head
var links = findExistingCSS(load.address);
if(!cssIsReloadable(links))
return '';
return loadCSS(load.address, links);
};
}
else {
var builderPromise;
function getBuilder(loader) {
if (builderPromise)
return builderPromise;
return builderPromise = System['import']('./css-plugin-base.js', module.id)
.then(function(CSSPluginBase) {
return new CSSPluginBase(function compile(source, address) {
return {
css: source,
map: null,
moduleSource: null,
moduleFormat: null
};
});
});
}
exports.cssPlugin = true;
exports.fetch = function(load, fetch) {
if (!this.builder)
return '';
return fetch(load);
};
exports.translate = function(load, opts) {
if (!this.builder)
return '';
var loader = this;
return getBuilder(loader).then(function(builder) {
return builder.translate.call(loader, load, opts);
});
};
exports.instantiate = function(load, opts) {
if (!this.builder)
return;
var loader = this;
return getBuilder(loader).then(function(builder) {
return builder.instantiate.call(loader, load, opts);
});
};
exports.bundle = function(loads, compileOpts, outputOpts) {
var loader = this;
return getBuilder(loader).then(function(builder) {
return builder.bundle.call(loader, loads, compileOpts, outputOpts);
});
};
exports.listAssets = function(loads, opts) {
var loader = this;
return getBuilder(loader).then(function(builder) {
return builder.listAssets.call(loader, loads, opts);
});
};
}
// Because IE8?
function filter(arrayLike, func) {
var arr = []
forEach(arrayLike, function(item){
if(func(item))
arr.push(item);
});
return arr;
}
// Because IE8?
function forEach(arrayLike, func){
for (var i = 0; i < arrayLike.length; i++) {
func(arrayLike[i])
}
}

Wyświetl plik

@ -0,0 +1,329 @@
const env = {};
(function (environment) {
/**
* List of existings modules
* @type {Object}
*/
var modules = {};
/**
* Array of waiting modules
* @type {Array}
*/
var waitingModules = [];
/**
* Count created script for control
* @type {Number}
*/
var scriptCounter = 1;
/**
* Base element check for IE 6-8
* @type {Node}
*/
var baseElement = document.getElementsByTagName('base')[0];
/**
* Head element
* @type {Node}
*/
var head = document.getElementsByTagName('head')[0];
/**
* @param {String} name the name of the module
* @param {Array} deps dependencies of the module
* @param {Function} module module definition
* @param {String} dir relative dir path from which to load files
*/
function Include (name, deps, module, dir) {
var self = this;
if (typeof name !== "string") {
module = deps;
deps = name;
name = null;
}
if (deps.constructor !== [].constructor) {
module = deps;
deps = [];
}
waitingModules.unshift([name, deps, module]);
/**
* Uid for script differentiation
* @type {String}
*/
self.uid = Math.random().toString(36).replace(/[^a-z0-9]+/g, '').substr(0, 10);
self.checkModuleLoaded();
if (deps.length) {
self.each(deps, self.parseFiles);
}
};
/**
* Loop trougth an array of element with the given function
* @param {Array|NodeList} array array to loop
* @param {Function} callback function to execute with each element
*/
Include.prototype.each = function (array, callback) {
var self = this,
i;
for (i = 0; i < array.length; i++) {
if (array[i] !== undefined && callback.call(self, array[i], i, array) === false) {
break;
}
}
}
/**
* Get element data id
* @param {String} name
* @param {Boolean} clean only clean the name
* @return {String}
*/
Include.prototype.getId = function (name, clean) {
return (clean ? '' : this.uid + '-') + name.replace(/[^a-z0-9]+/g, '');
}
/**
* Check if a module is loaded
*/
Include.prototype.checkModuleLoaded = function () {
var self = this;
self.each(waitingModules, function (module, i) {
var name = module[0],
dependencies = module[1],
exec = module[2],
args = [];
self.each(dependencies, function (dependencie, n, t) {
n = dependencie.push ? dependencie[0] : dependencie;
t = document.querySelector('[data-id*="' + self.getId(n, 1) + '"]');
if (t && t.nodeName == "LINK") {
args.push(null);
return;
}
if (modules[n] !== undefined) {
args.push(modules[n]);
}
});
if (dependencies.length === args.length || dependencies.length === 0) {
if (name === null && i+1 === waitingModules.length) {
waitingModules = [];
scriptCounter = 1;
}
exec = typeof exec == 'function' ? exec.apply(this, args) : exec;
modules[name] = exec;
}
});
}
/**
* onModuleLoaded
* @param {String} name name of the module
* @param {Number} index index of the module
*/
Include.prototype.onModuleLoaded = function (name, index) {
var self = this;
// Is this script add a waiting module ? If not, that's a "normal" script file
if (index > waitingModules.length) {
scriptCounter--;
modules[name] = modules[name] || scriptCounter;
} else if (waitingModules[0][0] === null) {
waitingModules[0][0] = name;
}
self.checkModuleLoaded();
}
/**
* On Load event
* @param {Event} event event of the load
* @param {Function} caller
*/
Include.prototype.onLoad = function (event, caller) {
var self = this,
target = (event.currentTarget || event.srcElement);
//Check if the script is realy loaded and executed
if (event.type !== "load" && target.readyState != "complete") {
return;
}
target.setAttribute('data-loaded', true);
self.onModuleLoaded(target.getAttribute('data-module'), target.getAttribute('data-count'));
// Old browser need to use the detachEvent method
if (target.attachEvent) {
target.detachEvent('onreadystatechange', caller);
} else {
target.removeEventListener('load', caller);
}
}
/**
* Watch for css load
* @param {Element} elem elem to check loading
*/
Include.prototype.watchCss = function (elem) {
var self = this,
sheets = document.styleSheets,
i = sheets.length,
href = elem.href.split('//').pop();
// loop on document stylesheets to check if media is loaded
while (i--) {
if (sheets[i].href.indexOf(href) != -1) {
elem.setAttribute('data-loaded', true);
self.onModuleLoaded(elem.getAttribute('data-module'), elem.getAttribute('data-count'));
return;
}
}
setTimeout(function () {
self.watchCss.call(self, elem);
});
}
/**
* Attach events to an element
* @param {Element} elem elem to attach event
* @param {Boolean} isJs is elem a script
*/
Include.prototype.attachEvents = function (elem, isJs) {
var self = this,
cb = function () {
var args = Array.prototype.slice.call(arguments);
args.push(cb);
self.onLoad.apply(self, args);
};
if (isJs) {
if (elem.attachEvent) {
elem.attachEvent('onreadystatechange', cb);
} else {
elem.addEventListener('load', cb, true);
}
} else {
self.watchCss(elem);
}
}
/**
* Check if a script already load
* @param {String} moduleName module to load
* @param {String} isJs type of file
*/
Include.prototype.checkExists = function (moduleName, isJs) {
var exists = false;
this.each(document.getElementsByTagName(isJs ? 'script' : 'link'), function (elem) {
if (elem.getAttribute('data-module') && elem.getAttribute('data-module') === moduleName) {
exists = elem;
return false;
}
});
return exists;
}
/**
* Create a script element to load asked module
* @param {String} moduleName name of the module
* @param {String} moduleFile file to include
* @param {String} isJs type of file
*/
Include.prototype.create = function (moduleName, moduleFile, isJs) {
var self = this;
//SetTimeout prevent the element create browser rush
setTimeout(function(){
var elem = self.checkExists.call(self, moduleName, isJs);
if (elem) {
return;
}
scriptCounter++;
elem = document.createElement(isJs ? 'script' : 'link');
if (isJs) {
elem.async = true;
elem.type = "text/javascript";
elem.src = moduleFile;
} else {
elem.media = "all";
elem.href = moduleFile;
elem.rel = "stylesheet"
}
elem.setAttribute('data-id', self.getId(moduleName));
elem.setAttribute('data-module', moduleName);
elem.setAttribute('data-count', scriptCounter);
elem.setAttribute('data-loaded', false);
if (baseElement) {
//prevent IE 6-8 bug (script executed before appenchild execution.
baseElement.parentNode.insertBefore(elem, baseElement);
} else {
head.appendChild(elem);
}
self.attachEvents.call(self, elem, isJs);
}, 0);
}
/**
* Parse a file to include
* @param {String} file file to parse
*/
Include.prototype.parseFiles = function (file) {
var moduleName = file.push ? file[0] : file,
moduleFile = file.push ? file[1] : file,
ext;
//Don't load module already loaded
if (modules[moduleName]) {
this.checkModuleLoaded();
return;
}
if (moduleFile.indexOf('//') == -1 && !/\.js/.test(moduleFile) && !/^http/.test(moduleFile)) {
moduleFile = moduleFile.replace(/\./g, '/');
moduleFile = moduleFile + '.js';
}
ext = moduleFile.split('.').pop() == 'js';
this.create.call(this, moduleName, moduleFile, ext);
}
/**
* @param {String} name the name of the module
* @param {Array} deps dependencies of the module
* @param {Function} module module definition
*/
environment['include'] = environment['require'] = environment['define'] = function (name, deps, module) {
return new Include(name, deps, module);
};
})(env);
export default env;

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -18,12 +18,14 @@
<script src="{% static 'app/js/vendor/modernizr-2.8.3.min.js' %}"></script>
<script src="{% static 'app/js/vendor/jquery-1.11.2.min.js' %}"></script>
<script src="{% static 'app/js/vendor/system.js' %}"></script>
{% load render_bundle from webpack_loader %}
{% render_bundle 'main' %}
{% autoescape off %}
{% get_plugins_js_includes %}
{% get_plugins_css_includes %}
{% endautoescape %}
<title>{{title|default:"Login"}} - {{ SETTINGS.app_name }}</title>
@ -88,6 +90,7 @@
<script src="{% static 'app/js/vendor/metisMenu.min.js' %}"></script>
<script>
$(function(){
return;
$('#side-menu').metisMenu();
$(window).bind("load resize", function() {

Wyświetl plik

@ -8,4 +8,10 @@ register = template.Library()
def get_plugins_js_includes():
# Flatten all urls for all plugins
js_urls = list(itertools.chain(*[plugin.get_include_js_urls() for plugin in get_active_plugins()]))
return "\n".join(map(lambda url: "<script src='{}'></script>".format(url), js_urls))
return "\n".join(map(lambda url: "<script src='{}'></script>".format(url), js_urls))
@register.simple_tag(takes_context=False)
def get_plugins_css_includes():
# Flatten all urls for all plugins
css_urls = list(itertools.chain(*[plugin.get_include_css_urls() for plugin in get_active_plugins()]))
return "\n".join(map(lambda url: "<link href='{}' rel='stylesheet' type='text/css'>".format(url), css_urls))

Wyświetl plik

@ -43,7 +43,6 @@
"jest": "^21.0.1",
"json-loader": "^0.5.4",
"leaflet": "^1.0.1",
"leaflet-measure": "^2.0.5",
"node-sass": "^3.10.1",
"object.values": "^1.0.3",
"proj4": "^2.4.3",

Wyświetl plik

@ -0,0 +1 @@
from .plugin import *

Wyświetl plik

@ -0,0 +1,13 @@
{
"name": "Area/Length Measurements",
"webodmMinVersion": "0.4.2",
"description": "A plugin to compute area and length measurements on Leaflet",
"version": "0.1.0",
"author": "Piero Toffanin",
"email": "pt@masseranolabs.com",
"repository": "https://github.com/OpenDroneMap/WebODM",
"tags": ["area", "length", "measurements"],
"homepage": "https://github.com/OpenDroneMap/WebODM",
"experimental": false,
"deprecated": false
}

Wyświetl plik

@ -0,0 +1,5 @@
from app.plugins import PluginBase
class Plugin(PluginBase):
def include_js_files(self):
return ['main.js']

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 397 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 762 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 387 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 692 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 326 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 462 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 192 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 277 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 491 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1003 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 279 B

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 460 B

Wyświetl plik

@ -0,0 +1 @@
.leaflet-control-measure h3,.leaflet-measure-resultpopup h3{margin:0 0 12px 0;padding-bottom:10px;line-height:1em;font-weight:normal;font-size:1.1em;border-bottom:solid 1px #DDD}.leaflet-control-measure p,.leaflet-measure-resultpopup p{margin:10px 0 0;line-height:1em}.leaflet-control-measure p:first-child,.leaflet-measure-resultpopup p:first-child{margin-top:0}.leaflet-control-measure a,.leaflet-measure-resultpopup a{color:#5E66CC;text-decoration:none}.leaflet-control-measure a:hover,.leaflet-measure-resultpopup a:hover{opacity:0.5;text-decoration:none}.leaflet-control-measure .tasks,.leaflet-measure-resultpopup .tasks{margin:12px 0 0;padding:10px 0 0;border-top:solid 1px #DDD;list-style:none;list-style-image:none}.leaflet-control-measure .tasks li,.leaflet-measure-resultpopup .tasks li{display:inline;margin:0 10px 0 0}.leaflet-control-measure .tasks li:last-child,.leaflet-measure-resultpopup .tasks li:last-child{margin-right:0}.leaflet-control-measure .coorddivider,.leaflet-measure-resultpopup .coorddivider{color:#999}.leaflet-control-measure{background:#fff;border-radius:5px;box-shadow:0 1px 5px rgba(0,0,0,0.4)}.leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-control-measure .leaflet-control-measure-toggle:hover{display:block;width:36px;height:36px;background-position:50% 50%;background-repeat:no-repeat;background-image:url(images/rulers.png);border-radius:5px;text-indent:100%;white-space:nowrap;overflow:hidden}.leaflet-retina .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-retina .leaflet-control-measure .leaflet-control-measure-toggle:hover{background-image:url(images/rulers_@2X.png);background-size:16px 16px}.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle,.leaflet-touch .leaflet-control-measure .leaflet-control-measure-toggle:hover{width:44px;height:44px}.leaflet-control-measure .startprompt h3{margin-bottom:10px}.leaflet-control-measure .startprompt .tasks{margin-top:0;padding-top:0;border-top:0}.leaflet-control-measure .leaflet-control-measure-interaction{padding:10px 12px}.leaflet-control-measure .results .group{margin-top:10px;padding-top:10px;border-top:dotted 1px #eaeaea}.leaflet-control-measure .results .group:first-child{margin-top:0;padding-top:0;border-top:0}.leaflet-control-measure .results .heading{margin-right:5px;color:#999}.leaflet-control-measure a.start{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/start.png)}.leaflet-retina .leaflet-control-measure a.start{background-image:url(images/start_@2X.png);background-size:12px 12px}.leaflet-control-measure a.cancel{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/cancel.png)}.leaflet-retina .leaflet-control-measure a.cancel{background-image:url(images/cancel_@2X.png);background-size:12px 12px}.leaflet-control-measure a.finish{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/check.png)}.leaflet-retina .leaflet-control-measure a.finish{background-image:url(images/check_@2X.png);background-size:12px 12px}.leaflet-measure-resultpopup a.zoomto{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/focus.png)}.leaflet-retina .leaflet-measure-resultpopup a.zoomto{background-image:url(images/focus_@2X.png);background-size:12px 12px}.leaflet-measure-resultpopup a.deletemarkup{padding-left:18px;background-repeat:no-repeat;background-position:0% 50%;background-image:url(images/trash.png)}.leaflet-retina .leaflet-measure-resultpopup a.deletemarkup{background-image:url(images/trash_@2X.png);background-size:11px 12px}

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -0,0 +1,11 @@
PluginsAPI.Map.willAddControls([
'measure/leaflet-measure.css',
'measure/leaflet-measure.min.js'
], function(options){
L.control.measure({
primaryLengthUnit: 'meters',
secondaryLengthUnit: 'feet',
primaryAreaUnit: 'sqmeters',
secondaryAreaUnit: 'acres'
}).addTo(options.map);
});

Wyświetl plik

@ -1,3 +1,6 @@
PluginsAPI.Map.AddPanel(function(params){
console.log("GOT: ", params.map);
PluginsAPI.Map.willAddControls(function(options){
console.log("GOT: ", options);
});
PluginsAPI.Map.didAddControls(function(options){
console.log("GOT2: ", options);
});

Wyświetl plik

@ -72,6 +72,7 @@ module.exports = {
externals: {
// require("jquery") is external and available
// on the global let jQuery
"jquery": "jQuery"
"jquery": "jQuery",
"SystemJS": "SystemJS"
}
}