Widget request: Google maps with multiple pins

Hi,

I’ll start by being bluntlty honest: I am unable to create what I am going to propose, but hopefully someone else is more capable than me.
So the situation is, that I have several phones, the locations of which I’d like to plot on the same map in HABpanel. I have the locations in OpenHAB, so the data is available.
I came across this as a solution for the multiple pins, but I am hopelessly unable to adapt it for use in HABpanel. Anyone willing to try?

Found an angularJS implementation of Google Maps and played with one of their examples.
I know that the code is total bs and nowhere near actually providing usefull info. However thanks to the help of the brilliant @ysc on a previous problem, I believe it could be made into a HABpanel widget.

Well this is a boring monologue but while Googling around I stumbled upon https://ngmap.github.io/. Looks very powerful yet simple. Still facing the same issue though that I personally lack the competence to bring it to HABpanel.

are you looking for something like this?

If you have the data in openhab, it can be done. You will need a Google API key. Have a look at the html file in this tutorial. You should be able to adapt it to your needs.

I did not find that when searching, or then I overlooked it. That looks exactly right! Just one question: does the page periodically refresh when locations change?

Unfortunately, the page is pretty much static and will not update when items update. You can however, set it up to refresh at an interval of your choosing. From your original question, it looks like you are wanting to get this in habpanel. You will want to use a frame widget, which has a setting for refresh interval.

image

You could probably use ui-leaflet to display a map in a widget.

It can use OpenStreetMap tiles which means you won’t have to get an API key.

To display markers this example might help: http://angular-ui.github.io/ui-leaflet/#!/examples/marker

Thank you once again! But yes, you guessed it, I can’t get it working…
Here’s what I’ve done:
I downloaded angular-simple-logger.js, leaflet.js and ui-leaflet.jsand placed them in the static folder.
Next i created a map.controller.js, which looks like this:

angular
    .module('app.widgets')
	.controller('MarkerController', function ($scope) {

    angular.extend($scope, {
        osloCenter: {
            lat: 59.91,
            lng: 10.75,
            zoom: 12
        },
        markers: {
            osloMarker: {
                lat: 59.91,
                lng: 10.75,
                message: "I want to travel here!",
                focus: true,
                draggable: false
            }
        },
        defaults: {
            scrollWheelZoom: false
        }
    });
});

So it’s basically what’s on the page @ysc linked to, but slightly adapted to what he gave me for another problem I had.
Then for the template widget I have:

<div oc-lazy-load="'/static/leaflet.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/angular-simple-logger.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/ui-leaflet.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/map.controller.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div ng-controller="MarkerController">
	<leaflet id="map-marker"></leaflet>
</div>

I get no error and something is being loaded, but it doesn’t look quite right, as you can see:


I’m probably doing something very basic very wrong, but I have no clue what and how to fix…

@mikaelgu you might be running into this:

(it’s not from ui-leaflet but the library it was forked from, so it still applies)
Basically the size of the widget is wrong because it’s computed before the gridster (dashboard grid) is completely drawn, so it’s confused. Does it fix itself if you resize the window? If so you might need to call invalidateSize() somehow.

Here’s the example mentioned in the GitHub thread (the link 404’s):

http://angular-ui.github.io/ui-leaflet/examples/0000-viewer.html#/basic/hide-show-map-example

I’ll have a look later.

You are correct!
First of all I had forgotten to loadthe css that’s mandatory. After adding that and doing some fine tuning it’s mostly working. It does ofter require a resize to properly fill up the widget, but the map is whole and draggable and such.
On my iPad it renders the marker fine even, but on Chrome it’s not showing the marker. The controller is unchanged, but the widget not looks like:

<div oc-lazy-load="'/static/leaflet.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/angular-simple-logger.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/ui-leaflet.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/map.controller.js'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div oc-lazy-load="'/static/leaflet.css'" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<div ng-controller="MarkerController">
	<leaflet id="map-marker" markers="markers" lf-center="osloCenter" height="400px" width="100%"></leaflet>
</div>

Now I obviously need to figure out how to pass the item state to the controller, but I think there’s a thread about that already.

Yeah, I had a look and made it work after some struggle with the markers which would not show up, so I used the leaflet-vector-markers additional library as demonstrated here:
http://angular-ui.github.io/ui-leaflet/examples/0000-viewer.html#/markers/icons-example
That way you could have different colors/icons for different markers.

So you need to download those files in conf/html

The controller conf/html/map.controller.js:

angular
.module('app.widgets.map', ['app.services', 'ui-leaflet'])
.controller('MapController', ['$scope', '$timeout', 'leafletData', 'OHService',
      function ($scope, $timeout, leafletData, OHService) {

    // Creates a red vector marker icon
    var vectorMarkerIcon = {
        type: 'vectorMarker',
        icon: 'user',
        prefix: 'glyphicon',
        markerColor: 'red'
    };

    angular.extend($scope, {
        center: {
            lat: 59.91,
            lng: 10.75,
            zoom: 12,
        },
        markers: {
        },
        defaults: {
            scrollWheelZoom: false
        }
    });

    function onResized() {
        leafletData.getMap().then(function(map) {
            $timeout(function() {
                map.invalidateSize();
            });
        });
    }

    var resizedHandler = $scope.$on('gridster-resized', onResized);
    $scope.$on('$destroy', resizedHandler);

    OHService.onUpdate($scope, null, function (event) {
        leafletData.getMap().then(function(map) {
            $timeout(function() {
                map.invalidateSize();

                var item = OHService.getItem('DemoLocation');
                if (item && item.state.indexOf(',') > 0) {
                    $scope.markers.mylocation = {
                        lat: parseFloat(item.state.split(',')[0]),
                        lng: parseFloat(item.state.split(',')[1]),
                        message: item.label,
                        icon: vectorMarkerIcon
                    };
                }
            });
        });
    });
    
}]);

And the template (note the undocumented ocLazyLoad syntax to load the files sequentially):

<div oc-lazy-load="{ files: [
                       '/static/leaflet.css',
                       '/static/leaflet.js',
                       '/static/leaflet-vector-markers.css',
                       '/static/leaflet-vector-markers.min.js',
                       '/static/angular-simple-logger.min.js',
                       '/static/ui-leaflet.min.js',
                       '/static/map.controller.js'
                     ],
                     serie: true
                   }"
     style="position: absolute; left: 0; top: 0; width: 100%; height: 100%"
     data-snap-ignore="true">

  <div ng-controller="MapController"
       style="position: absolute; left: 0; top: 0; width: 100%; height: 100%">
    <leaflet center="center"
             markers="markers"
             defaults="defaults"
             style="position: absolute; left: 0; top: 0; width: 100%; height: 100%"></leaflet>
  </div>
</div>

Replace the DemoLocation hardcoded item by e.g. $scope.config.item if you’re making a custom widget (bonus points if you share it afterwards in the gallery!)

Location DemoLocation "My Location"

You can adapt the code for multiple items, and there’s room for improvement (don’t do anything if the item hasn’t changed for example).

This could really use to be a reusable widget, or even a built-in one if there’s sufficient demand for it.

I ended up converting the above into a custom widget ready to install from the widget gallery (and a git clone). This is a good example of distributing a widget and its supporting files efficiently through a repository.