Example #1
0
 def __init__(self, path: Path):
     self.path = path
     self.svg_origin = CanvasCoordinate.origin()
     self.position = CanvasCoordinate.origin()
     self.width = None
     self.height = None
     self.rotation = 0
Example #2
0
    def test_identity(self):
        assert Cc.from_pt(1, 1).pt == (1, 1)
        assert Cc.from_in(1, 1).inches == (1, 1)
        assert Cc.from_cm(1, 1).cm == (1, 1)
        assert Cc.from_mm(1, 1).mm == (1, 1)
        assert Cc.from_px(1, 1).px == (1, 1)

        assert Cc.from_pt(2, 2).pt == (2, 2)
        assert Cc.from_in(2, 2).inches == (2, 2)
        assert Cc.from_cm(2, 2).cm == (2, 2)
        assert Cc.from_mm(2, 2).mm == (2, 2)
        assert Cc.from_px(2, 2).px == (2, 2)
Example #3
0
    def test_output_translate_rotate_scale(self):
        path = Path(__file__).parent.joinpath('output/svg_trs.svg')
        path.unlink(missing_ok=True)
        canvas_builder = CanvasBuilder()
        canvas_builder.set_path(path)
        canvas_builder.set_size(
            Cu.from_cm(4),
            Cu.from_cm(4)
        )

        canvas = canvas_builder.build()

        background = Background()
        background.color = (0.8, 1, 0.8, 1)
        background.draw(canvas)

        svg = Svg(Path(__file__).parent.joinpath('test_svg_grid.svg'))
        svg_size = svg.read_svg_size()
        # Should set the origin of the image to the center.
        svg.svg_origin = Cc(svg_size[0] / 2, svg_size[1] / 2)
        # Should position the image in the center of the screen.
        svg.position = Cc.from_cm(2, 2)
        # Should rotate the image clock-wise.
        svg.rotation = math.pi / 8
        # Resizes the image to almost the size of the canvas, but not exactly.
        svg.width = Cu.from_cm(3)
        svg.height = Cu.from_cm(3)
        svg.draw(canvas)

        canvas.close()

        assert path.exists()
    def test_multi_polygons(self):
        path = Path(__file__).parent.joinpath(
            'output/stripe_filled_polygon_drawer_multi_polygons.svg')
        path.unlink(missing_ok=True)
        canvas_builder = CanvasBuilder()
        canvas_builder.set_path(path)
        canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100))

        canvas = canvas_builder.build()

        drawer = StripeFilledPolygonDrawer()
        drawer.stripe_colors = [(1, 1, 0), None, (0.7, 0, 0.7), (0, 0, 0)]
        drawer.stripe_widths = [
            Cu.from_pt(5),
            Cu.from_pt(5),
            Cu.from_pt(5),
            Cu.from_pt(5),
        ]
        drawer.stripe_origin = CanvasCoordinate.from_pt(30, 30)
        drawer.stripe_angle = math.pi / 8  # Vertical stripes
        drawer.geoms = [
            MultiPolygon([
                Polygon([
                    (30, 30),
                    (70, 30),
                    (70, 70),
                    (30, 70),
                    (30, 30),
                ], [[
                    (35, 35),
                    (65, 35),
                    (65, 65),
                    (35, 65),
                    (35, 35),
                ]]),
                Polygon([
                    (40, 40),
                    (60, 40),
                    (60, 60),
                    (40, 60),
                    (40, 40),
                ])
            ])
        ]
        drawer.draw(canvas)

        canvas.close()

        assert path.exists()

        with open(path, 'r') as file:
            data = file.read()
            assert data.find('fill:rgb(100%,100%,0%)') != -1
            assert data.find('fill:rgb(0%,0%,0%)') != -1
            assert data.find('fill:rgb(70%,0%,70%)') != -1
            assert data.find('stroke:none') != -1
            assert data.find('stroke-width:') == -1
Example #5
0
 def logical_extents(self) -> CanvasBbox:
     extent = self._layout.get_extents()[1]
     x = CanvasUnit.from_pt(pangocffi.units_to_double(extent.x))
     y = CanvasUnit.from_pt(pangocffi.units_to_double(extent.y))
     x += self._position.x
     y += self._position.y
     width = CanvasUnit.from_pt(pangocffi.units_to_double(extent.width))
     height = CanvasUnit.from_pt(pangocffi.units_to_double(extent.height))
     return CanvasBbox(CanvasCoordinate(x, y), width, height)
Example #6
0
    def test_scale(self):
        path = Path(__file__).parent.joinpath('output/svg_scale.svg')
        path.unlink(missing_ok=True)
        canvas_builder = CanvasBuilder()
        canvas_builder.set_path(path)
        canvas_builder.set_size(
            Cu.from_cm(9),
            Cu.from_cm(6)
        )

        canvas = canvas_builder.build()

        background = Background()
        background.color = (1, 0.8, 0.8, 1)
        background.draw(canvas)

        # No scaling
        svg = Svg(Path(__file__).parent.joinpath('test_svg.svg'))
        svg.position = Cc.from_cm(1, 1)
        svg.draw(canvas)

        # Scale 2x by height
        svg = Svg(Path(__file__).parent.joinpath('test_svg.svg'))
        svg.position = Cc.from_cm(5, 1)
        svg.width = Cu.from_cm(3)
        svg.draw(canvas)

        # Scale 2x by height
        svg = Svg(Path(__file__).parent.joinpath('test_svg.svg'))
        svg.position = Cc.from_cm(1, 4)
        svg.height = Cu.from_cm(1)
        svg.draw(canvas)

        # Scale without ratio preservation
        svg = Svg(Path(__file__).parent.joinpath('test_svg.svg'))
        svg.position = Cc.from_cm(5, 4)
        svg.width = Cu.from_cm(3)
        svg.height = Cu.from_cm(1)
        svg.draw(canvas)

        canvas.close()

        assert path.exists()
    def test_conversion(self):
        wgs84_crs = pyproj.CRS.from_epsg(4326)
        british_crs = pyproj.CRS.from_epsg(27700)

        coordinate_to_project = GeoCoordinate(55.862777, -4.260919, wgs84_crs)
        expected_canvas_coordinates = CanvasCoordinate(
            CanvasUnit.from_cm(1 + 6), CanvasUnit.from_cm(1 + 4)).pt

        origin_for_geo = GeoCoordinate(258000, 666000, british_crs)
        origin_for_canvas = CanvasCoordinate(CanvasUnit.from_cm(1),
                                             CanvasUnit.from_cm(1))

        # 100 meters for every centimeter
        geo_to_canvas_scale = geo_canvas_ops.GeoCanvasScale(
            100, CanvasUnit.from_cm(1))

        transformation_func = geo_canvas_ops.build_transformer(
            crs=british_crs,
            data_crs=wgs84_crs,
            scale=geo_to_canvas_scale,
            origin_for_geo=origin_for_geo,
            origin_for_canvas=origin_for_canvas)
        self.assert_coordinates_are_close(
            transformation_func(*coordinate_to_project.tuple),
            expected_canvas_coordinates)

        # Repeat the same test, but this time without data_crs:
        transformation_func = geo_canvas_ops.build_transformer(
            crs=british_crs,
            scale=geo_to_canvas_scale,
            origin_for_geo=origin_for_geo,
            origin_for_canvas=origin_for_canvas)
        coordinate_to_project = GeoCoordinate(258600, 665600, british_crs)
        self.assert_coordinates_are_close(
            transformation_func(*coordinate_to_project.tuple),
            expected_canvas_coordinates)
Example #8
0
def build_transformer(
    # The Coordinate Reference System for plotting coordinate data onto
    # the canvas.
    crs: pyproj.CRS,

    # Controls the scale that things on the map will appear in. For
    # example, the number of meters per centimeter.
    scale: GeoCanvasScale,

    # Controls where the map should point to. If `origin_for_canvas` is the
    # default, this geographic coordinate appear on the top-left corner of
    # the canvas.
    origin_for_geo: GeoCoordinate,

    # Controls where on the canvas the origin should correspond to (Useful
    # if your map has margins on the side of the map). Defaults to the
    # top-left corner of the canvas.
    origin_for_canvas: CanvasCoordinate = CanvasCoordinate.origin(),

    # The Coordinate Reference System for the input data. For example, if
    # your data is longitude/latitude data, you will want to set the
    # `data_crs` as `pyproj.CRS.from_epsg(4326)`.
    data_crs: Optional[pyproj.CRS] = None,
) -> Callable[[float, float], Tuple[float, float]]:
    data_transformer: Optional[pyproj.Transformer] = None
    if data_crs is not None:
        data_transformer = pyproj.Transformer.from_proj(data_crs, crs)
    transformed_origin_for_geo = pyproj.Transformer\
        .from_proj(origin_for_geo.crs, crs)\
        .transform(*origin_for_geo.tuple)
    scale_factor = scale.geo_units / scale.canvas_units.pt

    def projection(x: float, y: float) -> Tuple[float, float]:
        if data_transformer is not None:
            coord = data_transformer.transform(x, y)
        else:
            coord = (x, y)
        translated = (coord[0] - transformed_origin_for_geo[0],
                      coord[1] - transformed_origin_for_geo[1])
        # The y-coordinate is inverted because the coordinate space in computer
        # graphics is inverted.
        return (translated[0] / scale_factor + origin_for_canvas.x.pt,
                translated[1] / -scale_factor + origin_for_canvas.y.pt)

    return projection
Example #9
0
    def test_setters_and_getters(self):
        # Create a canvas for the sake of instantiating a layout object. We do
        # this because that is how Pango actually resolves the font dimensions.
        path = Path(__file__).parent.joinpath(
            'output/layout_setters_and_getters.svg')
        canvas_builder = CanvasBuilder()
        canvas_builder.set_path(path)
        canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100))
        canvas = canvas_builder.build()

        layout = Layout(canvas)
        layout.set_text('Hello world')
        layout.set_markup('<span weight="bold">Hello world!</span>')

        assert layout.width is None
        assert layout.height is None
        layout.width = Cu.from_pt(100)
        layout.height = Cu.from_pt(50)
        assert layout.width.pt == 100
        assert layout.height.pt == 50
        layout.reset_width()
        layout.reset_height()
        assert layout.width is None
        assert layout.height is None

        assert layout.position.x.pt == 0
        assert layout.position.y.pt == 0
        layout.position = CanvasCoordinate(Cu.from_pt(10), Cu.from_pt(5))
        assert layout.position.x.pt == 10
        assert layout.position.y.pt == 5

        assert layout.color == (0, 0, 0, 1)
        layout.color = (0, 1, 0, 0.5)
        assert layout.color == (0, 1, 0, 0.5)

        assert layout.alignment == Alignment.LEFT
        layout.alignment = Alignment.RIGHT
        assert layout.alignment == Alignment.RIGHT

        extents = layout.logical_extents
        assert extents.pos.x.pt == 10
        assert extents.pos.y.pt == 5
        assert extents.width.pt > 1
        assert extents.height.pt > 1
    def test_two_vertical_stripes(self):
        path = Path(__file__).parent.joinpath(
            'output/stripe_filled_polygon_drawer_two_vertical_stripes.svg')
        path.unlink(missing_ok=True)
        canvas_builder = CanvasBuilder()
        canvas_builder.set_path(path)
        canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100))

        canvas = canvas_builder.build()

        drawer = StripeFilledPolygonDrawer()
        drawer.stripe_colors = [(0, 1, 0), (0, 0, 1)]
        drawer.stripe_widths = [Cu.from_pt(20), Cu.from_pt(20)]
        drawer.stripe_origin = CanvasCoordinate.from_pt(30, 30)
        drawer.stripe_angle = math.pi / 2  # Vertical stripes
        drawer.geoms = [
            Polygon([
                (30, 30),
                (70, 30),
                (70, 70),
                (30, 70),
                (30, 30),
            ])
        ]
        drawer.draw(canvas)

        canvas.close()

        assert path.exists()

        with open(path, 'r') as file:
            data = file.read()
            assert data.find('M 30 30 L 30 70 L 50 70 L 50 30 Z M 30 30') != -1
            assert data.find('M 50 30 L 50 70 L 70 70 L 70 30 Z M 50 30') != -1
            assert data.find('fill:rgb(0%,100%,0%)') != -1
            assert data.find('fill:rgb(0%,0%,100%)') != -1
            assert data.find('stroke:none') != -1
            assert data.find('stroke-width:') == -1
Example #11
0
 def __init__(self):
     self.geoms = []
     self.stripe_widths = [CanvasUnit.from_pt(1)]
     self.stripe_colors = [(0, 0, 0, 1)]
     self.stripe_angle = 0
     self.stripe_origin = CanvasCoordinate.origin()
    def draw_wgs84(
            self,
            canvas: Canvas,
            mask_wgs84: MultiPolygon,
            world_map_wgs84: MultiPolygon,
            flight_paths_wgs84: MultiLineString
    ):
        wgs84_crs = CRS.from_epsg(4326)

        origin_for_geo = GeoCoordinate(
            0,
            0,
            wgs84_crs
        )
        origin_for_canvas = CanvasCoordinate(
            Cu.from_px(self.margin * 2 + self.azimuthal_width +
                       self.wgs84_width / 2),
            Cu.from_px(self.margin + self.wgs84_height / 2)
        )

        # 1 pixel for every degree
        geo_to_canvas_scale = geo_canvas_ops.GeoCanvasScale(
            1,
            Cu.from_px(1)
        )

        # Todo: These transformations should be handled by build_transformer.
        mask_wgs84 = ops.transform(lambda x, y: (y, x), mask_wgs84)
        world_map_wgs84 = ops.transform(lambda x, y: (y, x), world_map_wgs84)
        flight_paths_wgs84 = ops.transform(
            lambda x, y: (y, x), flight_paths_wgs84
        )

        # Generate a polygon that represents the full range.
        domain_wgs84 = Polygon(
            [(-180, -90), (180, -90), (180, 90), (-180, 90)]
        )

        wgs84_to_canvas = geo_canvas_ops.build_transformer(
            crs=wgs84_crs,
            data_crs=wgs84_crs,
            scale=geo_to_canvas_scale,
            origin_for_geo=origin_for_geo,
            origin_for_canvas=origin_for_canvas
        )

        mask_canvas = ops.transform(wgs84_to_canvas, mask_wgs84)
        world_map_canvas = ops.transform(wgs84_to_canvas, world_map_wgs84)
        flight_paths_canvas = ops.transform(
            wgs84_to_canvas, flight_paths_wgs84
        )
        domain_canvas = ops.transform(wgs84_to_canvas, domain_wgs84)

        polygon_drawer = PolygonDrawer()
        polygon_drawer.fill_color = (0, 0, 0)
        polygon_drawer.geoms = [domain_canvas]
        polygon_drawer.draw(canvas)

        polygon_drawer = PolygonDrawer()
        polygon_drawer.fill_color = self.sea_color
        polygon_drawer.geoms = [mask_canvas]
        polygon_drawer.draw(canvas)

        polygon_drawer = PolygonDrawer()
        polygon_drawer.fill_color = self.land_color
        polygon_drawer.geoms = [world_map_canvas]
        polygon_drawer.draw(canvas)

        polygon_drawer = LineDrawer()
        polygon_drawer.stroke_color = self.line_color
        polygon_drawer.geoms = [flight_paths_canvas]
        polygon_drawer.draw(canvas)
    def draw_azimuthal(
            self,
            canvas: Canvas,
            crs: CRS,
            mask_proj: Polygon,
            world_map_wgs84: MultiPolygon,
            flight_paths_wgs84: MultiLineString
    ):
        origin_for_geo = GeoCoordinate(0, 0, crs)

        origin_x = Cu.from_px(self.margin + self.azimuthal_width / 2)
        origin_y = Cu.from_px(self.margin + self.azimuthal_height / 2)
        origin_for_canvas = CanvasCoordinate(origin_x, origin_y)

        # Fit the projected radius as the width of the canvas
        geo_to_canvas_scale = geo_canvas_ops.GeoCanvasScale(
            crs.ellipsoid.semi_major_metre,
            Cu.from_px(self.azimuthal_width / 2)
        )

        # Convert the projected mask to the canvas.
        proj_to_canvas = geo_canvas_ops.build_transformer(
            crs=crs,
            data_crs=crs,
            scale=geo_to_canvas_scale,
            origin_for_geo=origin_for_geo,
            origin_for_canvas=origin_for_canvas
        )
        mask_canvas = ops.transform(proj_to_canvas, mask_proj)

        # Convert the world map, from wgs84 to proj to canvas
        wgs84_crs = CRS.from_epsg(4326)
        wgs84_to_canvas = geo_canvas_ops.build_transformer(
            crs=crs,
            data_crs=wgs84_crs,
            scale=geo_to_canvas_scale,
            origin_for_geo=origin_for_geo,
            origin_for_canvas=origin_for_canvas
        )
        world_map_canvas = transform_interpolated_euclidean(
            wgs84_to_canvas,
            world_map_wgs84
        )
        flight_paths_canvas = transform_interpolated_euclidean(
            wgs84_to_canvas,
            flight_paths_wgs84
        )

        # Render the polygons
        polygon_drawer = PolygonDrawer()
        polygon_drawer.fill_color = self.sea_color
        polygon_drawer.geoms = [mask_canvas]
        polygon_drawer.draw(canvas)

        polygon_drawer = PolygonDrawer()
        polygon_drawer.fill_color = self.land_color
        polygon_drawer.geoms = [world_map_canvas]
        polygon_drawer.draw(canvas)

        polygon_drawer = LineDrawer()
        polygon_drawer.stroke_color = self.line_color
        polygon_drawer.geoms = [flight_paths_canvas]
        polygon_drawer.draw(canvas)
Example #14
0
 def __init__(self, canvas: Canvas):
     self._layout = pangocairocffi.create_layout(canvas.context)
     self._position = CanvasCoordinate.origin()
     self._color = (0, 0, 0, 1)
Example #15
0
 def test_comparisons(self):
     assert Cc.from_in(1, 1).pt[0] > Cc.from_cm(1, 1).pt[0]
     assert Cc.from_cm(1, 1).pt[0] > Cc.from_mm(1, 1).pt[0]
     assert Cc.from_mm(1, 1).pt[0] > Cc.from_pt(1, 1).pt[0]
Example #16
0
 def test_eq(self):
     assert Cc.from_pt(1, 2) == Cc.from_pt(1, 2)
     assert Cc.from_pt(1, 2) != (1, 2)
Example #17
0
 def test_init(self):
     bbox = CanvasBbox(Cc.from_in(1, 2), Cu.from_in(3), Cu.from_in(4))
     assert bbox.pos == Cc.from_in(1, 2)
     assert bbox.width == Cu.from_in(3)
     assert bbox.height == Cu.from_in(4)