def set_zoom_at(self, zoom, x, y, scale=None): """Sets the zoom level, leaving the (x, y) at the exact same point in the view. """ zoom = clamp(zoom, self.map_source.get_min_zoom(), self.map_source.get_max_zoom()) if int(zoom) == int(self._zoom): return scale = scale or 1. # first, rescale the scatter scatter = self._scatter scale = clamp(scale, scatter.scale_min, scatter.scale_max) rescale = scale * 1.0 / scatter.scale scatter.apply_transform(Matrix().scale(rescale, rescale, rescale), post_multiply=True, anchor=scatter.to_local(x, y)) # adjust position if the zoom changed c1 = self.map_source.get_col_count(self._zoom) c2 = self.map_source.get_col_count(zoom) if c1 != c2: f = float(c2) / float(c1) self.delta_x = scatter.x + self.delta_x * f self.delta_y = scatter.y + self.delta_y * f # back to 0 every time scatter.apply_transform(Matrix().translate( -scatter.x, -scatter.y, 0 ), post_multiply=True) # avoid triggering zoom changes. self._zoom = zoom self.zoom = self._zoom
def center_on(self, *args): """Center the map on the coordinate :class:`Coordinate`, or a (lat, lon) """ map_source = self.map_source zoom = self._zoom if len(args) == 1 and isinstance(args[0], Coordinate): coord = args[0] lat = coord.lat lon = coord.lon elif len(args) == 2: lat, lon = args else: raise Exception("Invalid argument for center_on") lon = clamp(lon, MIN_LONGITUDE, MAX_LONGITUDE) lat = clamp(lat, MIN_LATITUDE, MAX_LATITUDE) scale = self._scatter.scale x = map_source.get_x(zoom, lon) - self.center_x / scale y = map_source.get_y(zoom, lat) - self.center_y / scale self.delta_x = -x self.delta_y = -y self.lon = lon self.lat = lat self._scatter.pos = 0, 0 self.trigger_update(True)
def set_zoom_at(self, zoom, x, y, scale=None): """Sets the zoom level, leaving the (x, y) at the exact same point in the view. """ zoom = clamp(zoom, self.map_source.get_min_zoom(), self.map_source.get_max_zoom()) if int(zoom) == int(self._zoom): return scale = scale or 1. # first, rescale the scatter scatter = self._scatter scale = clamp(scale, scatter.scale_min, scatter.scale_max) rescale = scale * 1.0 / scatter.scale scatter.apply_transform(Matrix().scale(rescale, rescale, rescale), post_multiply=True, anchor=scatter.to_local(x, y)) # adjust position if the zoom changed c1 = self.map_source.get_col_count(self._zoom) c2 = self.map_source.get_col_count(zoom) if c1 != c2: f = float(c2) / float(c1) self.delta_x = scatter.x + self.delta_x * f self.delta_y = scatter.y + self.delta_y * f # back to 0 every time scatter.apply_transform(Matrix().translate(-scatter.x, -scatter.y, 0), post_multiply=True) # avoid triggering zoom changes. self._zoom = zoom self.zoom = self._zoom
def scale_at(self, scale, x, y): scatter = self._scatter scale = clamp(scale, scatter.scale_min, scatter.scale_max) rescale = scale * 1.0 / scatter.scale scatter.apply_transform(Matrix().scale(rescale, rescale, rescale), post_multiply=True, anchor=scatter.to_local(x, y))
def get_lat(self, zoom, y): """Get the latitude to the y position in the map source's projection """ dy = y / float(self.dp_tile_size) n = pi - 2 * pi * dy / pow(2., zoom) lat = -180. / pi * atan(.5 * (exp(n) - exp(-n))) return clamp(lat, MIN_LATITUDE, MAX_LATITUDE)
def on_transform(self, *args): self._invalid_scale = True if self._transform_lock: return self._transform_lock = True # recalculate viewport map_source = self.map_source zoom = self._zoom scatter = self._scatter scale = scatter.scale if scale >= 2.: zoom += 1 scale /= 2. elif scale < 1: zoom -= 1 scale *= 2. zoom = clamp(zoom, map_source.min_zoom, map_source.max_zoom) if zoom != self._zoom: self.set_zoom_at(zoom, scatter.x, scatter.y, scale=scale) self.trigger_update(True) else: if zoom == map_source.min_zoom and scatter.scale < 1.: scatter.scale = 1. self.trigger_update(True) else: self.trigger_update(False) if map_source.bounds: self._apply_bounds() self._transform_lock = False self._scale = self._scatter.scale
def on_transform(self, *args): self._invalid_scale = True if self._transform_lock: return self._transform_lock = True # recalculate viewport map_source = self.map_source zoom = self._zoom scatter = self._scatter scale = scatter.scale if scale >= 2.01: zoom += 1 scale /= 2. elif scale < 0.99: zoom -= 1 scale *= 2. zoom = clamp(zoom, map_source.min_zoom, map_source.max_zoom) if zoom != self._zoom: self.set_zoom_at(zoom, scatter.x, scatter.y, scale=scale) self.trigger_update(True) else: if zoom == map_source.min_zoom and scatter.scale < 1.: scatter.scale = 1. self.trigger_update(True) else: self.trigger_update(False) if map_source.bounds: self._apply_bounds() self._transform_lock = False self._scale = self._scatter.scale
def get_y(self, zoom, lat): """Get the y position on the map using this map source's projection (0, 0) is located at the top left. """ lat = clamp(-lat, MIN_LATITUDE, MAX_LATITUDE) lat = lat * pi / 180. return ((1.0 - log(tan(lat) + 1.0 / cos(lat)) / pi) / \ 2. * pow(2., zoom)) * self.dp_tile_size
def bbox_for_zoom(self, vx, vy, w, h, zoom): # return a tile-bbox for the zoom map_source = self.map_source size = map_source.dp_tile_size scale = self._scale max_x_end = map_source.get_col_count(zoom) max_y_end = map_source.get_row_count(zoom) x_count = int(ceil(w / scale / float(size))) + 1 y_count = int(ceil(h / scale / float(size))) + 1 tile_x_first = int(clamp(vx / float(size), 0, max_x_end)) tile_y_first = int(clamp(vy / float(size), 0, max_y_end)) tile_x_last = tile_x_first + x_count tile_y_last = tile_y_first + y_count tile_x_last = int(clamp(tile_x_last, tile_x_first, max_x_end)) tile_y_last = int(clamp(tile_y_last, tile_y_first, max_y_end)) x_count = tile_x_last - tile_x_first y_count = tile_y_last - tile_y_first return (tile_x_first, tile_y_first, tile_x_last, tile_y_last, x_count, y_count)
def on_transform(self, *args): if self._transform_lock: return self._transform_lock = True # recalculate viewport map_source = self.map_source zoom = self._zoom scatter = self._scatter scale = scatter.scale if scale >= 2.: zoom += 1 scale /= 2. elif scale < 1: zoom -= 1 scale *= 2. zoom = clamp(zoom, self.map_source.min_zoom, self.map_source.max_zoom) if zoom != self._zoom: self.set_zoom_at(zoom, scatter.x, scatter.y, scale=scale) self.trigger_update(True) else: self.trigger_update(False) if map_source.bounds: # if the map_source have any constraints, apply them here. min_lon, min_lat, max_lon, max_lat = map_source.bounds xmin = map_source.get_x(zoom, min_lon) xmax = map_source.get_x(zoom, max_lon) ymin = map_source.get_y(zoom, min_lat) ymax = map_source.get_y(zoom, max_lat) mx, my = self._scatter.to_local(*self.center) mx -= self.delta_x my -= self.delta_y if mx < xmin: x_diff = xmin - mx self._scatter.x -= x_diff elif mx > xmax: x_diff = xmax - mx self._scatter.x -= x_diff if my < ymin: y_diff = ymin - my self._scatter.y -= y_diff elif my > ymax: y_diff = ymax - my self._scatter.y -= y_diff self._transform_lock = False self._scale = self._scatter.scale
def on_map_source(self, instance, source): if isinstance(source, string_types): self.map_source = MapSource.from_provider(source) elif isinstance(source, (tuple, list)): cache_key, min_zoom, max_zoom, url, attribution, options = source self.map_source = MapSource(url=url, cache_key=cache_key, min_zoom=min_zoom, max_zoom=max_zoom, attribution=attribution, **options) elif isinstance(source, MapSource): self.map_source = source else: raise Exception("Invalid map source provider") self.zoom = clamp(self.zoom, self.map_source.min_zoom, self.map_source.max_zoom) self.remove_all_tiles() self.trigger_update(True)
def on_transform(self, *args): if self._transform_lock: return self._transform_lock = True # recalculate viewport zoom = self._zoom scatter = self._scatter scale = scatter.scale if scale >= 2.: zoom += 1 scale /= 2. elif scale < 1: zoom -= 1 scale *= 2. zoom = clamp(zoom, self.map_source.min_zoom, self.map_source.max_zoom) if zoom != self._zoom: self.set_zoom_at(zoom, scatter.x, scatter.y, scale=scale) self.trigger_update(True) else: self.trigger_update(False) self._transform_lock = False
def get_lon(self, zoom, x): """Get the longitude to the x position in the map source's projection """ dx = x / float(self.dp_tile_size) lon = dx / pow(2., zoom) * 360. - 180. return clamp(lon, MIN_LONGITUDE, MAX_LONGITUDE)
def get_x(self, zoom, lon): """Get the x position on the map using this map source's projection (0, 0) is located at the top left. """ lon = clamp(lon, MIN_LONGITUDE, MAX_LONGITUDE) return ((lon + 180.) / 360. * pow(2., zoom)) * self.dp_tile_size
def get_x(self, lon): '''Get the x position on the map using this map source's projection (0, 0) is located at the top left. ''' return clamp(lon, MIN_LONGITUDE, MAX_LONGITUDE) * self.ms / 360.0
def get_y(self, lat): '''Get the y position on the map using this map source's projection (0, 0) is located at the top left. ''' lat = radians(clamp(-lat, MIN_LATITUDE, MAX_LATITUDE)) return ((1.0 - log(tan(lat) + 1.0 / cos(lat)) / pi)) * self.ms / 2.0