Commit 6b5cb4f2 authored by Matthias Schiffer's avatar Matthias Schiffer

Merge branch 'geo-location-osm'

parents 897ee73d a8d736ac
......@@ -407,6 +407,13 @@ config_mode \: optional
package. Set *geo_location.show_altitude* to *true* if you want the altitude
field to be visible.
The *geo_location.osm* section is only relevant when the *gluon-config-mode-geo-location-osm*
package is used. The *center.lon* and *center.lat* values are mandatory in this case and
define the default center of the map when no position has been picked yet. The *zoom* level
defaults to 12 in this case. *openlayers_url* allows to override the base URL of the
*build/ol.js* and *css/ol.css* files (the default is
``https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0``).
The remote login page only shows SSH key configuration by default. A
password form can be displayed by setting *remote_login.show_password_form*
to true; in this case, *remote_login.min_password_length* defines the
......@@ -419,6 +426,14 @@ config_mode \: optional
},
geo_location = {
show_altitude = true,
osm = {
center = {
lon = 52.951947558,
lat = 7.844238281,
},
zoom = 13,
openlayers_url = 'http://ffac/ol',
},
},
remote_login = {
show_password_form = true,
......
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-config-mode-geo-location-osm
PKG_VERSION:=1
include ../gluon.mk
define Package/gluon-config-mode-geo-location-osm
TITLE:=Set geographic location of a node (map support)
DEPENDS:=+gluon-config-mode-geo-location +gluon-web-osm
endef
define Package/gluon-config-mode-geo-location-osm/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/config-mode/www/static/
$(LN) /lib/gluon/web/www/static/gluon-web-osm.js $(1)/lib/gluon/config-mode/www/static/
endef
$(eval $(call BuildPackageGluon,gluon-config-mode-geo-location-osm))
need_number(in_site({'config_mode', 'geo_location', 'osm', 'center', 'lon'}))
need_number(in_site({'config_mode', 'geo_location', 'osm', 'center', 'lat'}))
need_number(in_site({'config_mode', 'geo_location', 'osm', 'zoom'}), false)
need_string(in_site({'config_mode', 'geo_location', 'osm', 'openlayers_url'}), false)
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: gluon-config-mode-geo-location\n"
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
"Language-Team: German\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid ""
"You may also select the position on the map displayed below if your computer "
"is connected to the internet at the moment."
msgstr ""
"Wenn dein Computer aktuell mit dem Internet verbunden ist, kann die Position "
"auch auf der hier angezeigten Karte ausgewählt werden."
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid ""
"You may also select the position on the map displayed below if your computer "
"is connected to the internet at the moment."
msgstr ""
local osm = require 'gluon.web.model.osm'
local site = require 'gluon.site'
local tonumber = tonumber
module 'gluon.config-mode.geo-location-osm'
MapValue = osm.MapValue
function help(i18n)
local pkg_i18n = i18n 'gluon-config-mode-geo-location-osm'
return pkg_i18n.translate(
'You may also select the position on the map displayed below if your computer is connected to the internet at the moment.'
)
end
function options()
local config = site.config_mode.geo_location.osm
return {
openlayers_url = config.openlayers_url(),
zoom = config.zoom(12),
pos = config.center(),
}
end
......@@ -14,11 +14,11 @@ msgid "Altitude"
msgstr "Höhe"
msgid ""
"If you want the location of your node to be displayed on the map, you can "
"enter its coordinates here."
"If you want the location of your node to be displayed on public maps, you "
"can enter its coordinates here."
msgstr ""
"Um deinen Knoten auf der Karte anzeigen zu können, benötigen wir seine "
"Koordinaten. Hier hast du die Möglichkeit, diese zu hinterlegen."
"Um deinen Knoten auf öffentlichen Karten anzeigen zu können, benötigen wir "
"seine Koordinaten. Hier hast du die Möglichkeit, diese zu hinterlegen."
msgid "Latitude"
msgstr "Breitengrad"
......@@ -26,8 +26,8 @@ msgstr "Breitengrad"
msgid "Longitude"
msgstr "Längengrad"
msgid "Show node on the map"
msgstr "Knoten auf der Karte anzeigen"
msgid "Advertise node position"
msgstr "Knotenposition veröffentlichen"
msgid ""
"Specifying the altitude is optional; it should only be filled in if an "
......
......@@ -14,11 +14,9 @@ msgid "Altitude"
msgstr "Hauteur"
msgid ""
"If you want the location of your node to be displayed on the map, you can "
"enter its coordinates here."
"If you want the location of your node to be displayed on public maps, you "
"can enter its coordinates here."
msgstr ""
"Pour Afficher votre nœud sur la Carte nous avons besoin de ses coordonnées. "
"Ici vous pouvez entrer sa position."
msgid "Latitude"
msgstr "Latitude"
......@@ -26,8 +24,8 @@ msgstr "Latitude"
msgid "Longitude"
msgstr "Longitude"
msgid "Show node on the map"
msgstr "Afficher le nœud sur la carte"
msgid "Advertise node position"
msgstr ""
msgid ""
"Specifying the altitude is optional; it should only be filled in if an "
......
......@@ -5,8 +5,8 @@ msgid "Altitude"
msgstr ""
msgid ""
"If you want the location of your node to be displayed on the map, you can "
"enter its coordinates here."
"If you want the location of your node to be displayed on public maps, you "
"can enter its coordinates here."
msgstr ""
msgid "Latitude"
......@@ -15,7 +15,7 @@ msgstr ""
msgid "Longitude"
msgstr ""
msgid "Show node on the map"
msgid "Advertise node position"
msgstr ""
msgid ""
......
......@@ -4,6 +4,9 @@ return function(form, uci)
local site = require 'gluon.site'
local osm
pcall(function() osm = require 'gluon.config-mode.geo-location-osm' end)
local location = uci:get_first("gluon-node-info", "location")
local show_altitude = site.config_mode.geo_location.show_altitude(false)
......@@ -14,6 +17,9 @@ return function(form, uci)
'If you want the location of your node to ' ..
'be displayed on the map, you can enter its coordinates here.'
)
if osm then
text = text .. ' ' .. osm.help(i18n)
end
if show_altitude then
text = text .. ' ' .. pkg_i18n.translate(
'Specifying the altitude is optional; it should only be filled in if an accurate ' ..
......@@ -26,7 +32,7 @@ return function(form, uci)
local o
local share_location = s:option(Flag, "location", pkg_i18n.translate("Show node on the map"))
local share_location = s:option(Flag, "location", pkg_i18n.translate("Advertise node position"))
share_location.default = uci:get_bool("gluon-node-info", location, "share_location")
function share_location:write(data)
uci:set("gluon-node-info", location, "share_location", data)
......@@ -37,6 +43,12 @@ return function(form, uci)
end
end
local map = {}
if osm then
map = s:option(osm.MapValue, "map", osm.options())
map:depends(share_location, true)
end
o = s:option(Value, "latitude", pkg_i18n.translate("Latitude"), pkg_i18n.translatef("e.g. %s", "53.873621"))
o.default = uci:get("gluon-node-info", location, "latitude")
o:depends(share_location, true)
......@@ -44,6 +56,7 @@ return function(form, uci)
function o:write(data)
uci:set("gluon-node-info", location, "latitude", data)
end
map.lat = o
o = s:option(Value, "longitude", pkg_i18n.translate("Longitude"), pkg_i18n.translatef("e.g. %s", "10.689901"))
o.default = uci:get("gluon-node-info", location, "longitude")
......@@ -52,6 +65,7 @@ return function(form, uci)
function o:write(data)
uci:set("gluon-node-info", location, "longitude", data)
end
map.lon = o
if show_altitude then
o = s:option(Value, "altitude",
......
......@@ -284,6 +284,12 @@ code {
margin-bottom: 2em;
}
.gluon-osm-map {
width: 100%;
height: 40em;
margin-bottom: 1em;
}
input:placeholder {
color: #aaaaaa;
}
......@@ -498,15 +504,15 @@ input.gluon-section-create-name {
margin-right: -0.25em;
}
div.gluon-map-descr {
div.gluon-form-descr {
margin-bottom: 1em;
}
.gluon-map-descr:empty, .gluon-section-descr:empty {
.gluon-form-descr:empty, .gluon-section-descr:empty {
display: none;
}
.gluon-map-descr, .gluon-section-descr, .gluon-page-actions {
.gluon-form-descr, .gluon-section-descr, .gluon-page-actions {
padding: 1em;
background: #ececec;
}
......
......@@ -2,9 +2,9 @@
<input type="hidden" name="token" value="<%=token%>" />
<input type="hidden" name="<%=id%>" value="1" />
<div class="gluon-map" id="gluon-<%=self.config%>">
<div class="gluon-form" id="form-<%=id%>">
<% if self.title and #self.title > 0 then %><h2 name="content"><%|self.title%></h2><% end %>
<% if self.description and #self.description > 0 then %><div class="gluon-map-descr"><%=self.description%></div><% end %>
<% if self.description and #self.description > 0 then %><div class="gluon-form-descr"><%=self.description%></div><% end %>
<% self:render_children(renderer) %>
</div>
<%- if self.message then %>
......
......@@ -183,6 +183,8 @@
}
function update() {
window.dispatchEvent(new Event('gluon-update'));
var state = false;
for (var id in dep_entries) {
var entry = dep_entries[id];
......@@ -191,6 +193,7 @@
if (node && node.parentNode && !check(entry.deps)) {
node.parentNode.removeChild(node);
node.dispatchEvent(new Event('gluon-hide'));
state = true;
} else if (parent && (!node || !node.parentNode) && check(entry.deps)) {
var next = undefined;
......@@ -207,6 +210,7 @@
parent.insertBefore(entry.node, next);
}
entry.node.dispatchEvent(new Event('gluon-show'));
state = true;
}
......@@ -471,6 +475,7 @@
bind(field, "blur", validator);
bind(field, "keyup", validator);
bind(field, "gluon-revalidate", validator);
if (field.nodeName.toLowerCase() == 'select') {
bind(field, "change", validator);
......
......@@ -43,7 +43,7 @@ end
Node = class()
function Node:__init__(title, description, name)
function Node:__init__(name, title, description)
self.children = {}
self.title = title or ""
self.description = description or ""
......@@ -117,8 +117,8 @@ end
Form = class(Node)
function Form:__init__(...)
Node.__init__(self, ...)
function Form:__init__(title, description, name)
Node.__init__(self, name, title, description)
self.template = "model/form"
end
......@@ -169,26 +169,24 @@ end
Section = class(Node)
function Section:__init__(...)
Node.__init__(self, ...)
self.fields = {}
function Section:__init__(title, description, name)
Node.__init__(self, name, title, description)
self.template = "model/section"
end
function Section:option(t, option, title, description, ...)
function Section:option(t, ...)
assert(instanceof(t, AbstractValue), "class must be a descendant of AbstractValue")
local obj = t(title, description, option, ...)
local obj = t(...)
self:append(obj)
self.fields[option] = obj
return obj
end
AbstractValue = class(Node)
function AbstractValue:__init__(option, ...)
Node.__init__(self, option, ...)
function AbstractValue:__init__(...)
Node.__init__(self, ...)
self.deps = {}
self.default = nil
......
<div id="<%=id%>" class="gluon-osm-map" style="display: none"></div>
<script type="text/javascript" src="/static/gluon-web-osm.js"></script>
<script type="text/javascript">
(function() {
var elMap = document.getElementById(<%=json(id)%>);
var wrapper = elMap.parentNode;
var elLon, elLat;
window.addEventListener('gluon-update', function() {
<% if self.lon then -%>
elLon = document.getElementById(<%=json(self.lon:id())%>);
<%- end %>
<% if self.lat then -%>
elLat = document.getElementById(<%=json(self.lat:id())%>);
<%- end %>
}, {once: true});
initOSM(<%=json(self.openlayers_url)%>, function(createMap) {
elMap.style.display = '';
var pos = <%=json(self:cfgvalue().pos)%>;
var map = createMap(
elMap,
[pos.lon, pos.lat],
<%=json(self:cfgvalue().zoom)%>,
<%=json(self:cfgvalue().set)%>,
function(lonlat) {
if (elLon) {
elLon.value = lonlat[0].toFixed(6);
elLon.dispatchEvent(new Event('gluon-revalidate'));
}
if (elLat) {
elLat.value = lonlat[1].toFixed(6);
elLat.dispatchEvent(new Event('gluon-revalidate'));
}
}
);
wrapper.addEventListener('gluon-show', function() {
map.updateSize();
});
});
})();
</script>
"use strict";function initOSM(e,o){var t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e+"/css/ol.css",document.head.appendChild(t);var n=document.createElement("script"),r=!1;n.onload=n.onreadystatechange=function(){if(!(r||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){r=!0;var t=new Image;t.onload=function(){var e=new ol.style.Style({image:new ol.style.Icon({img:t,imgSize:[30,45],anchor:[.5,1]})}),c=new ol.Feature;c.setStyle(e),o(function(e,t,o,n,r){var a=new ol.Map({target:e,layers:[new ol.layer.Tile({source:new ol.source.OSM}),new ol.layer.Vector({source:new ol.source.Vector({features:[c]})})],view:new ol.View({center:ol.proj.fromLonLat(t),zoom:o})}),l=function(e){c.setGeometry(new ol.geom.Point(e))};return a.addEventListener("click",function(e){l(e.coordinate),r(ol.proj.toLonLat(e.coordinate))}),n&&l(ol.proj.fromLonLat(t)),a})},t.src="data:image/svg+xml,"+escape('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="30" height="45"><path d="M2,15A13,13,0,0,1,28,13Q28,28,15,45Q2,28,2,15" fill="#48b" stroke="#369" stroke-width="1.5" /><circle cx="15" cy="15" r="6" fill="#fff" /></svg>')}},n.src=e+"/build/ol.js",document.head.appendChild(n)}
\ No newline at end of file
function findObj(e){for(list=document.getElementsByClassName("gluon-input-text"),i=0;i<list.length;i++)if(item=list.item(i),0<=item.id.indexOf(e))return item;return!1}function showMap(){if("object"==typeof OpenLayers&&!1!==findObj("longitude")){document.getElementById("locationPickerMap").style.display="block";var a=new OpenLayers.Projection("EPSG:4326"),o=new OpenLayers.Projection("EPSG:900913"),e=zoom,t=new OpenLayers.Layer.Markers("Markers");OpenLayers.Control.Click=OpenLayers.Class(OpenLayers.Control,{defaultHandlerOptions:{single:!0,double:!1,pixelTolerance:0,stopSingle:!1,stopDouble:!1},initialize:function(){this.handlerOptions=OpenLayers.Util.extend({},this.defaultHandlerOptions),OpenLayers.Control.prototype.initialize.apply(this,arguments),this.handler=new OpenLayers.Handler.Click(this,{click:this.trigger},this.handlerOptions)},trigger:function(e){var n=osmMap.getLonLatFromPixel(e.xy);oLon=findObj("longitude"),oLat=findObj("latitude"),lonlat1=new OpenLayers.LonLat(n.lon,n.lat).transform(o,a),oLon.value=lonlat1.lon,oLat.value=lonlat1.lat,t.clearMarkers(),t.addMarker(new OpenLayers.Marker(n)),oLon.className=oLon.className.replace(/ gluon-input-invalid/g,""),oLat.className=oLat.className.replace(/ gluon-input-invalid/g,"")}}),osmMap=new OpenLayers.Map("locationPickerMap",{controls:[new OpenLayers.Control.Navigation,new OpenLayers.Control.PanZoomBar,new OpenLayers.Control.MousePosition],maxExtent:new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),numZoomLevels:18,maxResolution:156543,units:"m",projection:o,displayProjection:a});var n=new OpenLayers.Layer.OSM("OpenStreetMap");osmMap.addLayer(n),osmMap.addLayer(t);var r=longitude,i=latitude;oLon=findObj("longitude"),oLat=findObj("latitude"),""!=oLon.value&&(r=oLon.value),""!=oLat.value&&(i=oLat.value),t.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(r,i).transform(a,o)));var l=new OpenLayers.LonLat(r,i).transform(a,o);osmMap.setCenter(l,e);var s=new OpenLayers.Control.Click;osmMap.addControl(s),s.activate()}else setTimeout(showMap,1e3)}
\ No newline at end of file
/*
Build using:
uglifyjs javascript/gluon-web-osm.js -o files/lib/gluon/web/www/static/gluon-web-osm.js -c -m
*/
'use strict';
function initOSM(openlayers_url, ready) {
var markerSvg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="30" height="45">'
+ '<path d="M2,15A13,13,0,0,1,28,13Q28,28,15,45Q2,28,2,15" fill="#48b" stroke="#369" stroke-width="1.5" />'
+ '<circle cx="15" cy="15" r="6" fill="#fff" />'
+ '</svg>';
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = openlayers_url + '/css/ol.css';
document.head.appendChild(style);
var script = document.createElement('script');
var done = false;
script.onload = script.onreadystatechange = function() {
if (done)
return;
if (this.readyState && this.readyState !== "loaded" && this.readyState !== "complete")
return;
done = true;
var markerImg = new Image();
markerImg.onload = function() {
var markerStyle = new ol.style.Style({
image: new ol.style.Icon({
img: markerImg,
imgSize: [30, 45],
anchor: [0.5, 1]
})
});
var marker = new ol.Feature();
marker.setStyle(markerStyle);
ready(function(elMap, pos, zoom, set, onUpdate) {
var map = new ol.Map({
target: elMap,
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: new ol.source.Vector({
features: [marker]
})
})
],
view: new ol.View({
center: ol.proj.fromLonLat(pos),
zoom: zoom,
})
});
var refresh = function(coord) {
marker.setGeometry(new ol.geom.Point(coord));
}
map.addEventListener('click', function(e) {
refresh(e.coordinate);
onUpdate(ol.proj.toLonLat(e.coordinate));
});
if (set)
refresh(ol.proj.fromLonLat(pos));
return map;
});
}
markerImg.src = 'data:image/svg+xml,' + escape(markerSvg);
};
script.src = openlayers_url + '/build/ol.js';
document.head.appendChild(script);
}
/*
Build using:
uglifyjs javascript/osm.js -o files/lib/gluon/web/www/static/osm.js -c -m
*/
function findObj(name) {
list = document.getElementsByClassName("gluon-input-text");
for(i = 0; i < list.length; i++) {
item = list.item(i);
if(item.id.indexOf(name) >= 0) return item;
}
return false;
}
function showMap() {
if ("object" == typeof OpenLayers && false !== findObj("longitude")) {
document.getElementById("locationPickerMap").style.display = "block";
var e = new OpenLayers.Projection("EPSG:4326"),
a = new OpenLayers.Projection("EPSG:900913"),
t = zoom,
n = new OpenLayers.Layer.Markers("Markers");
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
defaultHandlerOptions: {
single: !0,
"double": !1,
pixelTolerance: 0,
stopSingle: !1,
stopDouble: !1
},
initialize: function() {
this.handlerOptions = OpenLayers.Util.extend({}, this.defaultHandlerOptions), OpenLayers.Control.prototype.initialize.apply(this, arguments), this.handler = new OpenLayers.Handler.Click(this, {
click: this.trigger
}, this.handlerOptions)
},
trigger: function(t) {
var i = osmMap.getLonLatFromPixel(t.xy);
oLon = findObj("longitude");
oLat = findObj("latitude");
lonlat1 = new OpenLayers.LonLat(i.lon, i.lat).transform(a, e),
oLon.value = lonlat1.lon,
oLat.value = lonlat1.lat,
n.clearMarkers(),
n.addMarker(new OpenLayers.Marker(i)),
oLon.className = oLon.className.replace(/ gluon-input-invalid/g, ""),
oLat.className = oLat.className.replace(/ gluon-input-invalid/g, "");
}
}), osmMap = new OpenLayers.Map("locationPickerMap", {
controls: [new OpenLayers.Control.Navigation, new OpenLayers.Control.PanZoomBar, new OpenLayers.Control.MousePosition],
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
numZoomLevels: 18,
maxResolution: 156543,
units: "m",
projection: a,
displayProjection: e
});
var i = new OpenLayers.Layer.OSM("OpenStreetMap");
osmMap.addLayer(i), osmMap.addLayer(n);
var o = longitude,
r = latitude;
oLon = findObj("longitude");
oLat = findObj("latitude");
"" != oLon.value && (o = oLon.value),
"" != oLat.value && (r = oLat.value),
n.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(o, r).transform(e, a)));
var l = new OpenLayers.LonLat(o, r),
d = l.transform(e, a);
osmMap.setCenter(d, t);
var s = new OpenLayers.Control.Click;
osmMap.addControl(s), s.activate()
} else setTimeout(showMap, 1e3)
}
module('gluon.web.model.osm', package.seeall)
local classes = require 'gluon.web.model.classes'
local util = require "gluon.web.util"
local class = util.class
local DEFAULT_URL = 'https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0'
MapValue = class(classes.AbstractValue)
function MapValue:__init__(title, options)
classes.AbstractValue.__init__(self, title)
self.subtemplate = "model/osm/map"
self.openlayers_url = options.openlayers_url or DEFAULT_URL
self.lon = options.lon
self.lat = options.lat
self.pos = options.pos or {lon = 0, lat = 0}
self.zoom = options.zoom or 0
self.set = options.set or false
end
function MapValue:cfgvalue()
local pos_lon = tonumber(self.lon and self.lon:cfgvalue())
local pos_lat = tonumber(self.lat and self.lat:cfgvalue())
if pos_lon and pos_lat then
return {
zoom = 18,
pos = { lon = pos_lon, lat = pos_lat },
set = true,
}
else
return self
end
end
function MapValue:validate()
return true
end
......@@ -87,6 +87,7 @@ local function dispatch(config, http, request)
pcdata = util.pcdata,
urlencode = proto.urlencode,
attr = attr,
json = json.stringify,
url = function(path) return build_url(http, path) end,
}, { __index = _G }))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment