def test_get_bbox_test_cases(tile_generator: TileGenerator, tile_test_cases: Dict[str, dict]): """ test get_bbox for cases and all tiles between zoomlevel 0 to 10 Arguments: tile_generator {TileGenerator} -- default TileGenerator tile_test_cases {Dict[str, dict]} -- tile test cases """ # test special cases for test_case in tile_test_cases: tile_generator.zoom = tile_test_cases[test_case]["zoom"] tile_generator.x_pixel = tile_test_cases[test_case]["x"] tile_generator.y_pixel = tile_test_cases[test_case]["y"] box2d: Box2d = tile_generator.get_bbox() if not isinstance(box2d, Box2d): raise AssertionError if "{}".format(box2d.maxx) != tile_test_cases[test_case]["maxx"]: raise AssertionError if "{}".format(box2d.maxy) != tile_test_cases[test_case]["maxy"]: raise AssertionError if "{}".format(box2d.minx) != tile_test_cases[test_case]["minx"]: raise AssertionError if "{}".format(box2d.miny) != tile_test_cases[test_case]["miny"]: raise AssertionError
def test_render_tile_without_data(tile_generator: TileGenerator, tile_test_cases: Dict[str, dict]): """ test render tiles for tile_test_cases without using ohdm test data Arguments: tile_generator {TileGenerator} -- default TileGenerator """ for test_case in tile_test_cases: print("test: {}".format(test_case)) tile_generator.zoom = tile_test_cases[test_case]["zoom"] tile_generator.x_pixel = tile_test_cases[test_case]["x"] tile_generator.y_pixel = tile_test_cases[test_case]["y"] # generate new tile into a tmp file new_tile: SpooledTemporaryFile = SpooledTemporaryFile() new_tile.write(tile_generator.render_tile()) new_tile.seek(0) # open new tile as image new_tile_image: Image = Image.open(new_tile) # check if the tile is a PNG file assert new_tile_image.format == "PNG" # monochrome & resize images to better compare them reference_tile = Image.open( "/app/compose/local/django/test_tile/{}".format( tile_test_cases[test_case]["tile_png"])) assert ImageChops.difference(reference_tile, new_tile_image).getbbox() is None
def test_render_tile_with_data(tile_generator: TileGenerator, tile_test_cases: Dict[str, dict]): """ test render tiles for tile_test_cases with using ohdm test data Arguments: tile_generator {TileGenerator} -- default TileGenerator """ # cleanup data clear_mapnik_tables() # fill database run_import( file_path="/map.osm", db_cache_size=10000, cache2file=False, ) tile_generator.request_date = timezone.now() for test_case in tile_test_cases: # contine if test case has not test data if not tile_test_cases[test_case]["has_date_data"]: continue print("test: {}".format(test_case)) tile_generator.zoom = tile_test_cases[test_case]["zoom"] tile_generator.x_pixel = tile_test_cases[test_case]["x"] tile_generator.y_pixel = tile_test_cases[test_case]["y"] # generate new tile into a tmp file new_tile: SpooledTemporaryFile = SpooledTemporaryFile() new_tile.write(tile_generator.render_tile()) new_tile.seek(0) # open new tile as image new_tile_image: Image = Image.open(new_tile) new_tile_image.save("/app/bremen.png") # check if the tile is a PNG file assert new_tile_image.format == "PNG" # monochrome & resize images to better compare them reference_tile = Image.open( "/app/compose/local/django/test_tile/{}".format( tile_test_cases[test_case]["tile_png"])) diff: bool = ImageChops.difference(reference_tile, new_tile_image).getbbox() is None assert diff is False # cleanup data clear_mapnik_tables()
def generate_tile_reload_style(request, year: int, month: int, day: int, zoom: int, x_pixel: float, y_pixel: float) -> HttpResponse: """ reload style.xml & than generate a new mapnik tile :param request: django request :param year: request year as INT :param month: request month as INT :param day: request day as INT :param zoom: mapnik zoom level :param x_pixel: mapnik x coordinate :param y_pixel: mapnik y coordinate :return: """ # generate time sensitive tile and reload style.xml tile_gen: TileGenerator = TileGenerator( request_date=date(year=int(year), month=int(month), day=int(day)), style_xml_template=get_style_xml( generate_style_xml=False, carto_sytle_path=env("CARTO_STYLE_PATH")), zoom=int(zoom), x_pixel=float(x_pixel), y_pixel=float(y_pixel), osm_cato_path=env("CARTO_STYLE_PATH"), ) return HttpResponse(tile_gen.render_tile(), content_type="image/jpeg")
def test_get_bbox_random_zoom_10_20(tile_generator: TileGenerator): """ test get_bbox for random 1000 tiles each zoom level Arguments: tile_generator {TileGenerator} -- default TileGenerator """ for zoom in range(10, 20): tile_generator.zoom = zoom coordinate_limit: int = int(pow(2, zoom)) for _ in range(100): x: int = randrange(coordinate_limit) for _ in range(100): y: int = randrange(coordinate_limit) tile_generator.x_pixel = x tile_generator.y_pixel = y assert isinstance(tile_generator.get_bbox(), Box2d)
def test_without_date(): tile_generator: TileGenerator = TileGenerator(zoom=0, x_pixel=0, y_pixel=0, use_cache=False) with pytest.raises(RenderErrorNoDate): tile_generator.render_tile()
def test_success_empty_cache( self, test_tile: Dict[str, dict], tile_generator: TileGenerator ): # clear cache cache.clear() request: WSGIRequest = RequestFactory().get( self.get_path(kwargs=test_tile["data"]) ) response: HttpResponse = generate_tile( request=request, year=test_tile["data"]["year"], month=test_tile["data"]["month"], day=test_tile["data"]["day"], zoom=test_tile["data"]["zoom"], x_pixel=test_tile["data"]["x_pixel"], y_pixel=test_tile["data"]["y_pixel"], ) tile_cache: Optional[dict] = cache.get(test_tile["cache"]["cache_key"]) if tile_cache is None: raise AssertionError tile: Optional[bytes] = cache.get(tile_cache["tile_hash"]) tile_generator.zoom = test_tile["data"]["zoom"] tile_generator.x_pixel = test_tile["data"]["x_pixel"] tile_generator.y_pixel = test_tile["data"]["y_pixel"] # check if the right tile was rendert if response.content != tile_generator.render_tile(): raise AssertionError # check if the cache was setup right if hashlib.md5(response.content).hexdigest() != tile_cache["tile_hash"]: raise AssertionError if response.content != tile: raise AssertionError if not isinstance(response.content, bytes): raise AssertionError if response.status_code != 200: raise AssertionError if response["content-type"] != "image/jpeg": raise AssertionError
def async_generate_tile( year: int, month: int, day: int, style_xml_template: str, zoom: int, x_pixel: float, y_pixel: float, osm_cato_path: str, cache_key: str, ) -> str: """ run celery background task to generate a mapnik tile & cache the tile :param year: request year as INT :param month: request month as INT :param day: request day as INT :param style_xml_template: path to style.xml :param zoom: mapnik zoom level :param x_pixel: mapnik x coordinate :param y_pixel: mapnik y coordinate :param osm_cato_path: path to osm cato :param cache_key: cache key for mapnik tile :return: """ # render requested tile tile: bytes = TileGenerator( request_date=date(year=int(year), month=int(month), day=int(day)), style_xml_template=style_xml_template, zoom=int(zoom), x_pixel=float(x_pixel), y_pixel=float(y_pixel), osm_cato_path=osm_cato_path, use_cache=True, ).render_tile() # create a md5 hash of the tile tile_hash: str = hashlib.md5(tile).hexdigest() # set url-tile cache content tile_cache: dict = {"process_id": None, "tile_hash": tile_hash} # update tile cache & url-tile cache content if zoom <= env.int("ZOOM_LEVEL", 13): # cache for ever cache.set(tile_hash, tile, None) cache.set(cache_key, tile_cache, None) else: # cache for time in TILE_CACHE_TIME cache.set(tile_hash, tile, env.int("TILE_CACHE_TIME", 2592000) * 10) cache.set(cache_key, tile_cache, env.int("TILE_CACHE_TIME", 2592000)) return tile_hash
def set_valid_date(self): tile_generator: TileGenerator = TileGenerator(zoom=self.zoom, x_pixel=self.x_pixel, y_pixel=self.y_pixel) geom = Polygon.from_bbox(tile_generator.get_bbox()) valid_since: Optional[date] = self.valid_since valid_until: Optional[date] = self.valid_until self.valid_since = None self.valid_until = None planet_osm_lines: List[PlanetOsmLine] = [] for planet_osm_line in PlanetOsmLine.objects.filter( way__bbcontains=geom, valid_since__lte=valid_since, valid_until__gte=valid_until, ): planet_osm_lines.append(planet_osm_line) self.set_valid_date_iterate_objects(planet_osm_lines) planet_osm_points: List[PlanetOsmPoint] = [] for planet_osm_point in PlanetOsmPoint.objects.filter( way__bbcontains=geom, valid_since__lte=valid_since, valid_until__gte=valid_until, ): planet_osm_points.append(planet_osm_point) self.set_valid_date_iterate_objects(planet_osm_points) planet_osm_polygons: List[PlanetOsmPolygon] = [] for planet_osm_polygon in PlanetOsmPolygon.objects.filter( way__bbcontains=geom, valid_since__lte=valid_since, valid_until__gte=valid_until, ): planet_osm_polygons.append(planet_osm_polygon) self.set_valid_date_iterate_objects(planet_osm_polygons) planet_osm_roadss: List[PlanetOsmRoads] = [] for planet_osm_roads in PlanetOsmRoads.objects.filter( way__bbcontains=geom, valid_since__lte=valid_since, valid_until__gte=valid_until, ): planet_osm_roadss.append(planet_osm_roads) self.set_valid_date_iterate_objects(planet_osm_roadss) if self.valid_since is None: self.valid_since = valid_since if self.valid_until is None: self.valid_until = valid_until self.save()
def test_from_py_to_lat(tile_test_cases: Dict[str, dict]): """ test if method from_py_to_lat convert px & zoom to lat Arguments: tile_test_cases {Dict[str, dict]} -- tile test cases """ for test_case in tile_test_cases: print("lat test for {}".format(test_case)) lat: float = TileGenerator.from_py_to_lat( py=tile_test_cases[test_case]["y"], zoom=tile_test_cases[test_case]["zoom"], ) assert "{:0.10f}".format(lat) == tile_test_cases[test_case]["lat"] assert isinstance(lat, float)
def test_from_px_to_lon(tile_test_cases: Dict[str, dict]): """ test if method from_px_to_lon convert px & zoom to lon Arguments: tile_test_cases {Dict[str, dict]} -- tile test cases """ for test_case in tile_test_cases: print("lon test for {}".format(test_case)) lon: float = TileGenerator.from_px_to_lon( px=tile_test_cases[test_case]["x"], zoom=tile_test_cases[test_case]["zoom"], ) assert "{:0.10f}".format(lon) == tile_test_cases[test_case]["lon"] assert isinstance(lon, float)
def test_tile_generator_init(): """test TileGenerator constructor""" # test valid zoom level for zoom in range(20): if not isinstance(TileGenerator(zoom=zoom, x_pixel=0, y_pixel=0), TileGenerator): raise AssertionError # test zoom level with invalid zoom level with pytest.raises(ZoomOutOfRange): TileGenerator(zoom=-1, x_pixel=0, y_pixel=0) with pytest.raises(ZoomOutOfRange): TileGenerator(zoom=20, x_pixel=0, y_pixel=0) # test x & y coordinate limit for zoom in range(20): max_coordinate: int = int(pow(2, zoom)) # x to low with pytest.raises(CoordinateOutOfRange): TileGenerator(zoom=zoom, x_pixel=-1, y_pixel=0) # y to low with pytest.raises(CoordinateOutOfRange): TileGenerator(zoom=zoom, x_pixel=0, y_pixel=-1) # x to high with pytest.raises(CoordinateOutOfRange): TileGenerator(zoom=zoom, x_pixel=max_coordinate + 1, y_pixel=0) # y to high with pytest.raises(CoordinateOutOfRange): TileGenerator(zoom=zoom, x_pixel=0, y_pixel=max_coordinate + 1) # valid y & x if not isinstance( TileGenerator(zoom=zoom, x_pixel=max_coordinate, y_pixel=0), TileGenerator): raise AssertionError if not isinstance( TileGenerator(zoom=zoom, x_pixel=0, y_pixel=max_coordinate), TileGenerator): raise AssertionError
def test_get_bbox_zoom_0_10(tile_generator: TileGenerator): """ test get_bbox for all tiles between zoomlevel 0 to 10 Arguments: tile_generator {TileGenerator} -- default TileGenerator """ # test all tiles for zoom level 0 to 10 tile_generator.get_bbox() for zoom in range(10): tile_generator.zoom = zoom for x in range(int(pow(2, zoom))): for y in range(int(pow(2, zoom))): tile_generator.x_pixel = x tile_generator.y_pixel = y assert isinstance(tile_generator.get_bbox(), Box2d)
def async_generate_tile( year: int, month: int, day: int, style_xml_template: str, zoom: int, x_pixel: float, y_pixel: float, osm_cato_path: str, cache_key: str, ) -> str: """ run celery background task to generate a mapnik tile & cache the tile :param year: request year as INT :param month: request month as INT :param day: request day as INT :param style_xml_template: path to style.xml :param zoom: mapnik zoom level :param x_pixel: mapnik x coordinate :param y_pixel: mapnik y coordinate :param osm_cato_path: path to osm cato :param cache_key: cache key for mapnik tile :return: """ cache.set( cache_key, TileGenerator( request_date=date(year=int(year), month=int(month), day=int(day)), style_xml_template=style_xml_template, zoom=int(zoom), x_pixel=float(x_pixel), y_pixel=float(y_pixel), osm_cato_path=osm_cato_path, use_cache=True, ).render_tile(), ) return cache_key
def generate_osm_tile(request, zoom: int, x_pixel: float, y_pixel: float) -> HttpResponse: """ get a default mapnik tile, without check the valid date :param request: :param zoom: :param x_pixel: :param y_pixel: :return: """ # generate normal osm tile tile_gen: TileGenerator = TileGenerator( request_date=date(year=2000, month=1, day=1), style_xml_template=get_style_xml( generate_style_xml=False, carto_sytle_path=env("CARTO_STYLE_PATH_DEBUG")), zoom=int(zoom), x_pixel=float(x_pixel), y_pixel=float(y_pixel), osm_cato_path=env("CARTO_STYLE_PATH_DEBUG"), ) return HttpResponse(tile_gen.render_tile(), content_type="image/jpeg")
def tile_generator() -> TileGenerator: return TileGenerator(zoom=0, x_pixel=0, y_pixel=0, request_date=datetime(2020, 1, 1), use_cache=False)