Auch in der Visualisierung kann es von Nutzen sein, seine aktuelle Position und ggf. eine Fahrtroute auf einer Google Maps Karte zu visualisieren. SmartHomeNG bietet eine Reihe an Plugins und Möglichkeiten, um die jeweiligen Geokoordinaten zu erfassen (EgiGeoZone mit Network oder Webservices Plugin) und die Fahrzeit zu berechnen (Traffic Plugin). Die Anzeige dieser Daten muss jedoch in der Visualisierung geschehen.
Aus dieser Motivation heraus habe ich ein Google Maps Widget für die SmartVISU geschrieben, welches mir meine aktuelle Position, meine Heimkoordinaten und den Weg zur Arbeit bzw. den Weg von der aktuellen Position nach Hause anzeigt.
In https://www.smarthomeng.de/geozonen-basierte-services-mit-der-egigeozone-app-und-dem-network-plugin wurde beschrieben, wie man Positionsdaten in SmartHomeNG übertragen kann. In https://www.smarthomeng.de/das-traffic-plugin-am-beispiel-eines-staualarms wurde beschrieben, wie das Traffic Plugin (Google Directions) in SmartHomeNG eingebunden werden kann. Items aus beiden Artikeln werden für das Plugin benötigt!
Tipp: Am Ende des Artikels findet sich noch eine einfachere Version, die nur die aktuelle Position benötigt (und anzeigt)!
Items
Basis bilden ein Teil bzw. eine Erweiterung der Items aus dem EgiGeoZone und den Traffic Plugin Artikel. Bitte auf diese Artikel referenzieren, wie die Items belegt werden können.
%YAML 1.1
---
location:
home:
lat:
type: str
value: 48.263530
cache: 'yes'
lon:
type: str
value: 11.443952
cache: 'yes'
work:
lat:
type: str
value: 48.143158
cache: 'yes'
lon:
type: str
value: 11.547755
cache: 'yes'
lat:
type: str
cache: 'yes'
lon:
type: str
cache: 'yes'
travel_info:
calculate_way_home:
type: bool
cache: 'yes'
calculate_way_work:
type: bool
cache: 'yes'
travel_summary:
type: str
Neben den jeweiligen GPS Koordinaten der aktuellen Position (location.lat, location.lon
), der Home-Koordinate (location.home.lat, location.home.lon
) und der Koordinate der Arbeitsstelle (location.work.lat, location.work.lon
), sind hier auch zwei boolsche Items, die angeben, ob jeweils der Weg nach Hause oder in die Arbeit angezeigt werden soll (travel_info.calculate_way_home
und travel_info.calculate_way_work
) Für die Anzeige darf jeweils nur eines dieser Items True
sein. Näheres ist in https://www.smarthomeng.de/das-traffic-plugin-am-beispiel-eines-staualarms beschrieben.
Tipp: Man kann natürlich auch die „aktuelle“ Koordinate location.lat, location.lon
fest im Item hinterlegen, ohne dieses via EgiGeoZone zu bedaten. Diese Koordinate ist, so lange keine Route angezeigt wird, das Zentrum der Karte!
Google Maps Widget
Für das Widget sind im Ordner dropins/widgets
zwei Dateien zu erstellen. Im Javascript File wird dabei die Karte initialisiert und falls angegeben, die Verkehrsanzeige eingeschaltet. Daneben wird ein Marker auf die Home Koordinate und die aktuelle Position gesetzt. Letzterer ist mit einem Foto hinterlegt.
Im Update-Block werden die jeweiligen Positionen aktualisiert und die aktuelle Route gesetzt (falls eine Route aktuell angezeigt werden soll).
Im JS-File muss bedacht werden, dass der im Rahmen des Traffic Plugins eingerichtete API Key für die Google Directions API angegeben werden muss (<apikey>
)! Zudem kann als Title noch der jeweilige Name <Mein Name>
gesetzt werden.
dropins/widgets/gmaps.js
:
// dropins/widgets/gmaps.js
$.widget("sv.gmaps_map", $.sv.widget, {
initSelector: 'div[data-widget="gmaps_map"]',
options: {
'traffic': null,
},
_create: function () {
this._super();
this._create_map();
},
_create_map: function () {
try {
this.map = new google.maps.Map(this.element[0], {
zoom: 11,
disableDefaultUI: false,
clickableIcons: false,
mapTypeControl: false,
streetViewControl: false
});
}
catch(e) {
if(e.name == "ReferenceError") { // google maps script not loaded yet
var that = this;
// google maps script is already loading in another widget
if(window.google_maps_loading) {
window.setTimeout(function() { that._create_map() }, 100)
return;
}
// google maps script is not loading
window.google_maps_loading = true;
$.ajax({
url: 'https://maps.googleapis.com/maps/api/js?key=<apikey>',
dataType: "script",
complete: function() { window.google_maps_loading = false; that._create_map() }
});
return;
}
else // other exceptions should be trown
throw e;
}
this.directionsService = new google.maps.DirectionsService;
this.directionsDisplay = new google.maps.DirectionsRenderer();
this.directionsDisplay.setMap(this.map);
if (this.options['traffic']) {
var trafficLayer = new google.maps.TrafficLayer();
trafficLayer.setMap(this.map);
}
var pinColorGreen = "20d24a";
var pinImageGreen = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColorGreen,
new google.maps.Size(21, 34),
new google.maps.Point(0,0),
new google.maps.Point(10, 34));
var pinImageMyself = new google.maps.MarkerImage("/smartVISU/pics/phone/myself.jpg" ,
null, null, null, new google.maps.Size(40, 40));
var pinShadow = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_shadow",
new google.maps.Size(40, 37),
new google.maps.Point(0, 0),
new google.maps.Point(12, 35));
this.marker_home = new google.maps.Marker({
map: this.map,
icon: pinImageGreen,
shadow: pinShadow,
title:' Home '
});
this.marker_myself = new google.maps.Marker({
map: this.map,
icon: pinImageMyself,
title:' <Mein Name>',
zIndex:99999999
});
},
_update: function(response) {
if(!this.map) {
var that = this;
window.setTimeout(function() { that._update(response) }, 100)
return;
}
this.map.setCenter({lat: response[0], lng: response[1]});
var pos = new google.maps.LatLng(parseFloat(response[0]),parseFloat(response[1]));
this.marker_myself.setPosition(pos);
var pos_home = new google.maps.LatLng(parseFloat(response[2]),parseFloat(response[3]));
this.marker_home.setPosition(pos_home);
if (response[6] && response[7] && (response[4] == 1 || response[5] == 1)) {
if (response[4] == 1) {
var destination = { lat: parseFloat(response[2]), lng: parseFloat(response[3]) };
} else if (response[5] == 1) {
var destination = { lat: parseFloat(response[6]), lng: parseFloat(response[7]) };
};
var directionsDisplay = this.directionsDisplay;
this.directionsDisplay.setMap(this.map);
this.directionsService.route({
origin: pos,
//provideRouteAlternatives: true,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
}, function(result, status) {
if (status === google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(result);
} else {
console.log('Directions request failed due to ' + status);
}
});
} else {
if(this.directionsDisplay != null) {
this.directionsDisplay.setMap(null);
}
}
}
})
und dropins/widgets/gmaps.html
:
// dropins/widgets/gmaps.html - API Key für Google Directions angeben!
/**
* Displays a google maps map with current, home and work position, as well as routing
*
* @param unique id for this widget
* @param a gad/item for latitude
* @param a gad/item for longitude
* @param a gad/item for home latitude
* @param a gad/item for home longitude
* @param a gad/item for bool flag, if route to home shall be calculated
* @param a gad/item for bool flag, if route to work shall be calculated
* @param a gad/item for work latitude
* @param a gad/item for work longitude
* @param traffic information on (=1) or off (=0)
*/
{% macro map(id, gad_lat, gad_lon, gad_home_lat, gad_home_lon, gad_calculate_way_home, gad_calculate_way_work, gad_work_lat, gad_work_lon, traffic) %}
<div id="{{ uid(page, id) }}" style="width: 100%; height: 400px;" data-traffic="{{ traffic }}" data-widget="gmaps_map" data-item="{{ gad_lat }}, {{ gad_lon}}, {{ gad_home_lat }}, {{ gad_home_lon }}, {{ gad_calculate_way_home }}, {{ gad_calculate_way_work }}, {{ gad_work_lat }}, {{ gad_work_lon }}"></div>
{% endmacro %}
Weiterhin wird ein Bild, welches unter /smartVISU/pics/phone/myself.jpg
abgelegt ist, verwendet! Damit das Widget ordentlich funktioniert, sollte dieses Bild auch dort existieren.
Einbindung in smartVISU 2.9
Da die Widgets mit smartVISU 2.9 automatisch geladen werden, muss nun nur noch das Widget auf der entsprechenden Seite eingebunden werden:
<div class="block" style="width: 100%;">
<div class="set-2" data-role="collapsible-set" data-theme="c" data-content-theme="a" data-mini="true">
<div data-role="collapsible" data-collapsed="false">
<h3>Verkehr:
{{ basic.symbol('travel_info.calculate_way_home', 'travel_info.calculate_way_home', 'Nach Hause via ', '', '1') }}
{{ basic.symbol('travel_info.calculate_way_work', 'travel_info.calculate_way_work', 'Zur Arbeit via ', '', '1') }}
{{ basic.print('travel_info.travel_summary', 'travel_info.travel_summary', 'text') }}
</h3>
{{ gmaps.map('trafficmap', 'location.lat', 'location.lon', 'location.home.lat', 'location.home.lon', 'travel_info.calculate_way_home', 'travel_info.calculate_way_work', 'location.work.lat', 'location.work.lon', '1') }}
</div>
</div>
</div>
Das Ergebnis mit aktivem Arbeitsweg, sieht dann wie folgt aus:
Einfachere Version:
Hier noch eine einfachere Version des Widgets, nur mit Koordinaten der aktuellen Position:
{{ gmaps.simple_map('trafficmap_simple', 'location.lat', 'location.lon') }}
/**
* Displays a simple google maps map
*
* @param unique id for this widget
* @param a gad/item for latitude
* @param a gad/item for longitude
*/
{% macro simple_map(id, gad_lat, gad_lon) %}
<div id="{{ uid(page, id) }}" style="width: 100%; height: 400px;" data-widget="gmaps_simplemap" data-item="{{ gad_lat }}, {{ gad_lon}}"></div>
{% endmacro %}
$.widget("sv.gmaps_simplemap", $.sv.widget, {
initSelector: 'div[data-widget="gmaps_simplemap"]',
_create: function () {
this._super();
this._create_map();
},
_create_map: function () {
try {
this.map = new google.maps.Map(this.element[0], {
zoom: 11,
disableDefaultUI: false,
clickableIcons: false,
mapTypeControl: false,
streetViewControl: false
});
}
catch (e) {
if (e.name == "ReferenceError") { // google maps script not loaded yet
var that = this;
// google maps script is already loading in another widget
if (window.google_maps_loading) {
window.setTimeout(function () {
that._create_map()
}, 100)
return;
}
// google maps script is not loading
window.google_maps_loading = true;
$.ajax({
url: 'https://maps.googleapis.com/maps/api/js?key=<apikey>',
dataType: "script",
complete: function () {
window.google_maps_loading = false;
that._create_map()
}
});
return;
}
else // other exceptions should be thrown
throw e;
}
var pinColorGreen = "20d24a";
var pinImageGreen = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColorGreen,
new google.maps.Size(21, 34),
new google.maps.Point(0,0),
new google.maps.Point(10, 34));
this.marker_myself = new google.maps.Marker({
map: this.map,
icon: pinImageGreen,
title:' <Mein Name>',
zIndex:99999999
});
},
_update: function(response) {
if(!this.map) {
var that = this;
window.setTimeout(function() { that._update(response) }, 100)
return;
}
this.map.setCenter({lat: response[0], lng: response[1]});
var pos = new google.maps.LatLng(parseFloat(response[0]),parseFloat(response[1]));
this.marker_myself.setPosition(pos);
}
});
0 Kommentare