示例#1
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()
示例#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()