class WayIndex: def __init__(self, ways): self.max_way_id = 0 self.way_idx_mapping = {} self.idx = Rtree() for way in ways: self.add(way) def add(self, way): self.idx.add(id=self.max_way_id, coordinates=way.get_bbox(), obj=way.id) self.way_idx_mapping[way.id] = self.max_way_id self.max_way_id += 1 def delete(self, way): self.idx.delete(id=self.way_idx_mapping[way.id], coordinates=way.get_bbox()) way_ids = map(lambda way: way.object, wayidx.idx.intersection(way.get_bbox(), objects=True))
class SpatialIndex(Persistent): def __init__(self, *args): self.rtree_args = args self.rtree = Rtree(*args) self.backward = IOBTree() def index_doc(self, docid, value): if docid in self.backward: self.unindex_doc(docid) self.backward[docid] = value self.rtree.add(docid, value, obj=docid) def unindex_doc(self, docid): value = self.backward.get(docid) if value is None: return self.rtree.delete(docid, value) del self.backward[docid] def apply(self, value): return [x.object for x in self.rtree.intersection(value, objects=True)] def clear(self): self.backward.clear() props = self.rtree.properties if props.storage == RT_Disk: self.rtree.close() fname = props.filename try: os.unlink('%s.%s' % (fname, props.dat_extension)) except OSError: pass try: os.unlink('%s.%s' % (fname, props.idx_extension)) except OSError: pass self.rtree = Rtree(*self.rtree_args) def count(self, value): return self.rtree.count(value)
class GraphicScene(object): ITEM_SELECTION_RADIUS = 2 # px _logger = _module_logger.getChild('GraphicScene') ############################################## def __init__(self): self._items = OrderedDict() self._rtree = Rtree() self._selected_items = OrderedDict() ############################################## def add_item(self, item): item_id = item._id self._items[item_id] = item self._rtree.add(item_id, item.bounding_box) item._scene = self ############################################## def remove_item(self, item): if item in self._selected_items: item.deselect() item_id = item._id del self._items[item_id] self._rtree.delete(item_id, item.bounding_box) item._scene = None ############################################## def add_items(self, items): for item in items: self.add_item(item) ############################################## def remove_items(self, items): for item in items: self.remove_item(item) ############################################## def items_in(self, interval): items = [self._items[x] for x in self._rtree.intersection(interval.bounding_box())] items.sort() # accordind to z value return items ############################################## def items_at(self, x, y): # Fixme: vector or x, y return self.items_in(point_interval(x, y)) ############################################## def items_around(self, x, y, radius): return self.items_in(centred_interval(x, y, radius)) ############################################## def __iter__(self): return iter(self._items.values()) ############################################## def _select_item(self, item): self._selected_items[item._id] = self ############################################## def _deselect_item(self, item): del self._selected_items[item._id] ############################################## def iter_on_selected_items(self): return self._selected_items.values() ############################################## def erase(self, position, radius): removed_items = [] new_items = [] for item in self: # for path in self.items_around(position.x, position.y, radius): # erase return None, the item or an iterable of new items items = item.erase(position, radius) if items is not item: self._logger.info("Erase item {}".format(item.id)) removed_items.append(item) if items is not None: new_items.extend(items) self.remove_items(removed_items) self.add_items(new_items) return removed_items, new_items
class IntRtreeIndex(BaseIndex): """Avoids the slower Rtree query object=True interface """ _v_nextuid = None family = BTrees.family32 def clear(self): self.fwd = Rtree() self.bwd = self.family.OO.BTree() self.keys = self.family.IO.BTree() self.intids = self.family.OI.BTree() self.ids = self.family.OO.BTree() def __init__(self): self.clear() def key(self, item): try: return item['id'], tuple(self.bbox(item)) except: return tuple(item.items()) def fid(self, item): return item['id'] def intid(self, item): # Get and track next available key using zope.intid algorithm # Item might be already registered uid = self.intids.get(self.key(item)) if uid is not None: return uid # But if not registered nextuid = getattr(self, '_v_nextuid', None) while True: if nextuid is None: nextuid = random.randrange(0, self.family.maxint) uid = nextuid if uid not in self.keys: nextuid += 1 if nextuid > self.family.maxint: nextuid = None self._v_nextuid = nextuid return uid nextuid = None def intersection(self, bbox): """Return an iterator over Items that intersect with the bbox""" for hit in self.fwd.intersection(bbox, objects=False): yield self.bwd[int(hit)] def nearest(self, bbox, limit=1): """Return an iterator over the nearest N=limit Items to the bbox""" for hit in self.fwd.nearest(bbox, num_results=limit, objects=False): yield self.bwd[int(hit)] def item(self, fid, bbox): return self.bwd[self.intids[(fid, bbox)]] def items(self, fid): return [self.bwd[intid] for intid in self.ids[fid]] def index_item(self, itemid, bbox, item): """Add an Item to the index""" if itemid in self.bwd: self.unindex_item(itemid, bbox) # Store an id for the item if it has None try: item.update(id=item.get('id') or str(uuid.uuid4())) key = self.key(item) sid = self.fid(item) # Map keys <-> intids intid = self.intid(item) self.keys[intid] = key self.intids[key] = intid if sid not in self.ids: self.ids[sid] = IISet([]) self.ids[sid].add(intid) self.bwd[intid] = item self.fwd.add(intid, bbox) except: import pdb; pdb.set_trace() raise def unindex_item(self, itemid, bbox): """Remove an Item from the index""" intid = int(itemid) key = self.keys.get(intid) if key is None: return self.ids[key[0]].remove(intid) del self.keys[intid] del self.intids[key] del self.bwd[intid] self.fwd.delete(intid, bbox) def batch(self, changeset): BaseIndex.batch(self, changeset) def commit(self): transaction.commit() rtree_storage = self.fwd.properties.filename self.fwd.close() self.fwd = Rtree(rtree_storage) def close(self): self.fwd.close()
class GraphicScene(object): ITEM_SELECTION_RADIUS = 2 # px _logger = logging.getLogger(__name__) ############################################## def __init__(self, glortho2d): self._glortho2d = glortho2d # Fixme: used for window_to_scene_coordinate window_to_scene_distance self._items = {} self._rtree = Rtree() self._selected_item = None ############################################## def _window_to_scene_coordinate(self, event): location = np.array((event.x(), event.y()), dtype=np.float) return list(self._glortho2d.window_to_gl_coordinate(location)) ############################################## def _window_to_scene_distance(self, x): return self._glortho2d.window_to_gl_distance(x) ############################################## def add_item(self, item): item_id = hash(item) self._items[item_id] = item self._rtree.add(item_id, item.bounding_box) ############################################## def remove_item(self, item): if self._selected_item is item: self.deselect_item(item) item_id = hash(item) del self._items[item_id] self._rtree.delete(item_id, item.bounding_box) ############################################## def select_item(self, item): item.is_selected = True self._selected_item = item ############################################## def deselect_item(self, item): item.is_selected = False self._selected_item = None ############################################## def items_in(self, interval): self._logger.debug(str( interval)) items = [self._items[x] for x in self._rtree.intersection(interval.bounding_box())] items.sort() # accordind to z value return items ############################################## def items_at(self, x, y): return self.items_in(point_interval(x, y)) ############################################## def items_around(self, x, y, radius): return self.items_in(centred_interval(x, y, radius)) ############################################## def item_under_mouse(self, event): x, y = self._window_to_scene_coordinate(event) radius = self._window_to_scene_distance(GraphicScene.ITEM_SELECTION_RADIUS) items = self.items_around(x, y, radius) if items: # return the highest z value # Fixme: ok ? return items[-1] else: return None ############################################## def keyPressEvent(self, event): return False ############################################## def keyReleaseEvent(self, event): return False ############################################## def mousePressEvent(self, event): item = self.item_under_mouse(event) if item is not None: self.select_item(item) item.mousePressEvent(event) return True else: return False ############################################## def mouseReleaseEvent(self, event): if self._selected_item is not None: self._selected_item.mouseReleaseEvent(event) self.deselect_item(self._selected_item) return True else: return False ############################################## def mouseDoubleClickEvent(self, event): return False ############################################## def wheelEvent(self, event): return False ############################################## def mouseMoveEvent(self, event): if self._selected_item is not None: self._selected_item.mouseMoveEvent(event) return True else: return False
def delete(self, id_, coords): with threading.RLock(): Rtree.delete(self, id_, coords)
class GisCanvas: """ Top-level drawing class that contains a list of all the objects to draw. """ def __init__(self): """Initialise the class by creating an instance of each object.""" self._ratio_index = (0.05, 0.1, 0.2, 0.5, 1, 2, 4, 8, 16, 32, 64) # pagal sita dydi turi kisti tasko atstumas nuo zoominimo centro self._zoom_level = self._ratio_index.index(4) # 0-dirbame su realiomis koordinatemis - kitur koordinates yra gaunamos atliekant dalyba... todel nera labai tikslios self._device_area = None # gtk.gdk.Rectangle() self._shapes = [] # shapes data self._index_rtree = Rtree() # shapes indexes in self._shapes self._offset = Point(0, 0) #- naudojamas ctx.translate() #device koordinates self._prj = GisProjection() self._styler = Styler(self) self._painter = Painter(self._styler) self._drag_to_center = True # jeigu norime kad resize metu (bus iskviestas set_device_area) butu iskvietsas center() def load(self, name): self._styler.load_css_file("%s.css" % name) self.load_fcad_file("%s.fcad" % name) def save(self, file_name): #self.create_fcad_file("%s.fcad" % file_name) ShapeFile.create_fcad_file("%s.fcad" % file_name, self._shapes) StyleFile.create_css_file("%s.css" % file_name, self._styler.get_shapes_style(), self._styler.get_symbols_style(), prettyprint=True) #self._styler.create_css_file("%s.css" % file_name, prettyprint=True) def clear(self): print "clear canvas!" self._zoom_level = self._ratio_index.index(1) self._offset = Point(0, 0) self._shapes = [] #self._cairo_paths = {} self._index_rtree = Rtree() self._styler.load_default_style() def load_pickle(self, file_path): timer.start("loading shapes.p") if os.path.exists(file_path): self._shapes = pickle.load(open(file_path, "rb")) if len(self._shapes): def generator_function(points): for i, obj in enumerate(points): if obj == None: continue yield (i, self._styler.get_bbox(fshape.Shape.decompress(obj)), obj) self._index_rtree = Rtree(generator_function(self._shapes)) timer.end("loading shapes.p") def save_pickle(self, file_path): pickle.dump(self._shapes, open(file_path, "wb")) def load_fcad_file(self, file_path): timer.start("loading shapes.fcad") self._shapes = ShapeFile.read_fcad_file(file_path) if len(self._shapes): def generator_function(points): for i, obj in enumerate(points): if obj == None: continue yield (i, self._styler.get_bbox(fshape.Shape.decompress(obj)), obj) self._index_rtree = Rtree(generator_function(self._shapes)) timer.end("loading shapes.fcad") def set_device_area(self, area): # atnaujina screen.resize() self._device_area = area if self._drag_to_center: self.center() self._drag_to_center = False def get_shape_by_id(self, id): compressed_shape = self._shapes[id] if compressed_shape == None: return None return fshape.Shape.decompress(compressed_shape) def get_object_by_id(self, id): return self._shapes[id] def draw_100(self, ctx, area): """100% draw for printing, no scale, area in user coordinates""" page_area = Area(0, 0, area.width, area.height) #ctx.rectangle(0, 0, area.width, area.height) #ctx.clip() # tam kad nepaisytu uzh sito staciakampio ribu (tada gaunasi dubliuotos linijos) #self.background(ctx, page_area) # paint white background self._painter.background(ctx, page_area, color=(1,1,1), clip=True) # paint white background self._painter.setup(ctx, transform={"translate":(-area.x, -area.y)}) radius = 0 #self.pixel_radius + self.line_width # ne cia reikia prideti radiusa, o ten kur dedame i cavas'a shape'us elements = self._index_rtree.intersection((area.x-radius, area.y-radius, area.x+area.width+radius, area.y+area.height+radius)) elements_zindex = self._styler.create_zindex(elements) # jis yra pilnas visu galimu zindex'u sarasas - pradzioje dalis ju gali buti ir tusti [] timer.start("draw100") for zindex in sorted(elements_zindex.keys()): for element in elements_zindex[zindex]: self._painter.draw(element, update=elements_zindex) # kazka visada nupaiso - bet dar papildomai gali iterpti elementu su didesniu zindex'u # tie elementai bus nupaisomi veliau. timer.end("draw100") def draw_object(self, ctx, id, fill_or_stroke=True): #self.apply_transforms(ctx) # drag and scale self._painter.setup(ctx, transform={"translate":self.get_offset(), "scale": self.get_ratio()}) elements_zindex = self._styler.create_zindex([id]) timer.start("draw single object") for zindex in sorted(elements_zindex.keys()): for element in elements_zindex[zindex]: self._painter.draw(element, update=elements_zindex, fill_or_stroke=fill_or_stroke) # kazka visada nupaiso - bet dar papildomai gali iterpti elementu su didesniu zindex'u timer.end("draw single object") def draw(self, ctx, area, fill_or_stroke=True): """Draw the complete drawing by drawing each object in turn.""" self._painter.background(ctx, area, color=(1,1,1), clip=True) # paint white background self._painter.setup(ctx, transform={"translate":self.get_offset(), "scale": self.get_ratio()}) # paishysime tik tuos tashkus kurie pakliuna i vartotojo langa # reikia device_area konvertuoti i user koordinates x, y = self.device_to_user(Point(area.x, area.y)) x2, y2 = self.device_to_user(Point(area.x + area.width, area.y + area.height)) radius = 0 #self.pixel_radius + self.line_width # ne cia reikia prideti radiusa, o ten kur dedame i cavas'a shape'us # geriau cia, nes paprasciau yra iskviesti nupaisyti didesni gabala nei paskaiciuoti tikslu pvz linijo simboliu dydi... elements = self._index_rtree.intersection((x-radius, y-radius, x2+radius, y2+radius)) elements_zindex = self._styler.create_zindex(elements) # jis yra pilnas visu galimu zindex'u sarasas - pradzioje dalis ju gali buti ir tusti [] timer.start("draw") for zindex in sorted(elements_zindex.keys()): for element in elements_zindex[zindex]: #print "element: ", element[0][0] self._painter.draw(element, update=elements_zindex, fill_or_stroke=fill_or_stroke) # kazka visada nupaiso - bet dar papildomai gali iterpti elementu su didesniu zindex'u # tie elementai bus nupaisomi veliau. timer.end("draw") def get_ratio(self, level=None): if level == None: level = self._zoom_level return self._ratio_index[level] def drag2(self, drag_offset): self._offset = Point(self._offset.x + drag_offset.x, self._offset.y + drag_offset.y) def get_offset(self): return self._offset def find_objects_at_position(self, point): #ctx = self.get_context(transform=False) # naujas kontekstas kur paisysime, transformuoti nereikia - nes tai padarys .draw() ctx = cairo.Context(cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)) # naujas kontekstas kur paisysime, transformuoti nereikia - nes tai padarys .draw() area = Area(point.x, point.y, 1, 1) listener = ContextObjectsListener(point) listener_id = self._painter.addContextListener(listener) try: self.draw(ctx, area, fill_or_stroke=False) # nepaisysime tik sukursime path'us context'e ir su jais kazka atliks ContextListeneris finally: self._painter.removeContextListener(listener_id) return listener.get_objects() # rastu elementu indeksai def get_shape_redraw_area(self, id): #ctx = self.get_context(transform=False) ctx = cairo.Context(cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)) shape = self.get_object_by_id(id) if shape == None: return None listener = ContextBoundsListener() listener_id = self._painter.addContextListener(listener) try: self.draw_object(ctx, id, fill_or_stroke=False) finally: self._painter.removeContextListener(listener_id) area = listener.get_area() #print "Area: ", area return area def device_to_user(self, point): #x, y = self.ctx.device_to_user(point.x, point.y) #x, y = self.get_context().device_to_user(point.x, point.y) ratio = self.get_ratio() offset = self.get_offset() x, y = (point.x - offset.x)/ratio, (point.y - offset.y)/ratio return Point(x, y) def user_to_device(self, point, offset=(0, 0)): #x, y = self.ctx.user_to_device(point.x, point.y) #x, y = self.get_context().user_to_device(point.x, point.y) ratio = self.get_ratio() drag_offset = self.get_offset() x, y = (point.x * ratio) + drag_offset.x, (point.y * ratio) + drag_offset.y return Point(x + offset[0], y + offset[1]) def add(self, shape): id = len(self._shapes) # top element index self._shapes.append(shape.compress()) self._index_rtree.add(id, self._styler.get_bbox(shape)) return id def remove(self, id): shape = self.get_shape_by_id(id) self._index_rtree.delete(id, shape.bbox()) # po sito as jau niekada tokio id negausiu (nes viskas eina per rtree) self._shapes[id] = None def replace(self, id, shape): old = self.get_shape_by_id(id) #print "before :", list(self._index_rtree.intersection(old.bbox())) if old != None: self._index_rtree.delete(id, old.bbox()) #print "after :", list(self._index_rtree.intersection(old.bbox())) self._shapes[id] = shape.compress() self._index_rtree.add(id, self._styler.get_bbox(shape)) def zoom(self, direction, center=None): """center cia yra device koordinatemis - jeigu butu paspausta su zoom irankiu peles pagalba""" new_zoom_level = self._zoom_level+direction if new_zoom_level in range(0, len(self._ratio_index)): #esamas centro taskas atlikus zooma turi islikti toje pacioje vietoje center = center or Point(self._device_area.width/2, self._device_area.height/2) # vartotojo parinktas tashkas arba tiesiog centras center_user = self.device_to_user(center) # gauname priesh tai buvusio centro koordinates naujame zoom lygyje new_ratio = self.get_ratio(new_zoom_level) new_center = Point(center_user.x * new_ratio, center_user.y * new_ratio) # gauname centro poslinki (per tiek reikia perstumti visa vaizdeli) self._offset = Point(center.x - new_center.x, center.y - new_center.y) # naujas poslinkis #print "zoom: ", self._offset self._zoom_level = new_zoom_level return True return False def center(self, point=None): """center to user point""" if not point: bounds = self._index_rtree.bounds point = Point((bounds[0]+bounds[2])/2.0, (bounds[1]+bounds[3])/2.0) hand = self.user_to_device(point) to = Point(self._device_area.width/2, self._device_area.height/2) self.drag2(Point(to.x-hand.x, to.y-hand.y)) def get_projection(self): return self._prj def get_styler(self): return self._styler def load_ocad_file(self, file_path, generator=True): #import ocadfile #self.add(Shape(1, Point(0, 0), symbol="graphics")) # koordinaciu centras self._prj = GisProjection() self._zoom_level = self._ratio_index.index(4) of = OcadFile(file_path, self._prj) #self._styler.load_ocad_symbols(of, prj) timer.start("Adding ocad symbols") self._styler.set_symbols_style(of.get_symbols_style()) timer.end("Adding ocad symbols") timer.start("Adding ocad elements") shapes = of.get_shapes() print "Shapes: ", len(shapes) for shape in shapes: self.add(shape) timer.end("Adding ocad elements") self.center() def load_shape_file(self, file_path, generator=True): import shapefile from random import randint sf = shapefile.Reader(file_path) print "Number of shapes: ", sf.numRecords self.add(fshape.Shape(1, Point(0, 0), style={"z-index":99})) # koordinaciu centras if self.prj.scale == 1: # pirmas kartas prj = GisProjection(self, sf.bbox) #po sito ciklo jau turesime zemelapio ribas center = prj.map_to_user(prj.get_center()) self.center(center) self.prj = prj timer.start("Adding shapes") symbol = sf.shapeName.split("/")[-1] self._styler.set_symbol_style(symbol, {"color": (randint(0,255),randint(0,255),randint(0,255))}) for shape in sf.ogis_shapes(self.prj): self.add(fshape.Shape(shape.shapeType, shape.points, symbol=symbol)) timer.end("Adding shapes") def add_random_points(self, number, area, generator=True): """Kai generator=False - sunaudoja maziau atminties ikrovimo metu, bet trunka gerokai leciau """ self._shapes = [] timer.start("Adding random data") from random import randint for x in range(0, number): color = 65536 * randint(0,255) + 256 * randint(0,255) + randint(0,255) # RGBint x, y = randint(2, area.width), randint(2, area.height) if not generator: # darysime rtree.add kiekvienam taskui atskirai self.add(fshape.Shape(1, Point(x, y), color=color)) else: self._shapes.append((1, color, x, y)) if generator: def generator_function(points): for i, obj in enumerate(points): yield (i, (obj[2], obj[3], obj[2], obj[3]), obj) self._index_rtree = Rtree(generator_function(self._shapes)) timer.end("Adding random data")