Wednesday, 26 February 2014

Markers, popups and labels with leaflet and openstreetmap

It took me a while to get this code working right, and now I have I thought I'd share/store it. It could probably do with a little refactoring by outsourcing some of it to other methods, but it's good to go and its linear nature makes it easier to post here. I started this off using google maps but the performance was terrible and after a bit of zooming/panning the app would crash when running on a device. So I opted for leaflet with openstreetmap and I'm really please with the results.

What I'm doing with this code is plotting 40 or so holiday parks with markers and labels, with an associated popup when clicked. In the popup is a button (a JQM widget) that performs an action. Further to this I wanted to switch the marker for a larger one and hide/show labels at a certain zoom level, and I also wanted to specify different label orientations (top,right,left,bottom) for each park.

The end result looks something like this:



                var self=this;
                self.show_loader("Loading map data...");

                //set initial zoom depending on device
                var initZoom = 7;
                if(self.deviceIsMobile()){ initZoom = 6; }

                //init map with centre around Nottingham
                var map ='map_canvas').setView([52.946104,-1.170044], initZoom);
                //add copyright - v important!
                L.tileLayer('http://{s}{z}/{x}/{y}.png', {
                    attribution: 'Map data © OpenStreetMap contributors',
                    maxZoom: 18,
                    minZoom: 6

                //get park data from local storage
                    var parkListLite = $.parseJSON(window.localStorage.getItem(self.localStorageKeys.parkListLite));
                   //declare empty arrays
                    var locations = [];
                    var markers = [];

                    //get label positiong (t,r,b,l)
                    var labelPositions = self.settings.mapLabelPositions();
                    //loop through park data, create location object with everything we need,
                    //and push it to locations array
                    $(parkListLite).each(function(i, obj){ 
                        var location = [];

                        //popup html
                        var calloutMarkup = String.format('


'+ '{1} '+ 'From {4}', obj['ParkName'], obj['MapCopy'], obj['EPiServerPageId'], self.endpoints.img_url + obj['MapThumbnail'], self.utils.makePriceFriendly(obj['CheapestPrice'],"£") ); var wrapper = document.createElement("div"); wrapper.innerHTML = calloutMarkup; wrapper.className="mapCallout"; location[0] = obj['ParkName']; location[1] = obj['Latitude']; location[2] = obj['Longitude']; location[3] = wrapper; location[4] = labelPositions[obj['ParkName']]; location[5] = ''+ obj['ParkName'] +''; locations.push(location); }); //create icons var mapMarkerSmall, mapMarkerLarge, iconAnchor; $(locations).each(function(i, loc){ //label anchor influences where the popup appears //here we check if we want top, left, right or bottom //and specify the anchor and css accordingly switch(loc[4]) { case 't': labelAnchor= [-128,-63]; labelClass = "mapLabel top"; break; case 'r': labelAnchor= [-263,-24]; labelClass = "mapLabel right"; break; case 'b': labelAnchor= [-128,25]; labelClass = "mapLabel bottom"; break; case 'l': labelAnchor= [0,-24]; labelClass = "mapLabel left"; break; default: labelAnchor= [0,-24]; labelClass = "mapLabel left"; } //create the large and small icon for each park, with the anchor specified above mapMarkerSmall = L.icon({ iconUrl: 'img/mapParkMarker.png', iconRetinaUrl: 'img/mapParkMarker.png', iconSize: [20, 20], iconAnchor: [10,10], popupAnchor: [0,-20], labelAnchor: labelAnchor }); mapMarkerLarge = L.icon({ iconUrl: 'img/mapMarkerLarge.png', iconRetinaUrl: 'img/mapMarkerLarge.png', iconSize: [130, 130], iconAnchor: [65,65], popupAnchor: [0,-20], labelAnchor: labelAnchor }); //add marker to map with label var marker = L.marker([loc[1], loc[2]], {icon: mapMarkerSmall}).bindLabel(loc[5], {noHide: true,direction: 'left',className:labelClass}).addTo(map); //bind popup to marker marker.bindPopup(loc[3]); //we can assign arbitrary properties to marker. here we assign small and large markers marker.iconSmall = mapMarkerSmall; marker.iconLarge = mapMarkerLarge; //hide labels on initial load map.removeLayer(marker.label); //push marker to marker array markers.push(marker); }); //for some zooms we need to show/hide labels or switch out markers for larger/smaller map.on('zoomend', function(event) { var zoom = map.getZoom(); var markerLabelClass; for (i = 0; i < locations.length; i++) { markerLabelClass = markers[i].labelClass; if(zoom <= 10){ markers[i].setIcon(markers[i].iconSmall); map.removeLayer(markers[i].label); }else{ markers[i].setIcon(markers[i].iconLarge); map.addLayer(markers[i].label); } } }); //capture popup open event and run JQM create so it creates the button widget //also assign click event to park view button map.on('popupopen', function(e) { $('#map_canvas').trigger('create'); $('.parkButton').unbind(); $('.parkButton').on('click',function(){ self.setLocalStorage(self.localStorageKeys.lastRequestedParkId,$(this).data('parkPageId')); self.utils.notify('Not yet implemented','OK'); //$.mobile.changePage('#parkoverview'); }); }); self.hide_loader(); }

No comments:

Post a Comment

Comments are moderated, so you'll have to wait a little bit before they appear!