class ViewMap(BoxLayout): def __init__(self, **kwargs): super(ViewMap, self).__init__(**kwargs) self.build() def build(self): self.mapview = MapView(lat=51.48, lon=-3.17, zoom=11) self.mapLayer = MarkerMapLayer() for i in locations: locationLat = float(locations[i][0]) locationLon = float(locations[i][1]) marker = MapMarker(lat=locationLat, lon=locationLon) self.mapLayer.add_widget(marker) self.mapview.add_layer(self.mapLayer) self.add_widget(self.mapview) def update(self): print('updating map') def nodeExpanded(self): print('expanded node') def nodeColapsed(self): print('node colapsed')
class Editor(GridLayout): current_layer = None edit_last_move = None markers = [] mode = StringProperty("polygon") is_editing = BooleanProperty(False) map_source = ObjectProperty() geojson_fn = StringProperty() title = StringProperty() def __init__(self, **kwargs): super(Editor, self).__init__(**kwargs) self.marker_layer = MarkerMapLayer() self.current_layer = GeoJsonMapLayer() self.result_layer = GeoJsonMapLayer() self.result_layer.geojson = kwargs["geojson"] self.geojson_fn = kwargs["geojson_fn"] self.ids.mapview.add_widget(self.current_layer) self.ids.mapview.add_widget(self.result_layer) self.ids.mapview.add_widget(self.marker_layer) def on_touch_down(self, touch): if self.ids.mapview.collide_point(*touch.pos): if touch.is_double_tap and not self.is_editing: feature = self.select_feature(*touch.pos) if feature: self.edit_feature(feature) else: touch.grab(self) self.forward_to_object(touch) return True elif touch.is_double_tap and self.is_editing: if self.remove_marker_at(touch): return True touch.grab(self) return self.forward_to_object(touch) elif not self.is_editing: self.select_feature(*touch.pos) return self.ids.mapview.on_touch_down(touch) return super(Editor, self).on_touch_down(touch) def on_touch_move(self, touch): if touch.grab_current == self and self.is_editing: return self.forward_to_object(touch, move=True) return super(Editor, self).on_touch_move(touch) def switch_mode(self): if self.is_editing: self.finalize_object() return self.mode = "line" if self.mode == "polygon" else "polygon" def forward_to_object(self, touch, move=False): mapview = self.ids.mapview if not self.is_editing: self.is_editing = True self.clear_markers() coord = mapview.get_latlon_at(*touch.pos) if move: m = self.markers[-1] else: m = EditorMarker() m.mapview = mapview m.editor = self self.markers.append(m) self.marker_layer.add_widget(m) self.marker_layer.reposition() m.lat = coord.lat m.lon = coord.lon self._update_geojson() return True def clear_markers(self): while self.markers: self.remove_marker(self.markers.pop()) def remove_marker(self, m): m.mapview = m.editor = None self.marker_layer.remove_widget(m) if m in self.markers: self.markers.remove(m) self._update_geojson() def remove_marker_at(self, touch): pos = self.ids.mapview.to_local(*touch.pos) for marker in self.markers[:]: if marker.collide_point(*pos): self.remove_marker(marker) return True def finalize_object(self): if self.markers: geojson = self.current_layer.geojson if "properties" not in geojson: geojson["features"][0]["properties"] = {"title": self.title} else: geojson["features"][0]["properties"]["title"] = self.title self.result_layer.geojson["features"].extend(geojson["features"]) self.ids.mapview.trigger_update(True) self.clear_markers() self.is_editing = False self.title = "" self._update_geojson() def save_geojson(self): with open(self.geojson_fn, "wb") as fd: json.dump(self.result_layer.geojson, fd) def _update_geojson(self): features = [] if self.mode == "polygon": geotype = "Polygon" geocoords = lambda x: [x] else: geotype = "LineString" geocoords = lambda x: x # current commited path if self.markers: coordinates = [[c.lon, c.lat] for c in self.markers] features.append({ "type": "Feature", "properties": {}, "geometry": { "type": geotype, "coordinates": geocoords(coordinates) } }) geojson = {"type": "FeatureCollection", "features": features} self.current_layer.geojson = geojson self.ids.mapview.trigger_update(True) def point_inside_polygon(self, x, y, poly): n = len(poly) inside = False p1x, p1y = poly[0] for i in range(n + 1): p2x, p2y = poly[i % n] if y > min(p1y, p2y) and y <= max(p1y, p2y) and x <= max(p1x, p2x): if p1y != p2y: xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x if p1x == p2x or x <= xinters: inside = not inside p1x, p1y = p2x, p2y return inside def select_feature(self, x, y): coord = self.ids.mapview.get_latlon_at(x, y) for feature in self.result_layer.geojson["features"]: if feature["type"] != "Feature": continue geometry = feature["geometry"] if geometry["type"] == "Polygon": if self.point_inside_polygon(coord.lon, coord.lat, geometry["coordinates"][0]): return feature def edit_feature(self, feature): self.result_layer.geojson["features"].remove(feature) #self.current_layer.geojson = feature self.ids.mapview.trigger_update(True) self.title = feature.get("properties", {}).get("title", "") self.is_editing = True self.clear_markers() for c in feature["geometry"]["coordinates"][0]: m = EditorMarker(lon=c[0], lat=c[1]) m.mapview = self.ids.mapview m.editor = self self.marker_layer.add_widget(m) self.markers.append(m) self._update_geojson()
class ISSScreen(Screen): def __init__(self, **kwargs): super(ISSScreen, self).__init__(**kwargs) # Set the path for the folder self.path = os.path.dirname(os.path.abspath(__file__)) # Set the path for local images self.imagefolder = os.path.join(self.path, "images") # Ephem calculates the position using the Two Line Element data # We need to make sure we have up to date info tle = self.get_TLE() # Create an iss object from which we can get positional data self.iss = ephem.readtle(*tle) # Run the calculations self.iss.compute() # Get positon of iss and place a marker there lat, lon = self.get_loc() self.marker = MapMarker(lat=lat, lon=lon) # Create a value to check when we last drew ISS path self.last_path_update = 0 # Create the path icon self.path_icon = os.path.join(self.imagefolder, "dot.png") # Create the world map self.map = MapView(id="mpv", lat=0, lon=0, zoom=1, scale=1.5) x, y = self.map.get_window_xy_from(0, 0, 1) self.map.scale_at(1.2, x, y) # Add the ISS marker to the map and draw the map on the screen self.map.add_widget(self.marker) self.add_widget(self.map) # Add a new layer for the path self.mmlayer = MarkerMapLayer() self.draw_iss_path() self.timer = None def on_enter(self): self.timer = Clock.schedule_interval(self.update, 1) def on_leave(self): Clock.unschedule(self.timer) def utcnow(self): return (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds() def draw_iss_path(self): # Path is drawn every 5 mins if self.utcnow() - self.last_path_update > 30: try: self.map.remove_layer(self.mmlayer) except: pass self.mmlayer = MarkerMapLayer() # Create markers showing the ISS's position every 5 mins for i in range(20): lat, lon = self.get_loc(datetime.now() + timedelta(0, i * 300)) self.mmlayer.add_widget( MapMarker(lat=lat, lon=lon, source=self.path_icon)) # Update the flag so we know when next update should be run self.last_path_update = self.utcnow() # Add the layer and call the reposition function otherwise the # markers don't show otherwise! self.map.add_layer(self.mmlayer) self.mmlayer.reposition() def get_TLE(self): # Set some flags need_update = False # Set our data source and the name of the object we're tracking source = "http://www.celestrak.com/NORAD/elements/stations.txt" ISS = "ISS (ZARYA)" # Get the current time utc_now = self.utcnow() # Set the name of our file to store data data = os.path.join(self.path, "iss_tle.json") # Try loading old data try: with open(data, "r") as savefile: saved = json.load(savefile) # If we can't create a dummy dict except IOError: saved = {"updated": 0} # If old data is more than an hour hold, let's check for an update if utc_now - saved["updated"] > 3600: need_update = True # If we don't have any TLE data then we need an update if not saved.get("tle"): need_update = True if need_update: # Load the TLE data raw = requests.get(source).text # Split the data into a neat list all_sats = [sat.strip() for sat in raw.split("\n")] # Find the ISS and grab the whole TLE (three lines) iss_index = all_sats.index(ISS) iss_tle = all_sats[iss_index:iss_index + 3] # Prepare a dict to save our data new_tle = {"updated": utc_now, "tle": iss_tle} # Save it with open(data, "w") as savefile: json.dump(new_tle, savefile, indent=4) # ephem needs strings not unicode return [str(x) for x in iss_tle] else: # return the saved data (as strings) return [str(x) for x in saved["tle"]] def update(self, *args): # Update the ISS with newest TLE self.iss = ephem.readtle(*self.get_TLE()) # Get the position and update marker lat, lon = self.get_loc() self.marker.lat = lat self.marker.lon = lon self.map.remove_widget(self.marker) self.map.add_widget(self.marker) # Check if the path needs redrawing self.draw_iss_path() def get_loc(self, dt=None): # We can get the location for a specific time as well if dt is None: self.iss.compute() else: self.iss.compute(dt) # Convert the ephem data into something that the map can use lat = float(self.iss.sublat / ephem.degree) lon = float(self.iss.sublong / ephem.degree) return lat, lon
class ISSScreen(Screen): def __init__(self, **kwargs): super(ISSScreen, self).__init__(**kwargs) # Set the path for the folder self.path = os.path.dirname(os.path.abspath(__file__)) # Set the path for local images self.imagefolder = os.path.join(self.path, "images") # Ephem calculates the position using the Two Line Element data # We need to make sure we have up to date info tle = self.get_TLE() # Create an iss object from which we can get positional data self.iss = ephem.readtle(*tle) # Run the calculations self.iss.compute() # Get positon of iss and place a marker there lat, lon = self.get_loc() self.marker = MapMarker(lat=lat, lon=lon) # Create a value to check when we last drew ISS path self.last_path_update = 0 # Create the path icon self.path_icon = os.path.join(self.imagefolder, "dot.png") # Create the world map self.map = MapView(id="mpv",lat=0, lon=0, zoom=1, scale=1.5) x, y = self.map.get_window_xy_from(0,0,1) self.map.scale_at(1.2, x, y) # Add the ISS marker to the map and draw the map on the screen self.map.add_widget(self.marker) self.add_widget(self.map) # Add a new layer for the path self.mmlayer = MarkerMapLayer() self.draw_iss_path() self.timer = None def on_enter(self): self.timer = Clock.schedule_interval(self.update, 1) def on_leave(self): Clock.unschedule(self.timer) def utcnow(self): return (datetime.utcnow() - datetime(1970,1,1)).total_seconds() def draw_iss_path(self): # Path is drawn every 5 mins if self.utcnow() - self.last_path_update > 30: try: self.map.remove_layer(self.mmlayer) except: pass self.mmlayer = MarkerMapLayer() # Create markers showing the ISS's position every 5 mins for i in range(20): lat, lon = self.get_loc(datetime.now() + timedelta(0, i * 300)) self.mmlayer.add_widget(MapMarker(lat=lat, lon=lon, source=self.path_icon)) # Update the flag so we know when next update should be run self.last_path_update = self.utcnow() # Add the layer and call the reposition function otherwise the # markers don't show otherwise! self.map.add_layer(self.mmlayer) self.mmlayer.reposition() def get_TLE(self): # Set some flags need_update = False # Set our data source and the name of the object we're tracking source = "http://www.celestrak.com/NORAD/elements/stations.txt" ISS = "ISS (ZARYA)" # Get the current time utc_now = self.utcnow() # Set the name of our file to store data data = os.path.join(self.path, "iss_tle.json") # Try loading old data try: with open(data, "r") as savefile: saved = json.load(savefile) # If we can't create a dummy dict except IOError: saved = {"updated": 0} # If old data is more than an hour hold, let's check for an update if utc_now - saved["updated"] > 3600: need_update = True # If we don't have any TLE data then we need an update if not saved.get("tle"): need_update = True if need_update: # Load the TLE data raw = requests.get(source).text # Split the data into a neat list all_sats = [sat.strip() for sat in raw.split("\n")] # Find the ISS and grab the whole TLE (three lines) iss_index = all_sats.index(ISS) iss_tle = all_sats[iss_index:iss_index + 3] # Prepare a dict to save our data new_tle = {"updated": utc_now, "tle": iss_tle} # Save it with open(data, "w") as savefile: json.dump(new_tle, savefile, indent=4) # ephem needs strings not unicode return [str(x) for x in iss_tle] else: # return the saved data (as strings) return [str(x) for x in saved["tle"]] def update(self, *args): # Update the ISS with newest TLE self.iss = ephem.readtle(*self.get_TLE()) # Get the position and update marker lat, lon = self.get_loc() self.marker.lat = lat self.marker.lon = lon self.map.remove_widget(self.marker) self.map.add_widget(self.marker) # Check if the path needs redrawing self.draw_iss_path() def get_loc(self, dt=None): # We can get the location for a specific time as well if dt is None: self.iss.compute() else: self.iss.compute(dt) # Convert the ephem data into something that the map can use lat = float(self.iss.sublat / ephem.degree) lon = float(self.iss.sublong / ephem.degree) return lat, lon