class Grass2Leaflet(object): def __init__(self, grassimg): self.grassimg = grassimg self.draw_control = None self.zoom = 15 self.center = self.centermap() self.m = Map(default_tiles=TileLayer(opacity=1.0), center=self.center, zoom=self.zoom) def centermap(self): centerlat = [] centerlon = [] for i in self.grassimg: centerlat.append(self.grassimg[i]['C'][0]) centerlon.append(self.grassimg[i]['C'][1]) center = (sum(centerlat) / float(len(centerlat)), sum(centerlon) / float(len(centerlon))) return center def imgoverlays(self): self.leafletimg = OrderedDict() for i in self.grassimg: layer = ImageOverlay(url=self.grassimg[i]['raster'], bounds=(self.grassimg[i]['LL'], self.grassimg[i]['UR'])) self.leafletimg[i] = layer def render(self, draw_control=None): self.imgoverlays() self.dc = None options = ['None'] self.m.add_layer(self.leafletimg[list(self.grassimg.keys())[-1]]) if len(self.grassimg) >= 2: self.maplist = widgets.Dropdown( options=options + list(self.grassimg.keys()), value=list(self.grassimg.keys())[-1], description='Select Layer:', ) self.maplist.observe(self.on_value_change, names='value') display(self.maplist) if draw_control: self.dc = DrawControl() self.dc.on_draw(handle_draw) self.m.add_control(self.dc) display(self.m) return {'map': self.m, 'drawer': self.dc} def on_value_change(self, layername): self.m.clear_layers() self.m.add_layer(TileLayer(opacity=1.0)) if self.maplist.value != 'None': self.m.add_layer(self.leafletimg[layername['new']]) def main(self): self.imgoverlays() self.render()
def create_map(normalized_image: np.ndarray) -> Map: """ Creates leaflet map with given image :param normalized_image: Image data normalized to 0-255 8-bit integer :return: Leaflet map """ width = normalized_image.shape[0] height = normalized_image.shape[1] bounds = [(-width / 2, -height / 2), (width / 2, height / 2)] layer = ImageOverlay(url=serialize_to_url(normalized_image), bounds=bounds) leaflet = Map(center=[0, 0], zoom=1, interpolation='nearest') leaflet.clear_layers() leaflet.add_layer(layer) return leaflet
def clickablemap(center=[48.790153, 2.327395], zoom=13, layout=ipywidgets.Layout(width='100%', height='500px')): # look at: http://leaflet.github.io/Leaflet.draw/docs/examples/basic.html import json from ipyleaflet import (Map, Rectangle, Polygon, TileLayer, ImageOverlay, DrawControl, GeoJSON) #%matplotlib inline # %matplotlib notebook # google tileserver # https://stackoverflow.com/questions/9394190/leaflet-map-api-with-google-satellite-layer mosaicsTilesURL = 'https://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}' # Hybrid: s,h; Satellite: s; Streets: m; Terrain: p; # Map Settings # Define colors colors = {'blue': "#009da5"} # Define initial map center lat/long #center = [48.790153, 2.327395] # Define initial map zoom level #zoom = 13 # Create the map m = Map(center=center, zoom=zoom, scroll_wheel_zoom=True, layout=layout) # using custom basemap m.clear_layers() m.add_layer(TileLayer(url=mosaicsTilesURL)) # Define the draw tool type options polygon = {'shapeOptions': {'color': colors['blue']}} rectangle = {'shapeOptions': {'color': colors['blue']}} ## Create the draw controls ## @see https://github.com/ellisonbg/ipyleaflet/blob/master/ipyleaflet/leaflet.py#L293 #dc = DrawControl( # polygon = polygon, # rectangle = rectangle #) dc = DrawControl( polygon={'shapeOptions': { 'color': '#0000FF' }}, polyline={'shapeOptions': { 'color': '#0000FF' }}, circle={'shapeOptions': { 'color': '#0000FF' }}, rectangle={'shapeOptions': { 'color': '#0000FF' }}, ) # Initialize an action counter variable m.actionCount = 0 m.AOIs = [] # Register the draw controls handler def handle_draw(self, action, geo_json): # Increment the action counter #global actionCount m.actionCount += 1 # Remove the `style` property from the GeoJSON geo_json['properties'] = {} # Convert geo_json output to a string and prettify (indent & replace ' with ") geojsonStr = json.dumps(geo_json, indent=2).replace("'", '"') m.AOIs.append(json.loads(geojsonStr)) # Attach the draw handler to the draw controls `on_draw` event dc.on_draw(handle_draw) m.add_control(dc) # add a custom function to create and add a Rectangle layer # (LESS USEFUL THAN add_geojson) def add_rect(*args, **kwargs): r = Rectangle(*args, **kwargs) return m.add_layer(r) m.add_rectangle = add_rect # add a custom function to create and add a Polygon layer def add_geojson(*args, **kwargs): # ugly workaround to call without data=aoi if 'data' not in kwargs: kwargs['data'] = args[0] args2 = [i for i in args[1:-1]] else: args2 = args r = GeoJSON(*args2, **kwargs) return m.add_layer(r) m.add_GeoJSON = add_geojson # Display return m
def overlaymap(aoiY, imagesurls, zoom=13, layout=ipywidgets.Layout(width='100%', height='500px')): import json import numpy as np from ipyleaflet import ( Map, Rectangle, Polygon, TileLayer, ImageOverlay, DrawControl, ) ## handle the case of imageurls not a list if type(imagesurls) != list: imagesurls = [imagesurls] number_of_images = len(imagesurls) ## handle both kinds of calls with aoi, or aoi['coordinates'] if 'coordinates' in aoiY: aoiY = aoiY['coordinates'][0] # create the Map object # google tileserver # https://stackoverflow.com/questions/9394190/leaflet-map-api-with-google-satellite-layer mosaicsTilesURL = 'https://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}' # Hybrid: s,h; Satellite: s; Streets: m; Terrain: p; m = Map( center=aoiY[0][::-1], zoom=zoom, scroll_wheel_zoom=True, layout=layout, ) # using custom basemap m.clear_layers() m.add_layer(TileLayer(url=mosaicsTilesURL, opacity=1.00)) #vlayer = VideoOverlay(videoUrl, videoBounds ) #m.add_layer(vlayer) ### this shows an animated gif #m.add_layer(layer) # display map (this show) #display(m) ############## ADD INTERACTIVE LAYER from ipywidgets import interact, interactive, fixed, interact_manual import ipywidgets as widgets # meke sure that the images have unique names imagesurls = [ '%s?%05d' % (i, np.random.randint(10000)) for i in imagesurls ] # draw bounding polygon y = [a[::-1] for a in aoiY] p = Polygon(locations=y, weight=2, fill_opacity=0.25) m.add_layer(p) # create image layer = ImageOverlay(url='%s' % (imagesurls[0]), bounds=[ list(np.max(aoiY, axis=0)[::-1]), list(np.min(aoiY, axis=0)[::-1]) ]) m.add_layer(layer) # callback fro flipping images def showim(i): if (i < len(imagesurls)): # ----- FLICKERS ---- # layer.url='%s'%(imagesurls[i]) # layer.visible = False # layer.visible = True # ALTERNATIVE: add a new layer layer = ImageOverlay(url='%s' % (imagesurls[i]), bounds=[ list(np.max(aoiY, axis=0)[::-1]), list(np.min(aoiY, axis=0)[::-1]) ]) m.add_layer(layer) # remove old ones if len(m.layers) > 30: # image buffer for l in (m.layers[1:-1]): m.remove_layer(l) # build the UI #interact(showim,i=len(imagesurls)-1) #interact(showim, i=widgets.IntSlider(min=0,max=len(imagesurls),step=1,value=0)); play = widgets.Play( interval=200, #ms value=0, min=0, max=len(imagesurls) - 1, step=1, description="Press play", disabled=False, ) slider = widgets.IntSlider(min=0, max=len(imagesurls) - 1, description='Frame:') label = widgets.Label(value="") def on_value_change(change): label.value = imagesurls[change['new']] showim(change['new']) slider.observe(on_value_change, 'value') b1 = widgets.Button(description='fw', layout=widgets.Layout(width='auto')) b2 = widgets.Button(description='bw', layout=widgets.Layout(width='auto')) b3 = widgets.Button(description='hide', layout=widgets.Layout(width='auto')) b4 = widgets.Button(description='hidePoly', layout=widgets.Layout(width='auto')) def clickfw(b): slider.value = slider.value + 1 def clickbw(b): slider.value = slider.value - 1 def clickhide(b): if layer.visible: layer.visible = False else: layer.visible = True def clickhidePoly(b): if p.visible: p.visible = False else: p.visible = True b1.on_click(clickfw) b2.on_click(clickbw) b3.on_click(clickhide) b4.on_click(clickhidePoly) # add a custom function to create and add a Polygon layer def add_geojson(*args, **kwargs): # ugly workaround to call without data=aoi if 'data' not in kwargs: kwargs['data'] = args[0] args2 = [i for i in args[1:-1]] else: args2 = args r = GeoJSON(*args2, **kwargs) return m.add_layer(r) m.add_GeoJSON = add_geojson widgets.jslink((play, 'value'), (slider, 'value')) if number_of_images > 1: return widgets.VBox( [widgets.HBox([play, b2, b1, b3, b4, slider, label]), m]) else: return widgets.VBox([widgets.HBox([b3, b4, label]), m])
class Grass2Leaflet(object): def __init__(self, grassimg): self.grassimg = grassimg self.draw_control = None self.zoom = 15 self.center = self.centermap() self.m = Map(default_tiles=TileLayer(opacity=1.0), center=self.center, zoom=self.zoom) def centermap(self): centerlat = [] centerlon = [] for i in self.grassimg: centerlat.append(self.grassimg[i]['C'][0]) centerlon.append(self.grassimg[i]['C'][1]) center = (sum(centerlat) / float(len(centerlat)), sum(centerlon) / float(len(centerlon))) return center def imgoverlays(self): self.leafletimg = OrderedDict() for i in self.grassimg: layer = ImageOverlay(url=self.grassimg[i]['raster'], bounds=(self.grassimg[i]['LL'], self.grassimg[i]['UR'])) self.leafletimg[i] = layer def render(self, draw_control=None, caption=None): self.out = widgets.Output() self.imgoverlays() self.dc = None options = ['None'] self.m.add_layer(self.leafletimg[list(self.grassimg.keys())[-1]]) if len(self.grassimg) >= 2: self.maplist = widgets.Dropdown( options=options + list(self.grassimg.keys()), value=list(self.grassimg.keys())[-1], description='Select Layer:', ) self.maplist.observe(self.on_value_change, names='value') display(self.maplist) if draw_control: self.dc = DrawControl() self.dc.on_draw(handle_draw) self.m.add_control(self.dc) display(self.m) self.lastdraw = widgets.Button( description='Print last draw', disabled=False, button_style='', # 'success', 'info', 'warning', 'danger' or '' tooltip='Print last draw', #icon='check' ) self.lastdraw.on_click(self.on_button_clicked1) if not draw_control: self.lastdraw.disabled = True display(widgets.HBox([self.lastdraw, self.out])) if caption: display( HTML("<center><b>Figure %s:</b> %s<center>" % (caption[0], caption[1]))) return {'map': self.m, 'drawer': self.dc} def on_value_change(self, layername): self.m.clear_layers() self.m.add_layer(TileLayer(opacity=1.0)) if self.maplist.value != 'None': self.m.add_layer(self.leafletimg[layername['new']]) def on_button_clicked1(self, b): with self.out: clear_output() display(self.dc.last_draw) new_draw = GeoJSON(data=self.dc.last_draw) self.m.add_layer(new_draw) #display(dir(self.dc.layer)) def handle_draw_output(self, json): display(json.dumps(geo_json)) def main(self): self.imgoverlays() self.render()
class UbxMap(): def __init__(self, basemapName="Mapnik", animate=False, zoom=16, width="100%", height="720px"): """Parse the UBX File and put it into a DataFrame""" self.OnLocationChanged = None self.OnDataLoaded = None self.OnDataFileChanged = None self._animate = animate # map self._basemaps = { "Mapnik": basemaps.OpenStreetMap.Mapnik, "Satellite": basemaps.Esri.WorldImagery, "WorldStreetMap": basemaps.Esri.WorldStreetMap, "BlackAndWhite": basemaps.OpenStreetMap.BlackAndWhite, "France": basemaps.OpenStreetMap.France, "HOT": basemaps.OpenStreetMap.HOT, "OpenTopoMap": basemaps.OpenTopoMap, "WorldTopoMap": basemaps.Esri.WorldTopoMap, } self._basemapName = basemapName self._basemap = self._basemaps[self._basemapName] self._zoom = zoom self._startLocation = (49, 8) self._m = Map(basemap=self._basemap, center=self._startLocation, zoom=self._zoom, close_popup_on_click=False) self._m.layout.width = width self._m.layout.height = height self._addControls() # parser self._parser = UbxParser() # GUI elements self._fileSelector = widgets.Dropdown(value=self.UbxFiles[0], placeholder="Log Files", options=self.UbxFiles, description="Log File", ensureoption=False, disabled=False) self._fileSelector.observe(self._on_filename_changed, names='value') self._waypointSlider = widgets.IntSlider( value=0, description="Waypoint", min=0, max=0, layout=widgets.Layout(width='50%')) self._waypointSlider.observe(self._on_waypoint_changed, names='value') self._mapSelector = widgets.Dropdown(value=self._basemapName, options=list( self._basemaps.keys()), description="Map", ensureoption=True, disabled=False) self._mapSelector.observe(self._on_mapstyle_changed, names='value') self._upload = FileUpload(accept='.pubx', multiple=True) self._upload.observe(self._on_file_upload_changed, names="value") self._header = HTML( "<b><font size='2' color='orange'>pixelmaps</font></b>") self._gui = widgets.VBox([ widgets.HBox([ self._header, self._fileSelector, self._waypointSlider, self._mapSelector, self._upload ]), self._m ]) @property def CurrentLocation(self): return self._waypointMarker.location @property def RouteLine(self): return self._routeLine @property def Map(self): return self._m @property def WaypointSliderWidget(self): return self._waypointSlider @property def FileSelectorWidget(self): return self._fileSelector @property def MaxNumberOfWaypoints(self): return len(self._df) - 1 @property def UbxFiles(self): ubxFiles = [] dir = os.listdir() ubxFiles = [] for file in dir: if file.endswith(".pubx"): ubxFiles.append(file) if len(ubxFiles) == 0: ubxFiles.append("<NoLogFiles>") return ubxFiles def setCurrentWaypoint(self, value): self._currentWaypoint = value row = self._df.iloc[self._currentWaypoint] speed = row["groundspeed"] gz = row["gravityz"] ml = row["miclevel"] lat = row["latitude"] lng = row["longitude"] head = row["headingofmotion"] self._waypointMessage.value = f"Moving {self._parser.direction(head)} with {speed:.0f} Km/h" #self._waypointMarker.rotation_angle = head self._waypointMarker.location = (lat, lng) self._center() def Append(self, filename): self._filename = filename self._df = self._df.append( DataFrame.from_dict(self._parser.read(self._filename))) self._updateData() def Load(self, filename="<First>"): if filename == "<First>": self._filename = self.UbxFiles[0] if self._filename == "<NoLogFiles>": return else: self._filename = filename self._df = DataFrame.from_dict(self._parser.read(self._filename)) self._updateData() self._clearLayers() def _updateData(self): # Get the start location self._startLocation = (self._df.at[0, 'latitude'], self._df.at[0, 'longitude']) # Calculate the route average speed self._averageSpeed = self._df["groundspeed"].mean() self._minSpeed = self._df["groundspeed"].min() self._maxSpeed = self._df["groundspeed"].max() self._averageHOF = self._df["headingofmotion"].mean() # Get the route points to draw the route self._routePoints = self._df.loc[:, ["latitude", "longitude" ]].values.tolist() self._waypointSlider.max = self.MaxNumberOfWaypoints if self.OnDataLoaded: self.OnDataLoaded(self) def _addControls(self): # Add Controls self._m.add_control( MeasureControl(position='bottomleft', active_color='orange', primary_length_unit='kilometers')) self._m.add_control(FullScreenControl()) self._m.add_control(ScaleControl(position='bottomleft')) searchMarker = Marker(icon=AwesomeIcon( name="check", marker_color='green', icon_color='darkred')) self._m.add_control( SearchControl( position="topleft", url= 'https://nominatim.openstreetmap.org/search?format=json&q={s}', zoom=self._zoom, marker=searchMarker)) def Display(self): self._fileSelector.options = self.UbxFiles self._waypointSlider.max = self.MaxNumberOfWaypoints return self._gui def move(self, location): self._waypointMarker.location = location def _on_filename_changed(self, change): newFile = change["new"] self.Load(newFile) self.DrawRoute() if self.OnDataFileChanged: self.OnDataFileChanged(self, change["new"]) def _on_file_upload_changed(self, change): newFile = change["new"] with open(newFile, 'wb') as output_file: for uploaded_filename in self._upload.value: content = self._upload.value[uploaded_filename]['content'] output_file.write(content) def _on_mapstyle_changed(self, change): self._basemapName = change["new"] self._m.layers = [basemap_to_tiles(self._basemaps[self._basemapName])] self.DrawRoute() def _on_waypoint_changed(self, change): self._currentWaypoint = int(change['new']) self.setCurrentWaypoint(self._currentWaypoint) def _on_location_changed(self, event): # Do some computation given the new marker location, accessible from `event['new'] print(event["new"]) def _addWaypoint(self): # Waypoint self._waypointMarker = Marker(location=self._startLocation, draggable=False) self._waypointMarker.observe(self._on_location_changed, 'location') self._m.add_layer(self._waypointMarker) # Waypoint Popup Message self._waypointMessage = HTML() self._waypointMarker.popup = self._waypointMessage def DrawRoute(self): # Draw the route #self._clearLayers() self._routeLine = AntPath( locations=self._routePoints, dash_array=[1, int(self._averageSpeed)], delay=int(self._averageSpeed * 500), color="red", pulse_color="blue") #color='#7590ba',pulse_color='#3f6fba') self._m.add_layer(self._routeLine) self._addWaypoint() self._center() self._addStats() if self._animate: self._center() def _center(self): self._m.center = self._waypointMarker.location def _centerTo(self, location): self._m.center = location def _clearLayers(self): self._m.clear_layers() self._m.layers = [basemap_to_tiles(self._basemap)] def calcSpeedZGravityThreshold(self, speed, grvityz): threshold = 10.5 if speed < 6: #walking threshold = 12.5 elif speed >= 6 and speed < 10: # running threshold = 14.5 elif speed > 10 and speed < 25: # bike threshold = 16.5 return threshold def _addStats(self): oldloc = (0, 0) # Iterate through the route points for index, row in self._df.iterrows(): # Get various row info speed = row["groundspeed"] gz = row["gravityz"] ml = row["miclevel"] lat = row["latitude"] lng = row["longitude"] loc = (lat, lng) # Add speed markers if speed < 2 and self._parser.distance(loc, oldloc) > 0.01: #m.add_layer(Marker(location=loc, draggable=False, title="Stopped")) self._m.add_layer( Circle(location=loc, radius=5, color="green", fill_color="green")) oldloc = loc if self._animate: self._centerTo(loc) # Add Z Gravity markers (bumps) if gz > self.calcSpeedZGravityThreshold(speed, gz): self._m.add_layer( Circle(location=loc, radius=int(gz / 2), color="red", fill_color="red")) if self._animate: self._centerTo(loc) # Add Sound Markers if ml >= 1.5: self._m.add_layer( Circle(location=loc, radius=int(ml * 5), color="orange", fill_color="orange")) if self._animate: self._centerTo(loc)