Beispiel #1
0
 def build(self):
     self.mapview = MapView(zoom=20, lat=45.47375, lon=9.17489, map_source="thunderforest-landscape")
     mml = MarkerMapLayer()
     self.mapview.add_layer(mml)
     self.player_marker = MapMarker(lat=45.47375, lon=9.17489)
     self.mapview.add_marker(self.player_marker)
     
     self.update_map()
     Clock.schedule_once(lambda *args: self.mapview.center_on(*self.location))
     Clock.schedule_interval(self.update_map, 1)
     Clock.schedule_interval(lambda *a: mml.reposition(), 0.1)
     root = FloatLayout()
     root.add_widget(self.mapview)
     return root
Beispiel #2
0
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()
Beispiel #3
0
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
Beispiel #5
0
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
Beispiel #6
0
class RadarMapView(MapView):

    def __init__(self, lat=51.5, lon=0, zoom=8, url="http://localhost/dump1090/data/aircraft.json", interval=1):
        self.aircraft_layer = MarkerMapLayer()
        self.update_in_progress = False
        self.list_of_tracked_aircraft = []
        self.aircraft_markers = []
        self.url = url
        super().__init__(lat=lat, lon=lon, zoom=zoom)
        self.add_layer(self.aircraft_layer)
        Clock.schedule_interval(self.request_update, interval)

    def request_update(self, time):
        UrlRequest(self.url, on_success=self.update_aircraft)

    def update_aircraft(self, request, result):
        if self.update_in_progress:
            return
        self.update_in_progress = True
        list_of_aircraft = result["aircraft"]

        # Update tracked aircraft and mark stale ones as inactive
        for a_tracked_aircraft in self.list_of_tracked_aircraft:
            aircraft_active = False
            for an_aircraft in list_of_aircraft:
                if an_aircraft["hex"] == a_tracked_aircraft.hex:
                    a_tracked_aircraft.data = an_aircraft
                    aircraft_active = True
                    break
            a_tracked_aircraft.active = aircraft_active

        # Add newly detected aircraft
        for an_aircraft in list_of_aircraft:
            aircraft_tracked = False
            for a_tracked_aircraft in self.list_of_tracked_aircraft:
                if a_tracked_aircraft.hex == an_aircraft["hex"]:
                    aircraft_tracked = True
                    break
            if not aircraft_tracked:
                a_tracked_aircraft = Aircraft()
                a_tracked_aircraft.hex = an_aircraft["hex"]
                a_tracked_aircraft.active = True
                a_tracked_aircraft.data = an_aircraft
                self.list_of_tracked_aircraft.append(a_tracked_aircraft)

        # Set up markers
        for a_tracked_aircraft in self.list_of_tracked_aircraft:
            active = a_tracked_aircraft.active
            data = a_tracked_aircraft.data
            position_known = "lat" in data and "lon" in data and "track" in data
            has_marker = hasattr(a_tracked_aircraft, "marker")

            if active and position_known:
                if has_marker:
                    marker = a_tracked_aircraft.marker
                else:
                    marker = AircraftMarker()
                    marker.anchor_x = 0.5
                    marker.anchor_y = 0.5
                    a_tracked_aircraft.marker = marker
                    marker.popup_size = (80, 25)
                    a_tracked_aircraft.labelmode = 1
                    label = Label()
                    bubble = Bubble()
                    label.color = (1, 1, 0, 1)
                    label.outline_color = (0, 0, 0, 1)
                    bubble.add_widget(label)
                    marker.add_widget(bubble)
                    a_tracked_aircraft.label = label
                if hasattr(a_tracked_aircraft, "label"):
                    label = a_tracked_aircraft.label
                    if "flight" in data:
                        label.text = data["flight"]
                    else:
                        label.text = "unknown"
                    if marker.full_label:
                        marker.popup_size = (80, 100)
                        if "altitude" in data:
                            label.text = label.text + \
                                "\nALT: {}".format(data["altitude"])
                        if "vert_rate" in data:
                            label.text = label.text + \
                                "\nRoC: {}".format(data["vert_rate"])
                        if "track" in data:
                            label.text = label.text + \
                                "\nTRK: {}".format(data["track"])
                        if "speed" in data:
                            label.text = label.text + \
                                "\nSPD: {}".format(data["speed"])
                    else:
                        marker.popup_size = (80, 25)

                marker.source = "icons/plane{}.png".format(
                    round(a_tracked_aircraft.data["track"]/10)*10)
                marker.lat = a_tracked_aircraft.data["lat"]
                marker.lon = a_tracked_aircraft.data["lon"]
                if not has_marker:
                    self.add_marker(
                        a_tracked_aircraft.marker, layer=self.aircraft_layer)
            else:
                if has_marker:
                    self.remove_marker(a_tracked_aircraft.marker)
                    delattr(a_tracked_aircraft, "marker")
                if not active:
                    self.list_of_tracked_aircraft.remove(a_tracked_aircraft)
        self.aircraft_layer.reposition()
        self.update_in_progress = False