New upstream version 0.12.0
Bas Couwenberg
1 year, 6 months ago
11 | 11 | |
12 | 12 | steps: |
13 | 13 | - name: Checkout source |
14 | uses: actions/checkout@v2 | |
14 | uses: actions/checkout@v3 | |
15 | 15 | |
16 | 16 | - name: Set up Python |
17 | uses: actions/setup-python@v2 | |
17 | uses: actions/setup-python@v4 | |
18 | 18 | with: |
19 | 19 | python-version: "3.x" |
20 | 20 | |
25 | 25 | twine check --strict dist/* |
26 | 26 | |
27 | 27 | - name: Publish distribution to PyPI |
28 | uses: pypa/gh-action-pypi-publish@master | |
28 | uses: pypa/gh-action-pypi-publish@release/v1 | |
29 | 29 | with: |
30 | 30 | user: __token__ |
31 | 31 | password: ${{ secrets.PYPI_API_TOKEN }} |
3 | 3 | Development version |
4 | 4 | ------------------- |
5 | 5 | |
6 | Version 0.12 (October 24, 2022) | |
7 | ------------------------------- | |
8 | ||
9 | The highlight of this release is the support for Shapely 2.0. This makes it possible to | |
10 | test Shapely 2.0 (currently 2.0b1) alongside GeoPandas. | |
11 | ||
12 | Note that if you also have PyGEOS installed, you need to set an environment variable | |
13 | (`USE_PYGEOS=0`) before importing geopandas to actually test Shapely 2.0 features instead of PyGEOS. See | |
14 | https://geopandas.org/en/latest/getting_started/install.html#using-the-optional-pygeos-dependency | |
15 | for more details. | |
16 | ||
6 | 17 | New features and improvements: |
7 | 18 | |
19 | - Added ``normalize()`` method from shapely to GeoSeries/GeoDataframe (#2537). | |
20 | - Added ``make_valid()`` method from shapely to GeoSeries/GeoDataframe (#2539). | |
21 | - Added ``where`` filter to ``read_file`` (#2552). | |
22 | - Updated the distributed natural earth datasets (*naturalearth_lowres* and | |
23 | *naturalearth_cities*) to version 5.1 (#2555). | |
24 | ||
8 | 25 | Deprecations and compatibility notes: |
9 | 26 | |
10 | Bug fixes: | |
11 | ||
12 | Notes on (optional) dependencies: | |
27 | - Accessing the `crs` of a `GeoDataFrame` without active geometry column was deprecated | |
28 | and this now raises an AttributeError (#2578). | |
29 | - Resolved colormap-related warning in ``.explore()`` for recent Matplotlib versions | |
30 | (#2596). | |
31 | ||
32 | Bug fixes: | |
33 | ||
34 | - Fix cryptic error message in ``geopandas.clip()`` when clipping with an empty geometry (#2589). | |
35 | - Accessing `gdf.geometry` where the active geometry column is missing, and a column | |
36 | named `"geometry"` is present will now raise an `AttributeError`, rather than | |
37 | returning `gdf["geometry"]` (#2575). | |
38 | - Combining GeoSeries/GeoDataFrames with ``pandas.concat`` will no longer silently | |
39 | override CRS information if not all inputs have the same CRS (#2056). | |
13 | 40 | |
14 | 41 | Version 0.11.1 (July 24, 2022) |
15 | 42 | ------------------------------ |
0 | Copyright (c) 2013-2016, GeoPandas developers. | |
0 | Copyright (c) 2013-2022, GeoPandas developers. | |
1 | 1 | All rights reserved. |
2 | 2 | |
3 | 3 | Redistribution and use in source and binary forms, with or without |
8 | 8 | * Redistributions in binary form must reproduce the above copyright notice, |
9 | 9 | this list of conditions and the following disclaimer in the documentation |
10 | 10 | and/or other materials provided with the distribution. |
11 | * Neither the name of Enthought, Inc. nor the names of its contributors may | |
11 | * Neither the name of GeoPandas nor the names of its contributors may | |
12 | 12 | be used to endorse or promote products derived from this software without |
13 | 13 | specific prior written permission. |
14 | 14 |
4 | 4 | - python=3.10 |
5 | 5 | - cython |
6 | 6 | # required |
7 | - shapely | |
8 | - fiona | |
9 | 7 | - pyproj |
10 | 8 | - geos |
11 | 9 | - packaging |
27 | 25 | # dev versions of packages |
28 | 26 | - --pre --extra-index https://pypi.anaconda.org/scipy-wheels-nightly/simple |
29 | 27 | - numpy |
28 | - fiona | |
30 | 29 | - git+https://github.com/pandas-dev/pandas.git@main |
31 | 30 | - git+https://github.com/matplotlib/matplotlib.git@main |
32 | # - git+https://github.com/Toblerity/Shapely.git@main | |
31 | - git+https://github.com/shapely/shapely.git@main | |
33 | 32 | - git+https://github.com/pygeos/pygeos.git@master |
34 | 33 | - git+https://github.com/python-visualization/folium.git@main |
35 | 34 | - git+https://github.com/geopandas/xyzservices.git@main |
7 | 7 | - shapely |
8 | 8 | # - fiona # build with only pyogrio |
9 | 9 | - libgdal |
10 | - tiledb=2.11.3 | |
10 | 11 | - pyproj |
11 | 12 | - pygeos |
12 | 13 | - packaging |
26 | 27 | - SQLalchemy |
27 | 28 | - libspatialite |
28 | 29 | - pyarrow |
29 | - pip | |
30 | - pip: | |
31 | - pyogrio | |
30 | - pyogrio |
3 | 3 | dependencies: |
4 | 4 | - python=3.8 |
5 | 5 | # required |
6 | - numpy=1.21 # pin to < 1.23 for compat with older shapely on defaults channel | |
6 | 7 | - pandas |
7 | 8 | - shapely |
8 | 9 | - fiona |
0 | 0 | name: test |
1 | 1 | channels: |
2 | 2 | - conda-forge |
3 | # - conda-forge/label/shapely_dev | |
3 | 4 | dependencies: |
4 | 5 | - python=3.9 |
5 | 6 | # required |
6 | 7 | - pandas=1.3 |
7 | - shapely | |
8 | # - shapely=2 | |
8 | 9 | - fiona |
10 | - tiledb=2.11.3 | |
9 | 11 | - pyproj |
10 | - pygeos | |
12 | # use this build to have one with only shapely 2.0 installed | |
13 | # - pygeos | |
11 | 14 | - packaging |
12 | 15 | # testing |
13 | 16 | - pytest |
31 | 34 | - pyarrow |
32 | 35 | # doctest testing |
33 | 36 | - pytest-doctestplus |
34 | ||
37 | - pip | |
38 | - pip: | |
39 | - --pre | |
40 | - shapely==2.0b1 |
6 | 6 | - pandas=1.2 |
7 | 7 | - shapely |
8 | 8 | - fiona |
9 | - tiledb=2.11.3 | |
9 | 10 | - pyproj |
10 | 11 | - pygeos |
11 | 12 | - packaging |
1 | 1 | channels: |
2 | 2 | - conda-forge |
3 | 3 | dependencies: |
4 | - python=3.9.7 | |
5 | - pandas=1.3.2 | |
6 | - shapely=1.7.1 | |
7 | - fiona=1.8.20 | |
8 | - pyproj=3.2.1 | |
9 | - rtree=0.9.7 | |
10 | - geopy=2.2.0 | |
11 | - matplotlib=3.4.3 | |
12 | - mapclassify=2.4.3 | |
13 | - sphinx=4.2.0 | |
14 | - pydata-sphinx-theme=0.6.3 | |
15 | - numpydoc=1.1.0 | |
16 | - ipython=7.27.0 | |
17 | - pillow=8.3.2 | |
18 | - mock=4.0.3 | |
19 | - cartopy=0.20.0 | |
20 | - pyepsg=0.4.0 | |
21 | - contextily=1.1.0 | |
22 | - rasterio=1.2.8 | |
23 | - geoplot=0.4.4 | |
24 | - sphinx-gallery=0.9.0 | |
25 | - jinja2=3.0.1 | |
26 | - doc2dash=2.3.0 | |
27 | - matplotlib-scalebar=0.7.2 | |
4 | - python | |
5 | - pandas | |
6 | - shapely | |
7 | - fiona | |
8 | - pyproj | |
9 | - rtree | |
10 | - geopy | |
11 | - matplotlib | |
12 | - mapclassify | |
13 | - sphinx | |
14 | - pydata-sphinx-theme | |
15 | - numpydoc | |
16 | - ipython | |
17 | - pillow | |
18 | - mock | |
19 | - cartopy | |
20 | - pyepsg | |
21 | - contextily | |
22 | - rasterio | |
23 | - geoplot | |
24 | - sphinx-gallery | |
25 | - jinja2 | |
26 | - doc2dash | |
27 | - matplotlib-scalebar | |
28 | 28 | # specify additional dependencies to reduce solving for conda |
29 | - gdal=3.3.2 | |
30 | - libgdal=3.3.2 | |
31 | - proj=8.0.1 | |
32 | - geos=3.9.1 | |
33 | - nbsphinx=0.8.7 | |
34 | - jupyter_client=7.0.3 | |
35 | - ipykernel=6.4.1 | |
36 | - myst-parser=0.15.2 | |
37 | - folium=0.12.0 | |
38 | - libpysal=4.5.1 | |
39 | - pygeos=0.10.2 | |
40 | - xyzservices=2021.9.1 | |
41 | - packaging=21.0 | |
29 | - gdal | |
30 | - libgdal | |
31 | - proj | |
32 | - geos | |
33 | - nbsphinx | |
34 | - jupyter_client | |
35 | - ipykernel | |
36 | - myst-parser | |
37 | - folium | |
38 | - libpysal | |
39 | - pygeos | |
40 | - xyzservices | |
41 | - packaging | |
42 | 42 | - pip |
43 | 43 | - pip: |
44 | 44 | - sphinx-toggleprompt |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><line x1="120.15" y1="48.9" x2="162.49" y2="48.9" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="161.03 53.89 169.67 48.9 161.03 43.91 161.03 53.89" style="fill:#1d1d1b"/><circle cx="45.9" cy="48.9" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="48.9" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><g style="opacity:0.8"><circle cx="214.68" cy="48.9" r="34.98" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="48.9" r="34.98" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><path d="M214.68,48.9a35,35,0,0,1,17.48-30.29,35,35,0,1,0,0,60.57A34.93,34.93,0,0,1,214.68,48.9Z" style="fill:#fec905;opacity:0.8"/></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><line x1="120.15" y1="48.9" x2="162.49" y2="48.9" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="161.03 53.89 169.67 48.9 161.03 43.91 161.03 53.89" style="fill:#139c5a"/><circle cx="45.9" cy="48.9" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="48.9" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><g style="opacity:0.8"><circle cx="214.68" cy="48.9" r="34.98" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="48.9" r="34.98" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><path d="M214.68,48.9a35,35,0,0,1,17.48-30.29,35,35,0,1,0,0,60.57A34.93,34.93,0,0,1,214.68,48.9Z" style="fill:#fec905;opacity:0.8"/></svg>⏎ |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><g style="opacity:0.8"><circle cx="214.68" cy="51.28" r="34.98" transform="matrix(0.94, -0.33, 0.33, 0.94, -4.87, 73.95)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="51.28" r="34.98" transform="translate(74.78 236.69) rotate(-58.28)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><line x1="120.15" y1="51.28" x2="162.49" y2="51.28" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="161.03 56.27 169.67 51.28 161.03 46.3 161.03 56.27" style="fill:#1d1d1b"/><circle cx="45.9" cy="51.28" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="51.28" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><ellipse cx="232.16" cy="51.28" rx="17.49" ry="30.29" style="fill:#fec905;opacity:0.8"/></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><g style="opacity:0.8"><circle cx="214.68" cy="51.28" r="34.98" transform="matrix(0.94, -0.33, 0.33, 0.94, -4.87, 73.95)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="51.28" r="34.98" transform="translate(74.78 236.69) rotate(-58.28)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><line x1="120.15" y1="51.28" x2="162.49" y2="51.28" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="161.03 56.27 169.67 51.28 161.03 46.3 161.03 56.27" style="fill:#139c5a"/><circle cx="45.9" cy="51.28" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="51.28" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><ellipse cx="232.16" cy="51.28" rx="17.49" ry="30.29" style="fill:#fec905;opacity:0.8"/></svg>⏎ |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><g style="opacity:0.8"><circle cx="214.68" cy="51.28" r="34.98" transform="matrix(0.94, -0.33, 0.33, 0.94, -4.87, 73.95)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="51.28" r="34.98" transform="translate(74.78 236.69) rotate(-58.28)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><line x1="120.15" y1="51.28" x2="162.49" y2="51.28" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="161.03 56.27 169.67 51.28 161.03 46.3 161.03 56.27" style="fill:#1d1d1b"/><circle cx="45.9" cy="51.28" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="51.28" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><g style="opacity:0.8"><path d="M214.68,51.28A35,35,0,0,1,232.16,21a35,35,0,1,0,0,60.57A35,35,0,0,1,214.68,51.28Z" style="fill:#fec905"/><path d="M249.65,16.31A34.81,34.81,0,0,0,232.16,21a35,35,0,0,1,0,60.57,35,35,0,1,0,17.49-65.26Z" style="fill:#fec905"/></g></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><g style="opacity:0.8"><circle cx="214.68" cy="51.28" r="34.98" transform="matrix(0.94, -0.33, 0.33, 0.94, -4.87, 73.95)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="51.28" r="34.98" transform="translate(74.78 236.69) rotate(-58.28)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><line x1="120.15" y1="51.28" x2="162.49" y2="51.28" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="161.03 56.27 169.67 51.28 161.03 46.3 161.03 56.27" style="fill:#139c5a"/><circle cx="45.9" cy="51.28" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="51.28" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><g style="opacity:0.8"><path d="M214.68,51.28A35,35,0,0,1,232.16,21a35,35,0,1,0,0,60.57A35,35,0,0,1,214.68,51.28Z" style="fill:#fec905"/><path d="M249.65,16.31A34.81,34.81,0,0,0,232.16,21a35,35,0,0,1,0,60.57,35,35,0,1,0,17.49-65.26Z" style="fill:#fec905"/></g></svg>⏎ |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><g style="opacity:0.8"><circle cx="214.68" cy="50.49" r="34.98" transform="matrix(0.94, -0.33, 0.33, 0.94, -4.61, 73.91)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="50.49" r="34.98" transform="translate(75.45 236.31) rotate(-58.28)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><line x1="120.15" y1="50.49" x2="162.49" y2="50.49" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="161.03 55.48 169.67 50.49 161.03 45.5 161.03 55.48" style="fill:#1d1d1b"/><circle cx="45.9" cy="50.49" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="50.49" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><path d="M249.65,15.51a34.91,34.91,0,0,0-17.49,4.69,35,35,0,1,0,0,60.57,35,35,0,1,0,17.49-65.26Z" style="fill:#fec905;opacity:0.8"/></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="105.44mm" height="35.61mm" viewBox="0 0 298.88 100.95"><g style="opacity:0.8"><circle cx="214.68" cy="50.49" r="34.98" transform="matrix(0.94, -0.33, 0.33, 0.94, -4.61, 73.91)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><g style="opacity:0.8"><circle cx="249.65" cy="50.49" r="34.98" transform="translate(75.45 236.31) rotate(-58.28)" style="fill:none;stroke:#fec905;stroke-miterlimit:10;stroke-dasharray:1.9972946643829346,1.9972946643829346"/></g><line x1="120.15" y1="50.49" x2="162.49" y2="50.49" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="161.03 55.48 169.67 50.49 161.03 45.5 161.03 55.48" style="fill:#139c5a"/><circle cx="45.9" cy="50.49" r="34.98" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><circle cx="80.88" cy="50.49" r="34.98" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><path d="M249.65,15.51a34.91,34.91,0,0,0-17.49,4.69,35,35,0,1,0,0,60.57,35,35,0,1,0,17.49-65.26Z" style="fill:#fec905;opacity:0.8"/></svg>⏎ |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="115.16mm" height="58.15mm" viewBox="0 0 326.44 164.84"><rect x="5.24" y="27.78" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="5.24" y="46.88" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="5.24" y="65.98" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="5.24" y="85.08" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="5.24" y="104.19" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="48.04" y="27.78" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="8.68" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="48.04" y="46.88" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="65.98" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="85.08" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="104.19" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="194.49" y="27.78" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="194.49" y="46.88" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="194.49" y="65.98" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="194.49" y="85.08" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="194.49" y="104.19" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="237.29" y="27.78" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="8.68" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="237.29" y="46.88" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="65.98" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="85.08" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="104.19" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><line x1="138.48" y1="37.33" x2="180.83" y2="37.33" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="179.37 42.32 188 37.33 179.37 32.35 179.37 42.32" style="fill:#1d1d1b"/><line x1="138.48" y1="56.43" x2="180.83" y2="56.43" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="179.37 61.42 188 56.43 179.37 51.45 179.37 61.42" style="fill:#1d1d1b"/><line x1="138.48" y1="75.53" x2="180.83" y2="75.53" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="179.37 80.52 188 75.53 179.37 70.55 179.37 80.52" style="fill:#1d1d1b"/><line x1="138.48" y1="94.64" x2="180.83" y2="94.64" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="179.37 99.62 188 94.64 179.37 89.65 179.37 99.62" style="fill:#1d1d1b"/><line x1="138.48" y1="113.74" x2="180.83" y2="113.74" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="179.37 118.72 188 113.74 179.37 108.75 179.37 118.72" style="fill:#1d1d1b"/><rect x="3.19" y="25.4" width="42.25" height="99.95" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10;stroke-width:0.5px"/><path d="M7.38,143.15H9.76v-4.21H7.68v-1.19H11v5.4H13v1.2H7.38Zm1.9-7.21a.73.73,0,0,1,.22-.56.84.84,0,0,1,.59-.21h.29a.84.84,0,0,1,.6.21.77.77,0,0,1,.22.56.75.75,0,0,1-.22.55.85.85,0,0,1-.61.2h-.28a.83.83,0,0,1-.59-.2A.71.71,0,0,1,9.28,135.94Z" style="fill:#1d1d1b"/><path d="M14.46,137.75h1.25v1.14h.11a1.48,1.48,0,0,1,.59-.94,2,2,0,0,1,1.17-.32,2.4,2.4,0,0,1,.92.17,1.89,1.89,0,0,1,.68.49,2.13,2.13,0,0,1,.43.75,3,3,0,0,1,.14,1v4.34H18.43v-4.16a1.5,1.5,0,0,0-.34-1.06,1.27,1.27,0,0,0-1-.37,1.28,1.28,0,0,0-1,.39,1.57,1.57,0,0,0-.36,1.08v4.12H14.46Z" style="fill:#1d1d1b"/><path d="M21.54,140.21a3.49,3.49,0,0,1,.16-1.07,2.29,2.29,0,0,1,.46-.81,1.85,1.85,0,0,1,.71-.52,2.34,2.34,0,0,1,.93-.18A2,2,0,0,1,25,138a1.47,1.47,0,0,1,.61.93h.11l0-.41c0-.12,0-.24,0-.38s0-.25,0-.35v-2H27v8.64H25.71v-1.14H25.6a1.47,1.47,0,0,1-.61.93,2,2,0,0,1-1.19.33,2.34,2.34,0,0,1-.93-.18,1.85,1.85,0,0,1-.71-.52,2.25,2.25,0,0,1-.46-.82,3.6,3.6,0,0,1-.16-1.08Zm1.32,0v1.62a1.49,1.49,0,0,0,.38,1.07,1.35,1.35,0,0,0,1,.4,1.28,1.28,0,0,0,1-.4,1.45,1.45,0,0,0,.37-1v-1.64a1.47,1.47,0,0,0-.37-1.06,1.3,1.3,0,0,0-1-.4,1.38,1.38,0,0,0-1,.39A1.49,1.49,0,0,0,22.86,140.23Z" style="fill:#1d1d1b"/><path d="M28.74,140.19a3,3,0,0,1,.19-1.07,2.27,2.27,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.24,2.24,0,0,1,.55.8,2.8,2.8,0,0,1,.19,1.07v1.21H30.05v.47a1.49,1.49,0,0,0,.39,1.1,1.46,1.46,0,0,0,1.07.4,2.34,2.34,0,0,0,.88-.16,1,1,0,0,0,.54-.47h1.3a2.29,2.29,0,0,1-.35.72,2.23,2.23,0,0,1-.6.54,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.74,2.74,0,0,1-.2-1.06Zm1.31.25H33v-.25a1.35,1.35,0,0,0-1.47-1.46,1.34,1.34,0,0,0-1.46,1.46Z" style="fill:#1d1d1b"/><path d="M38,141l-2.23-3.2h1.53l1.23,1.86a1.05,1.05,0,0,1,.15.3.43.43,0,0,1,0,.13h.06a.43.43,0,0,1,0-.13,1,1,0,0,1,.15-.3l1.24-1.86H41.7l-2.22,3.19,2.37,3.41H40.31l-1.33-2a.59.59,0,0,1-.1-.17l-.07-.14-.06-.14h-.07s0,.1-.06.15l-.06.14a1.4,1.4,0,0,1-.1.16l-1.35,2H35.58Z" style="fill:#1d1d1b"/><rect x="46.28" y="6.23" width="86.81" height="119.12" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10;stroke-width:0.5px"/><path d="M135.05,144.47a3.23,3.23,0,0,1-1.15-.19,2.52,2.52,0,0,1-.87-.52,2.36,2.36,0,0,1-.55-.83,2.6,2.6,0,0,1-.2-1.06v-1.65a2.65,2.65,0,0,1,.2-1.07,2.32,2.32,0,0,1,.55-.82,2.54,2.54,0,0,1,.87-.53,3.76,3.76,0,0,1,2.3,0,2.38,2.38,0,0,1,.87.52,2.32,2.32,0,0,1,.55.82,2.59,2.59,0,0,1,.2,1.07v1.66a2.83,2.83,0,0,1-.19,1.07,2.24,2.24,0,0,1-.55.82,2.34,2.34,0,0,1-.88.52A3.23,3.23,0,0,1,135.05,144.47Zm-1.45-2.6a1.5,1.5,0,0,0,2.51,1,1.4,1.4,0,0,0,.39-1v-1.65a1.39,1.39,0,0,0-.39-1.05,1.64,1.64,0,0,0-2.12,0,1.39,1.39,0,0,0-.39,1.05Z" style="fill:#1d1d1b"/><path d="M139.61,137.75h1.26v1.14h.1a1.46,1.46,0,0,1,.6-.93,2.07,2.07,0,0,1,1.2-.33,2.32,2.32,0,0,1,.92.18,1.85,1.85,0,0,1,.71.52,2.29,2.29,0,0,1,.46.81,3.21,3.21,0,0,1,.16,1.07v1.66a3.31,3.31,0,0,1-.16,1.08,2.25,2.25,0,0,1-.46.82,1.85,1.85,0,0,1-.71.52,2.32,2.32,0,0,1-.92.18,2.07,2.07,0,0,1-1.2-.33,1.46,1.46,0,0,1-.6-.93h-.1a1,1,0,0,0,0,.13,2.33,2.33,0,0,1,0,.29c0,.11,0,.23,0,.36s0,.25,0,.36v2h-1.32Zm1.32,2.48v1.64a1.45,1.45,0,0,0,.37,1,1.25,1.25,0,0,0,1,.4,1.33,1.33,0,0,0,1-.4,1.45,1.45,0,0,0,.38-1.07v-1.62a1.45,1.45,0,0,0-.38-1.07,1.36,1.36,0,0,0-1-.39,1.28,1.28,0,0,0-1,.4A1.47,1.47,0,0,0,140.93,140.23Z" style="fill:#1d1d1b"/><path d="M146.68,140.19a2.8,2.8,0,0,1,.19-1.07,2.24,2.24,0,0,1,.55-.8,2.46,2.46,0,0,1,.88-.51,3.43,3.43,0,0,1,1.15-.18,3.47,3.47,0,0,1,1.15.18,2.6,2.6,0,0,1,.87.51,2.24,2.24,0,0,1,.55.8,3,3,0,0,1,.19,1.07v1.21H148v.47a1.46,1.46,0,0,0,.4,1.1,1.44,1.44,0,0,0,1.07.4,2.29,2.29,0,0,0,.87-.16,1,1,0,0,0,.54-.47h1.3a2.1,2.1,0,0,1-.35.72,2.35,2.35,0,0,1-.59.54,2.9,2.9,0,0,1-.8.35,3.85,3.85,0,0,1-1,.12,3.43,3.43,0,0,1-1.15-.18,2.38,2.38,0,0,1-.87-.52,2.2,2.2,0,0,1-.55-.81,2.57,2.57,0,0,1-.2-1.06Zm1.3.25h2.93v-.25a1.52,1.52,0,0,0-2.54-1.07,1.42,1.42,0,0,0-.39,1.07Z" style="fill:#1d1d1b"/><path d="M155.52,137.75v1.14h.11a1.45,1.45,0,0,1,.62-.93,2.11,2.11,0,0,1,1.22-.33,2.15,2.15,0,0,1,1.65.65,2.61,2.61,0,0,1,.6,1.82v.43h-1.35v-.3a1.5,1.5,0,0,0-.38-1.08,1.32,1.32,0,0,0-1-.39,1.3,1.3,0,0,0-1,.39,1.53,1.53,0,0,0-.38,1.08v4.12h-1.32v-6.6Z" style="fill:#1d1d1b"/><path d="M160.92,142.47a1.9,1.9,0,0,1,.67-1.53,2.71,2.71,0,0,1,1.8-.56h1.79v-.55a1,1,0,0,0-.36-.83,1.67,1.67,0,0,0-1-.29,1.82,1.82,0,0,0-.88.19.9.9,0,0,0-.48.52h-1.29a1.94,1.94,0,0,1,.87-1.31,3.17,3.17,0,0,1,1.79-.48,3.1,3.1,0,0,1,2,.58,1.91,1.91,0,0,1,.72,1.58v4.56h-1.22v-1.28h-.1a1.56,1.56,0,0,1-.7,1,2.51,2.51,0,0,1-1.41.38,2.18,2.18,0,0,1-1.57-.55A1.92,1.92,0,0,1,160.92,142.47Zm1.32-.11a1,1,0,0,0,.33.78,1.37,1.37,0,0,0,.91.27,2.43,2.43,0,0,0,.68-.09,2,2,0,0,0,.54-.27,1.4,1.4,0,0,0,.36-.41,1.13,1.13,0,0,0,.12-.51v-.83h-1.77a1.24,1.24,0,0,0-.86.28A1,1,0,0,0,162.24,142.36Z" style="fill:#1d1d1b"/><path d="M168.07,137.75h1.86v-2h1.32v2h2.53v1.19h-2.53v3.51a.69.69,0,0,0,.19.51.73.73,0,0,0,.54.19h1.68v1.2h-1.74a2,2,0,0,1-1.45-.52,1.81,1.81,0,0,1-.54-1.38v-3.51h-1.86Z" style="fill:#1d1d1b"/><path d="M175.72,143.15h2.37v-4.21H176v-1.19h3.36v5.4h1.93v1.2h-5.59Zm1.89-7.21a.77.77,0,0,1,.22-.56.84.84,0,0,1,.6-.21h.29a.84.84,0,0,1,.59.21.73.73,0,0,1,.22.56.71.71,0,0,1-.22.55.85.85,0,0,1-.61.2h-.27a.84.84,0,0,1-.6-.2A.75.75,0,0,1,177.61,135.94Z" style="fill:#1d1d1b"/><path d="M185.45,144.47a3.19,3.19,0,0,1-1.15-.19,2.52,2.52,0,0,1-.87-.52,2.36,2.36,0,0,1-.55-.83,2.6,2.6,0,0,1-.2-1.06v-1.65a2.65,2.65,0,0,1,.2-1.07,2.32,2.32,0,0,1,.55-.82,2.54,2.54,0,0,1,.87-.53,3.43,3.43,0,0,1,1.15-.18,3.47,3.47,0,0,1,1.15.18,2.46,2.46,0,0,1,.87.52,2.32,2.32,0,0,1,.55.82,2.76,2.76,0,0,1,.2,1.07v1.66a2.83,2.83,0,0,1-.19,1.07,2.24,2.24,0,0,1-.55.82,2.42,2.42,0,0,1-.88.52A3.23,3.23,0,0,1,185.45,144.47Zm-1.45-2.6a1.4,1.4,0,0,0,.38,1,1.65,1.65,0,0,0,2.13,0,1.4,1.4,0,0,0,.39-1v-1.65a1.39,1.39,0,0,0-.39-1.05,1.64,1.64,0,0,0-2.12,0,1.39,1.39,0,0,0-.39,1.05Z" style="fill:#1d1d1b"/><path d="M190,137.75h1.24v1.14h.11a1.52,1.52,0,0,1,.59-.94,2,2,0,0,1,1.18-.32,2.34,2.34,0,0,1,.91.17,2,2,0,0,1,.69.49,2,2,0,0,1,.42.75,3,3,0,0,1,.15,1v4.34H194v-4.16a1.5,1.5,0,0,0-.35-1.06,1.24,1.24,0,0,0-1-.37,1.26,1.26,0,0,0-1,.39,1.57,1.57,0,0,0-.36,1.08v4.12H190Z" style="fill:#1d1d1b"/><path d="M61.67,140.17a3.51,3.51,0,0,1,.15-1,2.33,2.33,0,0,1,.46-.8,2.05,2.05,0,0,1,.72-.51,2.34,2.34,0,0,1,.93-.18,2.1,2.1,0,0,1,1.23.35,1.56,1.56,0,0,1,.63,1h.09v-1.2h1.25V144a2.3,2.3,0,0,1-.72,1.79,2.83,2.83,0,0,1-2,.65h-1.7v-1.13h1.7a1.36,1.36,0,0,0,1-.35,1.27,1.27,0,0,0,.37-1v-.22l.05-1.2h-.08a1.61,1.61,0,0,1-.65,1,2,2,0,0,1-1.22.35,2.34,2.34,0,0,1-.93-.18,2.11,2.11,0,0,1-.71-.51,2.31,2.31,0,0,1-.45-.8,3.13,3.13,0,0,1-.16-1.05ZM63,141.29a1.31,1.31,0,0,0,1.4,1.42,1.31,1.31,0,0,0,1.42-1.42v-1.1a1.31,1.31,0,0,0-1.42-1.42,1.31,1.31,0,0,0-1.4,1.42Z" style="fill:#1d1d1b"/><path d="M68.91,140.19a3,3,0,0,1,.19-1.07,2.27,2.27,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.12,2.12,0,0,1,.55.8,2.79,2.79,0,0,1,.2,1.07v1.21H70.22v.47a1.49,1.49,0,0,0,.39,1.1,1.46,1.46,0,0,0,1.07.4,2.34,2.34,0,0,0,.88-.16,1,1,0,0,0,.54-.47h1.3a2.1,2.1,0,0,1-.35.72,2.35,2.35,0,0,1-.59.54,3,3,0,0,1-.81.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.74,2.74,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.47,1.47,0,1,0-2.93,0Z" style="fill:#1d1d1b"/><path d="M78.88,144.47a3.17,3.17,0,0,1-1.14-.19,2.52,2.52,0,0,1-.87-.52,2.39,2.39,0,0,1-.56-.83,2.78,2.78,0,0,1-.2-1.06v-1.65a2.83,2.83,0,0,1,.2-1.07,2.35,2.35,0,0,1,.56-.82,2.54,2.54,0,0,1,.87-.53,3.73,3.73,0,0,1,2.29,0,2.38,2.38,0,0,1,.87.52,2.35,2.35,0,0,1,.56.82,2.76,2.76,0,0,1,.2,1.07v1.66a2.83,2.83,0,0,1-.2,1.07,2.12,2.12,0,0,1-.55.82,2.38,2.38,0,0,1-.87.52A3.28,3.28,0,0,1,78.88,144.47Zm-1.45-2.6a1.35,1.35,0,0,0,1.45,1.44,1.45,1.45,0,0,0,1.07-.39,1.4,1.4,0,0,0,.39-1v-1.65a1.39,1.39,0,0,0-.39-1.05,1.65,1.65,0,0,0-2.13,0,1.39,1.39,0,0,0-.39,1.05Z" style="fill:#1d1d1b"/><path d="M83.14,144.35v-6.6h1.1v1h.09a1.24,1.24,0,0,1,.32-.8,1,1,0,0,1,.76-.29.91.91,0,0,1,.72.28,1.62,1.62,0,0,1,.39.81h.08a1.15,1.15,0,0,1,.33-.8,1,1,0,0,1,.77-.29,1.19,1.19,0,0,1,1,.45,1.93,1.93,0,0,1,.37,1.24v5H87.82v-5a1,1,0,0,0-.15-.59.57.57,0,0,0-.46-.2q-.63,0-.63.84v4.92h-1v-5a.94.94,0,0,0-.16-.59.63.63,0,0,0-.48-.2q-.63,0-.63.84v4.92Z" style="fill:#1d1d1b"/><path d="M90.51,140.19a3,3,0,0,1,.19-1.07,2.27,2.27,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.12,2.12,0,0,1,.55.8,2.79,2.79,0,0,1,.2,1.07v1.21H91.82v.47a1.49,1.49,0,0,0,.39,1.1,1.46,1.46,0,0,0,1.07.4,2.34,2.34,0,0,0,.88-.16,1,1,0,0,0,.54-.47H96a2.1,2.1,0,0,1-.35.72,2.35,2.35,0,0,1-.59.54,3,3,0,0,1-.81.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.74,2.74,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.35,1.35,0,0,0-1.47-1.46,1.34,1.34,0,0,0-1.46,1.46Z" style="fill:#1d1d1b"/><path d="M97.51,137.75h1.86v-2h1.32v2h2.53v1.19h-2.53v3.51a.66.66,0,0,0,.19.51.73.73,0,0,0,.54.19h1.68v1.2h-1.74a2,2,0,0,1-1.46-.52,1.84,1.84,0,0,1-.53-1.38v-3.51H97.51Z" style="fill:#1d1d1b"/><path d="M106.56,137.75v1.14h.1a1.48,1.48,0,0,1,.62-.93,2.14,2.14,0,0,1,1.22-.33,2.17,2.17,0,0,1,1.66.65,2.61,2.61,0,0,1,.6,1.82v.43H109.4v-.3a1.49,1.49,0,0,0-.37-1.08,1.35,1.35,0,0,0-1-.39,1.33,1.33,0,0,0-1,.39,1.52,1.52,0,0,0-.37,1.08v4.12h-1.32v-6.6Z" style="fill:#1d1d1b"/><path d="M111.79,137.75h1.44l1.39,3.64a4.26,4.26,0,0,1,.15.47,4.31,4.31,0,0,1,.09.44l.06.43h.1a3.28,3.28,0,0,1,0-.43c0-.13.06-.27.09-.43s.09-.32.14-.48l1.3-3.64H118l-3.17,8.64h-1.39l.91-2.46Z" style="fill:#1d1d1b"/></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="115.16mm" height="58.15mm" viewBox="0 0 326.44 164.84"><rect x="5.24" y="27.78" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="5.24" y="46.88" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="5.24" y="65.98" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="5.24" y="85.08" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="5.24" y="104.19" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="48.04" y="27.78" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="8.68" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="48.04" y="46.88" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="65.98" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="85.08" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="48.04" y="104.19" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="194.49" y="27.78" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="194.49" y="46.88" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="194.49" y="65.98" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="194.49" y="85.08" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="194.49" y="104.19" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="237.29" y="27.78" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="8.68" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="237.29" y="46.88" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="65.98" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="85.08" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="237.29" y="104.19" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><line x1="138.48" y1="37.33" x2="180.83" y2="37.33" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="179.37 42.32 188 37.33 179.37 32.35 179.37 42.32" style="fill:#139c5a"/><line x1="138.48" y1="56.43" x2="180.83" y2="56.43" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="179.37 61.42 188 56.43 179.37 51.45 179.37 61.42" style="fill:#139c5a"/><line x1="138.48" y1="75.53" x2="180.83" y2="75.53" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="179.37 80.52 188 75.53 179.37 70.55 179.37 80.52" style="fill:#139c5a"/><line x1="138.48" y1="94.64" x2="180.83" y2="94.64" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="179.37 99.62 188 94.64 179.37 89.65 179.37 99.62" style="fill:#139c5a"/><line x1="138.48" y1="113.74" x2="180.83" y2="113.74" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="179.37 118.72 188 113.74 179.37 108.75 179.37 118.72" style="fill:#139c5a"/><rect x="3.19" y="25.4" width="42.25" height="99.95" style="fill:none;stroke:#139c5a;stroke-miterlimit:10;stroke-width:0.5px"/><path d="M7.38,143.15H9.76v-4.21H7.68v-1.19H11v5.4H13v1.2H7.38Zm1.9-7.21a.73.73,0,0,1,.22-.56.84.84,0,0,1,.59-.21h.29a.84.84,0,0,1,.6.21.77.77,0,0,1,.22.56.75.75,0,0,1-.22.55.85.85,0,0,1-.61.2h-.28a.83.83,0,0,1-.59-.2A.71.71,0,0,1,9.28,135.94Z" style="fill:#139c5a"/><path d="M14.46,137.75h1.25v1.14h.11a1.48,1.48,0,0,1,.59-.94,2,2,0,0,1,1.17-.32,2.4,2.4,0,0,1,.92.17,1.89,1.89,0,0,1,.68.49,2.13,2.13,0,0,1,.43.75,3,3,0,0,1,.14,1v4.34H18.43v-4.16a1.5,1.5,0,0,0-.34-1.06,1.27,1.27,0,0,0-1-.37,1.28,1.28,0,0,0-1,.39,1.57,1.57,0,0,0-.36,1.08v4.12H14.46Z" style="fill:#139c5a"/><path d="M21.54,140.21a3.49,3.49,0,0,1,.16-1.07,2.29,2.29,0,0,1,.46-.81,1.85,1.85,0,0,1,.71-.52,2.34,2.34,0,0,1,.93-.18A2,2,0,0,1,25,138a1.47,1.47,0,0,1,.61.93h.11l0-.41c0-.12,0-.24,0-.38s0-.25,0-.35v-2H27v8.64H25.71v-1.14H25.6a1.47,1.47,0,0,1-.61.93,2,2,0,0,1-1.19.33,2.34,2.34,0,0,1-.93-.18,1.85,1.85,0,0,1-.71-.52,2.25,2.25,0,0,1-.46-.82,3.6,3.6,0,0,1-.16-1.08Zm1.32,0v1.62a1.49,1.49,0,0,0,.38,1.07,1.35,1.35,0,0,0,1,.4,1.28,1.28,0,0,0,1-.4,1.45,1.45,0,0,0,.37-1v-1.64a1.47,1.47,0,0,0-.37-1.06,1.3,1.3,0,0,0-1-.4,1.38,1.38,0,0,0-1,.39A1.49,1.49,0,0,0,22.86,140.23Z" style="fill:#139c5a"/><path d="M28.74,140.19a3,3,0,0,1,.19-1.07,2.27,2.27,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.24,2.24,0,0,1,.55.8,2.8,2.8,0,0,1,.19,1.07v1.21H30.05v.47a1.49,1.49,0,0,0,.39,1.1,1.46,1.46,0,0,0,1.07.4,2.34,2.34,0,0,0,.88-.16,1,1,0,0,0,.54-.47h1.3a2.29,2.29,0,0,1-.35.72,2.23,2.23,0,0,1-.6.54,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.74,2.74,0,0,1-.2-1.06Zm1.31.25H33v-.25a1.35,1.35,0,0,0-1.47-1.46,1.34,1.34,0,0,0-1.46,1.46Z" style="fill:#139c5a"/><path d="M38,141l-2.23-3.2h1.53l1.23,1.86a1.05,1.05,0,0,1,.15.3.43.43,0,0,1,0,.13h.06a.43.43,0,0,1,0-.13,1,1,0,0,1,.15-.3l1.24-1.86H41.7l-2.22,3.19,2.37,3.41H40.31l-1.33-2a.59.59,0,0,1-.1-.17l-.07-.14-.06-.14h-.07s0,.1-.06.15l-.06.14a1.4,1.4,0,0,1-.1.16l-1.35,2H35.58Z" style="fill:#139c5a"/><rect x="46.28" y="6.23" width="86.81" height="119.12" style="fill:none;stroke:#139c5a;stroke-miterlimit:10;stroke-width:0.5px"/><path d="M135.05,144.47a3.23,3.23,0,0,1-1.15-.19,2.52,2.52,0,0,1-.87-.52,2.36,2.36,0,0,1-.55-.83,2.6,2.6,0,0,1-.2-1.06v-1.65a2.65,2.65,0,0,1,.2-1.07,2.32,2.32,0,0,1,.55-.82,2.54,2.54,0,0,1,.87-.53,3.76,3.76,0,0,1,2.3,0,2.38,2.38,0,0,1,.87.52,2.32,2.32,0,0,1,.55.82,2.59,2.59,0,0,1,.2,1.07v1.66a2.83,2.83,0,0,1-.19,1.07,2.24,2.24,0,0,1-.55.82,2.34,2.34,0,0,1-.88.52A3.23,3.23,0,0,1,135.05,144.47Zm-1.45-2.6a1.5,1.5,0,0,0,2.51,1,1.4,1.4,0,0,0,.39-1v-1.65a1.39,1.39,0,0,0-.39-1.05,1.64,1.64,0,0,0-2.12,0,1.39,1.39,0,0,0-.39,1.05Z" style="fill:#139c5a"/><path d="M139.61,137.75h1.26v1.14h.1a1.46,1.46,0,0,1,.6-.93,2.07,2.07,0,0,1,1.2-.33,2.32,2.32,0,0,1,.92.18,1.85,1.85,0,0,1,.71.52,2.29,2.29,0,0,1,.46.81,3.21,3.21,0,0,1,.16,1.07v1.66a3.31,3.31,0,0,1-.16,1.08,2.25,2.25,0,0,1-.46.82,1.85,1.85,0,0,1-.71.52,2.32,2.32,0,0,1-.92.18,2.07,2.07,0,0,1-1.2-.33,1.46,1.46,0,0,1-.6-.93h-.1a1,1,0,0,0,0,.13,2.33,2.33,0,0,1,0,.29c0,.11,0,.23,0,.36s0,.25,0,.36v2h-1.32Zm1.32,2.48v1.64a1.45,1.45,0,0,0,.37,1,1.25,1.25,0,0,0,1,.4,1.33,1.33,0,0,0,1-.4,1.45,1.45,0,0,0,.38-1.07v-1.62a1.45,1.45,0,0,0-.38-1.07,1.36,1.36,0,0,0-1-.39,1.28,1.28,0,0,0-1,.4A1.47,1.47,0,0,0,140.93,140.23Z" style="fill:#139c5a"/><path d="M146.68,140.19a2.8,2.8,0,0,1,.19-1.07,2.24,2.24,0,0,1,.55-.8,2.46,2.46,0,0,1,.88-.51,3.43,3.43,0,0,1,1.15-.18,3.47,3.47,0,0,1,1.15.18,2.6,2.6,0,0,1,.87.51,2.24,2.24,0,0,1,.55.8,3,3,0,0,1,.19,1.07v1.21H148v.47a1.46,1.46,0,0,0,.4,1.1,1.44,1.44,0,0,0,1.07.4,2.29,2.29,0,0,0,.87-.16,1,1,0,0,0,.54-.47h1.3a2.1,2.1,0,0,1-.35.72,2.35,2.35,0,0,1-.59.54,2.9,2.9,0,0,1-.8.35,3.85,3.85,0,0,1-1,.12,3.43,3.43,0,0,1-1.15-.18,2.38,2.38,0,0,1-.87-.52,2.2,2.2,0,0,1-.55-.81,2.57,2.57,0,0,1-.2-1.06Zm1.3.25h2.93v-.25a1.52,1.52,0,0,0-2.54-1.07,1.42,1.42,0,0,0-.39,1.07Z" style="fill:#139c5a"/><path d="M155.52,137.75v1.14h.11a1.45,1.45,0,0,1,.62-.93,2.11,2.11,0,0,1,1.22-.33,2.15,2.15,0,0,1,1.65.65,2.61,2.61,0,0,1,.6,1.82v.43h-1.35v-.3a1.5,1.5,0,0,0-.38-1.08,1.32,1.32,0,0,0-1-.39,1.3,1.3,0,0,0-1,.39,1.53,1.53,0,0,0-.38,1.08v4.12h-1.32v-6.6Z" style="fill:#139c5a"/><path d="M160.92,142.47a1.9,1.9,0,0,1,.67-1.53,2.71,2.71,0,0,1,1.8-.56h1.79v-.55a1,1,0,0,0-.36-.83,1.67,1.67,0,0,0-1-.29,1.82,1.82,0,0,0-.88.19.9.9,0,0,0-.48.52h-1.29a1.94,1.94,0,0,1,.87-1.31,3.17,3.17,0,0,1,1.79-.48,3.1,3.1,0,0,1,2,.58,1.91,1.91,0,0,1,.72,1.58v4.56h-1.22v-1.28h-.1a1.56,1.56,0,0,1-.7,1,2.51,2.51,0,0,1-1.41.38,2.18,2.18,0,0,1-1.57-.55A1.92,1.92,0,0,1,160.92,142.47Zm1.32-.11a1,1,0,0,0,.33.78,1.37,1.37,0,0,0,.91.27,2.43,2.43,0,0,0,.68-.09,2,2,0,0,0,.54-.27,1.4,1.4,0,0,0,.36-.41,1.13,1.13,0,0,0,.12-.51v-.83h-1.77a1.24,1.24,0,0,0-.86.28A1,1,0,0,0,162.24,142.36Z" style="fill:#139c5a"/><path d="M168.07,137.75h1.86v-2h1.32v2h2.53v1.19h-2.53v3.51a.69.69,0,0,0,.19.51.73.73,0,0,0,.54.19h1.68v1.2h-1.74a2,2,0,0,1-1.45-.52,1.81,1.81,0,0,1-.54-1.38v-3.51h-1.86Z" style="fill:#139c5a"/><path d="M175.72,143.15h2.37v-4.21H176v-1.19h3.36v5.4h1.93v1.2h-5.59Zm1.89-7.21a.77.77,0,0,1,.22-.56.84.84,0,0,1,.6-.21h.29a.84.84,0,0,1,.59.21.73.73,0,0,1,.22.56.71.71,0,0,1-.22.55.85.85,0,0,1-.61.2h-.27a.84.84,0,0,1-.6-.2A.75.75,0,0,1,177.61,135.94Z" style="fill:#139c5a"/><path d="M185.45,144.47a3.19,3.19,0,0,1-1.15-.19,2.52,2.52,0,0,1-.87-.52,2.36,2.36,0,0,1-.55-.83,2.6,2.6,0,0,1-.2-1.06v-1.65a2.65,2.65,0,0,1,.2-1.07,2.32,2.32,0,0,1,.55-.82,2.54,2.54,0,0,1,.87-.53,3.43,3.43,0,0,1,1.15-.18,3.47,3.47,0,0,1,1.15.18,2.46,2.46,0,0,1,.87.52,2.32,2.32,0,0,1,.55.82,2.76,2.76,0,0,1,.2,1.07v1.66a2.83,2.83,0,0,1-.19,1.07,2.24,2.24,0,0,1-.55.82,2.42,2.42,0,0,1-.88.52A3.23,3.23,0,0,1,185.45,144.47Zm-1.45-2.6a1.4,1.4,0,0,0,.38,1,1.65,1.65,0,0,0,2.13,0,1.4,1.4,0,0,0,.39-1v-1.65a1.39,1.39,0,0,0-.39-1.05,1.64,1.64,0,0,0-2.12,0,1.39,1.39,0,0,0-.39,1.05Z" style="fill:#139c5a"/><path d="M190,137.75h1.24v1.14h.11a1.52,1.52,0,0,1,.59-.94,2,2,0,0,1,1.18-.32,2.34,2.34,0,0,1,.91.17,2,2,0,0,1,.69.49,2,2,0,0,1,.42.75,3,3,0,0,1,.15,1v4.34H194v-4.16a1.5,1.5,0,0,0-.35-1.06,1.24,1.24,0,0,0-1-.37,1.26,1.26,0,0,0-1,.39,1.57,1.57,0,0,0-.36,1.08v4.12H190Z" style="fill:#139c5a"/><path d="M61.67,140.17a3.51,3.51,0,0,1,.15-1,2.33,2.33,0,0,1,.46-.8,2.05,2.05,0,0,1,.72-.51,2.34,2.34,0,0,1,.93-.18,2.1,2.1,0,0,1,1.23.35,1.56,1.56,0,0,1,.63,1h.09v-1.2h1.25V144a2.3,2.3,0,0,1-.72,1.79,2.83,2.83,0,0,1-2,.65h-1.7v-1.13h1.7a1.36,1.36,0,0,0,1-.35,1.27,1.27,0,0,0,.37-1v-.22l.05-1.2h-.08a1.61,1.61,0,0,1-.65,1,2,2,0,0,1-1.22.35,2.34,2.34,0,0,1-.93-.18,2.11,2.11,0,0,1-.71-.51,2.31,2.31,0,0,1-.45-.8,3.13,3.13,0,0,1-.16-1.05ZM63,141.29a1.31,1.31,0,0,0,1.4,1.42,1.31,1.31,0,0,0,1.42-1.42v-1.1a1.31,1.31,0,0,0-1.42-1.42,1.31,1.31,0,0,0-1.4,1.42Z" style="fill:#139c5a"/><path d="M68.91,140.19a3,3,0,0,1,.19-1.07,2.27,2.27,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.12,2.12,0,0,1,.55.8,2.79,2.79,0,0,1,.2,1.07v1.21H70.22v.47a1.49,1.49,0,0,0,.39,1.1,1.46,1.46,0,0,0,1.07.4,2.34,2.34,0,0,0,.88-.16,1,1,0,0,0,.54-.47h1.3a2.1,2.1,0,0,1-.35.72,2.35,2.35,0,0,1-.59.54,3,3,0,0,1-.81.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.74,2.74,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.47,1.47,0,1,0-2.93,0Z" style="fill:#139c5a"/><path d="M78.88,144.47a3.17,3.17,0,0,1-1.14-.19,2.52,2.52,0,0,1-.87-.52,2.39,2.39,0,0,1-.56-.83,2.78,2.78,0,0,1-.2-1.06v-1.65a2.83,2.83,0,0,1,.2-1.07,2.35,2.35,0,0,1,.56-.82,2.54,2.54,0,0,1,.87-.53,3.73,3.73,0,0,1,2.29,0,2.38,2.38,0,0,1,.87.52,2.35,2.35,0,0,1,.56.82,2.76,2.76,0,0,1,.2,1.07v1.66a2.83,2.83,0,0,1-.2,1.07,2.12,2.12,0,0,1-.55.82,2.38,2.38,0,0,1-.87.52A3.28,3.28,0,0,1,78.88,144.47Zm-1.45-2.6a1.35,1.35,0,0,0,1.45,1.44,1.45,1.45,0,0,0,1.07-.39,1.4,1.4,0,0,0,.39-1v-1.65a1.39,1.39,0,0,0-.39-1.05,1.65,1.65,0,0,0-2.13,0,1.39,1.39,0,0,0-.39,1.05Z" style="fill:#139c5a"/><path d="M83.14,144.35v-6.6h1.1v1h.09a1.24,1.24,0,0,1,.32-.8,1,1,0,0,1,.76-.29.91.91,0,0,1,.72.28,1.62,1.62,0,0,1,.39.81h.08a1.15,1.15,0,0,1,.33-.8,1,1,0,0,1,.77-.29,1.19,1.19,0,0,1,1,.45,1.93,1.93,0,0,1,.37,1.24v5H87.82v-5a1,1,0,0,0-.15-.59.57.57,0,0,0-.46-.2q-.63,0-.63.84v4.92h-1v-5a.94.94,0,0,0-.16-.59.63.63,0,0,0-.48-.2q-.63,0-.63.84v4.92Z" style="fill:#139c5a"/><path d="M90.51,140.19a3,3,0,0,1,.19-1.07,2.27,2.27,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.12,2.12,0,0,1,.55.8,2.79,2.79,0,0,1,.2,1.07v1.21H91.82v.47a1.49,1.49,0,0,0,.39,1.1,1.46,1.46,0,0,0,1.07.4,2.34,2.34,0,0,0,.88-.16,1,1,0,0,0,.54-.47H96a2.1,2.1,0,0,1-.35.72,2.35,2.35,0,0,1-.59.54,3,3,0,0,1-.81.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.74,2.74,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.35,1.35,0,0,0-1.47-1.46,1.34,1.34,0,0,0-1.46,1.46Z" style="fill:#139c5a"/><path d="M97.51,137.75h1.86v-2h1.32v2h2.53v1.19h-2.53v3.51a.66.66,0,0,0,.19.51.73.73,0,0,0,.54.19h1.68v1.2h-1.74a2,2,0,0,1-1.46-.52,1.84,1.84,0,0,1-.53-1.38v-3.51H97.51Z" style="fill:#139c5a"/><path d="M106.56,137.75v1.14h.1a1.48,1.48,0,0,1,.62-.93,2.14,2.14,0,0,1,1.22-.33,2.17,2.17,0,0,1,1.66.65,2.61,2.61,0,0,1,.6,1.82v.43H109.4v-.3a1.49,1.49,0,0,0-.37-1.08,1.35,1.35,0,0,0-1-.39,1.33,1.33,0,0,0-1,.39,1.52,1.52,0,0,0-.37,1.08v4.12h-1.32v-6.6Z" style="fill:#139c5a"/><path d="M111.79,137.75h1.44l1.39,3.64a4.26,4.26,0,0,1,.15.47,4.31,4.31,0,0,1,.09.44l.06.43h.1a3.28,3.28,0,0,1,0-.43c0-.13.06-.27.09-.43s.09-.32.14-.48l1.3-3.64H118l-3.17,8.64h-1.39l.91-2.46Z" style="fill:#139c5a"/></svg>⏎ |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="255.6mm" height="57.81mm" viewBox="0 0 724.53 163.88"><rect x="11" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="11" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="11" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="11" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="11" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="53.8" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="53.8" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="200.25" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="200.25" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="200.25" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="200.25" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="200.25" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="243.05" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="243.05" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><line x1="144.24" y1="38.14" x2="187.07" y2="54.66" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="183.91 58.79 193.76 57.25 187.5 49.49 183.91 58.79" style="fill:#1d1d1b"/><line x1="144.24" y1="57.25" x2="188.08" y2="91.07" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="183.88 94.12 193.76 95.45 189.97 86.23 183.88 94.12" style="fill:#1d1d1b"/><line x1="144.24" y1="76.35" x2="188.08" y2="110.17" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="183.88 113.22 193.76 114.55 189.97 105.33 183.88 113.22" style="fill:#1d1d1b"/><line x1="144.24" y1="95.45" x2="189.07" y2="43.57" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="191.89 47.94 193.76 38.15 184.34 41.42 191.89 47.94" style="fill:#1d1d1b"/><line x1="144.24" y1="114.55" x2="188.08" y2="80.73" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="189.97 85.57 193.76 76.35 183.88 77.67 189.97 85.57" style="fill:#1d1d1b"/><line x1="533.72" y1="38.14" x2="576.06" y2="38.14" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="574.6 43.13 583.23 38.15 574.6 33.16 574.6 43.13" style="fill:#1d1d1b"/><line x1="533.72" y1="57.25" x2="576.06" y2="57.25" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="574.6 62.23 583.23 57.25 574.6 52.26 574.6 62.23" style="fill:#1d1d1b"/><line x1="533.72" y1="76.35" x2="576.06" y2="76.35" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="574.6 81.33 583.23 76.35 574.6 71.36 574.6 81.33" style="fill:#1d1d1b"/><line x1="533.72" y1="95.45" x2="576.06" y2="95.45" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="574.6 100.44 583.23 95.45 574.6 90.46 574.6 100.44" style="fill:#1d1d1b"/><line x1="533.72" y1="114.55" x2="576.06" y2="114.55" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="574.6 119.54 583.23 114.55 574.6 109.56 574.6 119.54" style="fill:#1d1d1b"/><rect x="400.47" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="400.47" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="400.47" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="400.47" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="400.47" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="443.27" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="443.27" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="589.72" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="589.72" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="589.72" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="589.72" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="589.72" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="632.52" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="632.52" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><path d="M130.89,145.13a1.9,1.9,0,0,1,.66-1.52,2.71,2.71,0,0,1,1.81-.57h1.79v-.55a1,1,0,0,0-.37-.82,1.59,1.59,0,0,0-1-.29,1.93,1.93,0,0,0-.87.18,1,1,0,0,0-.48.53h-1.3a1.93,1.93,0,0,1,.88-1.31,3.65,3.65,0,0,1,3.77.09,2,2,0,0,1,.73,1.59V147h-1.23v-1.29h-.09a1.57,1.57,0,0,1-.71,1,2.51,2.51,0,0,1-1.41.38,2.17,2.17,0,0,1-1.56-.55A1.89,1.89,0,0,1,130.89,145.13Zm1.32-.11a.94.94,0,0,0,.32.78,1.34,1.34,0,0,0,.91.28,2.17,2.17,0,0,0,.69-.1,1.71,1.71,0,0,0,.54-.27,1.21,1.21,0,0,0,.35-.4,1.14,1.14,0,0,0,.13-.51V144h-1.78a1.21,1.21,0,0,0-.85.28A1,1,0,0,0,132.21,145Z" style="fill:#1d1d1b"/><path d="M137.83,139.49v-1.2h3.46V145a.8.8,0,0,0,.22.61.78.78,0,0,0,.6.23h2V147h-2a2.07,2.07,0,0,1-1.53-.55A2,2,0,0,1,140,145v-5.49Z" style="fill:#1d1d1b"/><path d="M145.68,145.82h2.38V141.6H146v-1.18h3.36v5.4h1.93V147h-5.59Zm1.9-7.22a.71.71,0,0,1,.22-.55.84.84,0,0,1,.59-.21h.29a.8.8,0,0,1,.59.21.78.78,0,0,1,0,1.11.87.87,0,0,1-.6.2h-.28a.88.88,0,0,1-.59-.2A.72.72,0,0,1,147.58,138.6Z" style="fill:#1d1d1b"/><path d="M152.59,142.84a3.5,3.5,0,0,1,.16-1.05,2.22,2.22,0,0,1,.45-.8,2.09,2.09,0,0,1,.73-.51,2.3,2.3,0,0,1,.93-.18,2.1,2.1,0,0,1,1.23.34,1.65,1.65,0,0,1,.63,1h.09v-1.2h1.24v6.2a2.3,2.3,0,0,1-.71,1.78,2.78,2.78,0,0,1-2,.66h-1.71v-1.13h1.71a1.4,1.4,0,0,0,1-.35,1.27,1.27,0,0,0,.36-1v-.23l0-1.2h-.07a1.64,1.64,0,0,1-.65,1,2.07,2.07,0,0,1-1.22.34,2.3,2.3,0,0,1-.93-.18,2,2,0,0,1-.71-.51,2.22,2.22,0,0,1-.45-.8,3.06,3.06,0,0,1-.17-1Zm1.34,1.12a1.41,1.41,0,1,0,2.81,0v-1.11a1.41,1.41,0,1,0-2.81,0Z" style="fill:#1d1d1b"/><path d="M160,140.42h1.25v1.14h.11a1.47,1.47,0,0,1,.58-.94,2.05,2.05,0,0,1,1.18-.32,2.22,2.22,0,0,1,.92.17,1.89,1.89,0,0,1,.68.49,2.13,2.13,0,0,1,.43.75,2.93,2.93,0,0,1,.14,1V147h-1.32v-4.17a1.48,1.48,0,0,0-.34-1,1.27,1.27,0,0,0-1-.38,1.29,1.29,0,0,0-1,.4,1.57,1.57,0,0,0-.36,1.08V147H160Z" style="fill:#1d1d1b"/><path d="M172.45,141v1.2h-5.28V141Zm-5.28,2.93h5.28v1.2h-5.28Z" style="fill:#1d1d1b"/><path d="M173.92,139.56v-1.18h6.19v1.18h-2.42V147h-1.35v-7.46Z" style="fill:#1d1d1b"/><path d="M183.08,140.42v1.14h.11a1.48,1.48,0,0,1,.62-.93,2.43,2.43,0,0,1,2.88.32,2.64,2.64,0,0,1,.59,1.82v.43h-1.35v-.3a1.58,1.58,0,0,0-.37-1.09,1.35,1.35,0,0,0-1-.39,1.33,1.33,0,0,0-1,.4,1.52,1.52,0,0,0-.37,1.08V147h-1.32v-6.6Z" style="fill:#1d1d1b"/><path d="M190.12,140.42v4.18c0,.93.42,1.38,1.28,1.38s1.31-.45,1.31-1.38v-4.18H194v4.18a2.48,2.48,0,0,1-.69,1.88,2.67,2.67,0,0,1-1.94.66,2.63,2.63,0,0,1-1.92-.67,2.46,2.46,0,0,1-.68-1.87v-4.18Z" style="fill:#1d1d1b"/><path d="M195.84,142.85a2.78,2.78,0,0,1,.19-1.06,2.16,2.16,0,0,1,.55-.8,2.55,2.55,0,0,1,.88-.51,3.47,3.47,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.29a2,2,0,0,1-.34.71,2.11,2.11,0,0,1-.6.55,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.41,1.41,0,0,0-.39-1.06,1.43,1.43,0,0,0-1.08-.39,1.33,1.33,0,0,0-1.46,1.45Z" style="fill:#1d1d1b"/><path d="M519.27,145.13a1.88,1.88,0,0,1,.67-1.52,2.66,2.66,0,0,1,1.8-.57h1.79v-.55a1,1,0,0,0-.36-.82,1.61,1.61,0,0,0-1-.29,1.94,1.94,0,0,0-.88.18,1,1,0,0,0-.48.53h-1.29a1.92,1.92,0,0,1,.87-1.31,3.67,3.67,0,0,1,3.78.09,1.94,1.94,0,0,1,.72,1.59V147h-1.22v-1.29h-.1a1.56,1.56,0,0,1-.7,1,2.51,2.51,0,0,1-1.41.38,2.15,2.15,0,0,1-1.56-.55A1.9,1.9,0,0,1,519.27,145.13Zm1.32-.11a1,1,0,0,0,.33.78,1.32,1.32,0,0,0,.91.28,2.07,2.07,0,0,0,.68-.1,1.52,1.52,0,0,0,.54-.27,1.37,1.37,0,0,0,.36-.4,1.13,1.13,0,0,0,.12-.51V144h-1.77a1.24,1.24,0,0,0-.86.28A1,1,0,0,0,520.59,145Z" style="fill:#1d1d1b"/><path d="M526.22,139.49v-1.2h3.46V145a.84.84,0,0,0,.21.61.81.81,0,0,0,.6.23h2V147h-2a2.06,2.06,0,0,1-1.52-.55,2,2,0,0,1-.55-1.49v-5.49Z" style="fill:#1d1d1b"/><path d="M534.07,145.82h2.37V141.6h-2.07v-1.18h3.36v5.4h1.93V147h-5.59ZM536,138.6a.72.72,0,0,1,.23-.55.8.8,0,0,1,.59-.21h.29a.82.82,0,0,1,.59.21.71.71,0,0,1,.22.55.72.72,0,0,1-.22.56.89.89,0,0,1-.6.2h-.28a.84.84,0,0,1-.59-.2A.72.72,0,0,1,536,138.6Z" style="fill:#1d1d1b"/><path d="M541,142.84a3.21,3.21,0,0,1,.16-1.05,2.09,2.09,0,0,1,.45-.8,2.05,2.05,0,0,1,.72-.51,2.35,2.35,0,0,1,.94-.18,2,2,0,0,1,1.22.34,1.62,1.62,0,0,1,.64,1h.08v-1.2h1.25v6.2a2.3,2.3,0,0,1-.71,1.78,2.78,2.78,0,0,1-2,.66h-1.71v-1.13h1.71a1.42,1.42,0,0,0,1-.35,1.27,1.27,0,0,0,.36-1v-.23l0-1.2h-.07a1.68,1.68,0,0,1-.65,1,2.09,2.09,0,0,1-1.22.34,2.27,2.27,0,0,1-.93-.18,1.93,1.93,0,0,1-.71-.51,2.24,2.24,0,0,1-.46-.8,3.07,3.07,0,0,1-.16-1Zm1.33,1.12a1.43,1.43,0,0,0,.38,1,1.41,1.41,0,0,0,2.44-1v-1.11a1.47,1.47,0,0,0-2.44-1,1.43,1.43,0,0,0-.38,1Z" style="fill:#1d1d1b"/><path d="M548.35,140.42h1.25v1.14h.1a1.52,1.52,0,0,1,.59-.94,2.05,2.05,0,0,1,1.18-.32,2.25,2.25,0,0,1,.92.17,2.08,2.08,0,0,1,.68.49,2.13,2.13,0,0,1,.43.75,3.22,3.22,0,0,1,.14,1V147h-1.32v-4.17a1.48,1.48,0,0,0-.35-1,1.24,1.24,0,0,0-1-.38,1.28,1.28,0,0,0-1,.4,1.57,1.57,0,0,0-.36,1.08V147h-1.32Z" style="fill:#1d1d1b"/><path d="M560.84,141v1.2h-5.28V141Zm-5.28,2.93h5.28v1.2h-5.28Z" style="fill:#1d1d1b"/><path d="M562.84,138.37h5.32v1.19h-4V142h3.71v1.2h-3.69V147h-1.32Z" style="fill:#1d1d1b"/><path d="M569.67,145.13a1.88,1.88,0,0,1,.67-1.52,2.66,2.66,0,0,1,1.8-.57h1.79v-.55a1,1,0,0,0-.36-.82,1.61,1.61,0,0,0-1-.29,1.94,1.94,0,0,0-.88.18,1,1,0,0,0-.48.53h-1.29a1.92,1.92,0,0,1,.87-1.31,3.67,3.67,0,0,1,3.78.09,1.94,1.94,0,0,1,.72,1.59V147H574v-1.29h-.1a1.56,1.56,0,0,1-.7,1,2.51,2.51,0,0,1-1.41.38,2.18,2.18,0,0,1-1.57-.55A1.93,1.93,0,0,1,569.67,145.13ZM571,145a1,1,0,0,0,.33.78,1.32,1.32,0,0,0,.91.28,2.11,2.11,0,0,0,.68-.1,1.52,1.52,0,0,0,.54-.27,1.37,1.37,0,0,0,.36-.4,1.13,1.13,0,0,0,.12-.51V144h-1.77a1.24,1.24,0,0,0-.86.28A1,1,0,0,0,571,145Z" style="fill:#1d1d1b"/><path d="M576.62,139.49v-1.2h3.46V145a.84.84,0,0,0,.21.61.81.81,0,0,0,.6.23h2V147h-2a2,2,0,0,1-1.52-.55,2,2,0,0,1-.55-1.49v-5.49Z" style="fill:#1d1d1b"/><path d="M584.19,145.36h1.37a.79.79,0,0,0,.4.52,1.67,1.67,0,0,0,.81.19h.43a1.53,1.53,0,0,0,.92-.24.77.77,0,0,0,.33-.66q0-.72-1.05-.87l-1-.13a2.75,2.75,0,0,1-1.58-.62,1.72,1.72,0,0,1-.51-1.33,1.7,1.7,0,0,1,.64-1.42,2.89,2.89,0,0,1,1.83-.5h.42a2.87,2.87,0,0,1,1.7.46,1.82,1.82,0,0,1,.77,1.25h-1.35a.76.76,0,0,0-.38-.47,1.4,1.4,0,0,0-.74-.17h-.42a1.4,1.4,0,0,0-.86.21.7.7,0,0,0-.3.63.67.67,0,0,0,.22.54,1.39,1.39,0,0,0,.71.25l1,.13a3.08,3.08,0,0,1,1.65.63,2,2,0,0,1-.13,2.86,3,3,0,0,1-1.9.52h-.43a3.08,3.08,0,0,1-1.79-.48A1.77,1.77,0,0,1,584.19,145.36Z" style="fill:#1d1d1b"/><path d="M591.43,142.85a2.78,2.78,0,0,1,.19-1.06,2.16,2.16,0,0,1,.55-.8,2.46,2.46,0,0,1,.88-.51,3.76,3.76,0,0,1,2.3,0,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.44,1.44,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.14,1.14,0,0,0,.54-.47h1.29a2.22,2.22,0,0,1-.94,1.26,2.9,2.9,0,0,1-.8.35,3.79,3.79,0,0,1-1,.12,3.47,3.47,0,0,1-1.15-.18,2.38,2.38,0,0,1-.87-.52,2.2,2.2,0,0,1-.55-.81,2.6,2.6,0,0,1-.2-1.06Zm1.31.25h2.92v-.25a1.41,1.41,0,0,0-.38-1.06,1.69,1.69,0,0,0-2.16,0,1.41,1.41,0,0,0-.38,1.06Z" style="fill:#1d1d1b"/></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="255.6mm" height="57.81mm" viewBox="0 0 724.53 163.88"><rect x="11" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="11" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="11" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="11" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="11" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="53.8" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="53.8" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="200.25" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="200.25" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="200.25" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="200.25" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="200.25" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="243.05" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="243.05" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="243.05" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><line x1="144.24" y1="38.14" x2="187.07" y2="54.66" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="183.91 58.79 193.76 57.25 187.5 49.49 183.91 58.79" style="fill:#139c5a"/><line x1="144.24" y1="57.25" x2="188.08" y2="91.07" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="183.88 94.12 193.76 95.45 189.97 86.23 183.88 94.12" style="fill:#139c5a"/><line x1="144.24" y1="76.35" x2="188.08" y2="110.17" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="183.88 113.22 193.76 114.55 189.97 105.33 183.88 113.22" style="fill:#139c5a"/><line x1="144.24" y1="95.45" x2="189.07" y2="43.57" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="191.89 47.94 193.76 38.15 184.34 41.42 191.89 47.94" style="fill:#139c5a"/><line x1="144.24" y1="114.55" x2="188.08" y2="80.73" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="189.97 85.57 193.76 76.35 183.88 77.67 189.97 85.57" style="fill:#139c5a"/><line x1="533.72" y1="38.14" x2="576.06" y2="38.14" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="574.6 43.13 583.23 38.15 574.6 33.16 574.6 43.13" style="fill:#139c5a"/><line x1="533.72" y1="57.25" x2="576.06" y2="57.25" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="574.6 62.23 583.23 57.25 574.6 52.26 574.6 62.23" style="fill:#139c5a"/><line x1="533.72" y1="76.35" x2="576.06" y2="76.35" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="574.6 81.33 583.23 76.35 574.6 71.36 574.6 81.33" style="fill:#139c5a"/><line x1="533.72" y1="95.45" x2="576.06" y2="95.45" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="574.6 100.44 583.23 95.45 574.6 90.46 574.6 100.44" style="fill:#139c5a"/><line x1="533.72" y1="114.55" x2="576.06" y2="114.55" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="574.6 119.54 583.23 114.55 574.6 109.56 574.6 119.54" style="fill:#139c5a"/><rect x="400.47" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="400.47" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="400.47" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="400.47" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="400.47" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="443.27" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="443.27" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="443.27" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="589.72" y="28.59" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="589.72" y="47.7" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="589.72" y="66.8" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="589.72" y="85.9" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="589.72" y="105" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="632.52" y="28.59" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="9.49" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="632.52" y="47.7" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="66.8" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="85.9" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="632.52" y="105" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><path d="M130.89,145.13a1.9,1.9,0,0,1,.66-1.52,2.71,2.71,0,0,1,1.81-.57h1.79v-.55a1,1,0,0,0-.37-.82,1.59,1.59,0,0,0-1-.29,1.93,1.93,0,0,0-.87.18,1,1,0,0,0-.48.53h-1.3a1.93,1.93,0,0,1,.88-1.31,3.65,3.65,0,0,1,3.77.09,2,2,0,0,1,.73,1.59V147h-1.23v-1.29h-.09a1.57,1.57,0,0,1-.71,1,2.51,2.51,0,0,1-1.41.38,2.17,2.17,0,0,1-1.56-.55A1.89,1.89,0,0,1,130.89,145.13Zm1.32-.11a.94.94,0,0,0,.32.78,1.34,1.34,0,0,0,.91.28,2.17,2.17,0,0,0,.69-.1,1.71,1.71,0,0,0,.54-.27,1.21,1.21,0,0,0,.35-.4,1.14,1.14,0,0,0,.13-.51V144h-1.78a1.21,1.21,0,0,0-.85.28A1,1,0,0,0,132.21,145Z" style="fill:#139c5a"/><path d="M137.83,139.49v-1.2h3.46V145a.8.8,0,0,0,.22.61.78.78,0,0,0,.6.23h2V147h-2a2.07,2.07,0,0,1-1.53-.55A2,2,0,0,1,140,145v-5.49Z" style="fill:#139c5a"/><path d="M145.68,145.82h2.38V141.6H146v-1.18h3.36v5.4h1.93V147h-5.59Zm1.9-7.22a.71.71,0,0,1,.22-.55.84.84,0,0,1,.59-.21h.29a.8.8,0,0,1,.59.21.78.78,0,0,1,0,1.11.87.87,0,0,1-.6.2h-.28a.88.88,0,0,1-.59-.2A.72.72,0,0,1,147.58,138.6Z" style="fill:#139c5a"/><path d="M152.59,142.84a3.5,3.5,0,0,1,.16-1.05,2.22,2.22,0,0,1,.45-.8,2.09,2.09,0,0,1,.73-.51,2.3,2.3,0,0,1,.93-.18,2.1,2.1,0,0,1,1.23.34,1.65,1.65,0,0,1,.63,1h.09v-1.2h1.24v6.2a2.3,2.3,0,0,1-.71,1.78,2.78,2.78,0,0,1-2,.66h-1.71v-1.13h1.71a1.4,1.4,0,0,0,1-.35,1.27,1.27,0,0,0,.36-1v-.23l0-1.2h-.07a1.64,1.64,0,0,1-.65,1,2.07,2.07,0,0,1-1.22.34,2.3,2.3,0,0,1-.93-.18,2,2,0,0,1-.71-.51,2.22,2.22,0,0,1-.45-.8,3.06,3.06,0,0,1-.17-1Zm1.34,1.12a1.41,1.41,0,1,0,2.81,0v-1.11a1.41,1.41,0,1,0-2.81,0Z" style="fill:#139c5a"/><path d="M160,140.42h1.25v1.14h.11a1.47,1.47,0,0,1,.58-.94,2.05,2.05,0,0,1,1.18-.32,2.22,2.22,0,0,1,.92.17,1.89,1.89,0,0,1,.68.49,2.13,2.13,0,0,1,.43.75,2.93,2.93,0,0,1,.14,1V147h-1.32v-4.17a1.48,1.48,0,0,0-.34-1,1.27,1.27,0,0,0-1-.38,1.29,1.29,0,0,0-1,.4,1.57,1.57,0,0,0-.36,1.08V147H160Z" style="fill:#139c5a"/><path d="M172.45,141v1.2h-5.28V141Zm-5.28,2.93h5.28v1.2h-5.28Z" style="fill:#139c5a"/><path d="M173.92,139.56v-1.18h6.19v1.18h-2.42V147h-1.35v-7.46Z" style="fill:#139c5a"/><path d="M183.08,140.42v1.14h.11a1.48,1.48,0,0,1,.62-.93,2.43,2.43,0,0,1,2.88.32,2.64,2.64,0,0,1,.59,1.82v.43h-1.35v-.3a1.58,1.58,0,0,0-.37-1.09,1.35,1.35,0,0,0-1-.39,1.33,1.33,0,0,0-1,.4,1.52,1.52,0,0,0-.37,1.08V147h-1.32v-6.6Z" style="fill:#139c5a"/><path d="M190.12,140.42v4.18c0,.93.42,1.38,1.28,1.38s1.31-.45,1.31-1.38v-4.18H194v4.18a2.48,2.48,0,0,1-.69,1.88,2.67,2.67,0,0,1-1.94.66,2.63,2.63,0,0,1-1.92-.67,2.46,2.46,0,0,1-.68-1.87v-4.18Z" style="fill:#139c5a"/><path d="M195.84,142.85a2.78,2.78,0,0,1,.19-1.06,2.16,2.16,0,0,1,.55-.8,2.55,2.55,0,0,1,.88-.51,3.47,3.47,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.29a2,2,0,0,1-.34.71,2.11,2.11,0,0,1-.6.55,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.38,2.38,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.41,1.41,0,0,0-.39-1.06,1.43,1.43,0,0,0-1.08-.39,1.33,1.33,0,0,0-1.46,1.45Z" style="fill:#139c5a"/><path d="M519.27,145.13a1.88,1.88,0,0,1,.67-1.52,2.66,2.66,0,0,1,1.8-.57h1.79v-.55a1,1,0,0,0-.36-.82,1.61,1.61,0,0,0-1-.29,1.94,1.94,0,0,0-.88.18,1,1,0,0,0-.48.53h-1.29a1.92,1.92,0,0,1,.87-1.31,3.67,3.67,0,0,1,3.78.09,1.94,1.94,0,0,1,.72,1.59V147h-1.22v-1.29h-.1a1.56,1.56,0,0,1-.7,1,2.51,2.51,0,0,1-1.41.38,2.15,2.15,0,0,1-1.56-.55A1.9,1.9,0,0,1,519.27,145.13Zm1.32-.11a1,1,0,0,0,.33.78,1.32,1.32,0,0,0,.91.28,2.07,2.07,0,0,0,.68-.1,1.52,1.52,0,0,0,.54-.27,1.37,1.37,0,0,0,.36-.4,1.13,1.13,0,0,0,.12-.51V144h-1.77a1.24,1.24,0,0,0-.86.28A1,1,0,0,0,520.59,145Z" style="fill:#139c5a"/><path d="M526.22,139.49v-1.2h3.46V145a.84.84,0,0,0,.21.61.81.81,0,0,0,.6.23h2V147h-2a2.06,2.06,0,0,1-1.52-.55,2,2,0,0,1-.55-1.49v-5.49Z" style="fill:#139c5a"/><path d="M534.07,145.82h2.37V141.6h-2.07v-1.18h3.36v5.4h1.93V147h-5.59ZM536,138.6a.72.72,0,0,1,.23-.55.8.8,0,0,1,.59-.21h.29a.82.82,0,0,1,.59.21.71.71,0,0,1,.22.55.72.72,0,0,1-.22.56.89.89,0,0,1-.6.2h-.28a.84.84,0,0,1-.59-.2A.72.72,0,0,1,536,138.6Z" style="fill:#139c5a"/><path d="M541,142.84a3.21,3.21,0,0,1,.16-1.05,2.09,2.09,0,0,1,.45-.8,2.05,2.05,0,0,1,.72-.51,2.35,2.35,0,0,1,.94-.18,2,2,0,0,1,1.22.34,1.62,1.62,0,0,1,.64,1h.08v-1.2h1.25v6.2a2.3,2.3,0,0,1-.71,1.78,2.78,2.78,0,0,1-2,.66h-1.71v-1.13h1.71a1.42,1.42,0,0,0,1-.35,1.27,1.27,0,0,0,.36-1v-.23l0-1.2h-.07a1.68,1.68,0,0,1-.65,1,2.09,2.09,0,0,1-1.22.34,2.27,2.27,0,0,1-.93-.18,1.93,1.93,0,0,1-.71-.51,2.24,2.24,0,0,1-.46-.8,3.07,3.07,0,0,1-.16-1Zm1.33,1.12a1.43,1.43,0,0,0,.38,1,1.41,1.41,0,0,0,2.44-1v-1.11a1.47,1.47,0,0,0-2.44-1,1.43,1.43,0,0,0-.38,1Z" style="fill:#139c5a"/><path d="M548.35,140.42h1.25v1.14h.1a1.52,1.52,0,0,1,.59-.94,2.05,2.05,0,0,1,1.18-.32,2.25,2.25,0,0,1,.92.17,2.08,2.08,0,0,1,.68.49,2.13,2.13,0,0,1,.43.75,3.22,3.22,0,0,1,.14,1V147h-1.32v-4.17a1.48,1.48,0,0,0-.35-1,1.24,1.24,0,0,0-1-.38,1.28,1.28,0,0,0-1,.4,1.57,1.57,0,0,0-.36,1.08V147h-1.32Z" style="fill:#139c5a"/><path d="M560.84,141v1.2h-5.28V141Zm-5.28,2.93h5.28v1.2h-5.28Z" style="fill:#139c5a"/><path d="M562.84,138.37h5.32v1.19h-4V142h3.71v1.2h-3.69V147h-1.32Z" style="fill:#139c5a"/><path d="M569.67,145.13a1.88,1.88,0,0,1,.67-1.52,2.66,2.66,0,0,1,1.8-.57h1.79v-.55a1,1,0,0,0-.36-.82,1.61,1.61,0,0,0-1-.29,1.94,1.94,0,0,0-.88.18,1,1,0,0,0-.48.53h-1.29a1.92,1.92,0,0,1,.87-1.31,3.67,3.67,0,0,1,3.78.09,1.94,1.94,0,0,1,.72,1.59V147H574v-1.29h-.1a1.56,1.56,0,0,1-.7,1,2.51,2.51,0,0,1-1.41.38,2.18,2.18,0,0,1-1.57-.55A1.93,1.93,0,0,1,569.67,145.13ZM571,145a1,1,0,0,0,.33.78,1.32,1.32,0,0,0,.91.28,2.11,2.11,0,0,0,.68-.1,1.52,1.52,0,0,0,.54-.27,1.37,1.37,0,0,0,.36-.4,1.13,1.13,0,0,0,.12-.51V144h-1.77a1.24,1.24,0,0,0-.86.28A1,1,0,0,0,571,145Z" style="fill:#139c5a"/><path d="M576.62,139.49v-1.2h3.46V145a.84.84,0,0,0,.21.61.81.81,0,0,0,.6.23h2V147h-2a2,2,0,0,1-1.52-.55,2,2,0,0,1-.55-1.49v-5.49Z" style="fill:#139c5a"/><path d="M584.19,145.36h1.37a.79.79,0,0,0,.4.52,1.67,1.67,0,0,0,.81.19h.43a1.53,1.53,0,0,0,.92-.24.77.77,0,0,0,.33-.66q0-.72-1.05-.87l-1-.13a2.75,2.75,0,0,1-1.58-.62,1.72,1.72,0,0,1-.51-1.33,1.7,1.7,0,0,1,.64-1.42,2.89,2.89,0,0,1,1.83-.5h.42a2.87,2.87,0,0,1,1.7.46,1.82,1.82,0,0,1,.77,1.25h-1.35a.76.76,0,0,0-.38-.47,1.4,1.4,0,0,0-.74-.17h-.42a1.4,1.4,0,0,0-.86.21.7.7,0,0,0-.3.63.67.67,0,0,0,.22.54,1.39,1.39,0,0,0,.71.25l1,.13a3.08,3.08,0,0,1,1.65.63,2,2,0,0,1-.13,2.86,3,3,0,0,1-1.9.52h-.43a3.08,3.08,0,0,1-1.79-.48A1.77,1.77,0,0,1,584.19,145.36Z" style="fill:#139c5a"/><path d="M591.43,142.85a2.78,2.78,0,0,1,.19-1.06,2.16,2.16,0,0,1,.55-.8,2.46,2.46,0,0,1,.88-.51,3.76,3.76,0,0,1,2.3,0,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.44,1.44,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.14,1.14,0,0,0,.54-.47h1.29a2.22,2.22,0,0,1-.94,1.26,2.9,2.9,0,0,1-.8.35,3.79,3.79,0,0,1-1,.12,3.47,3.47,0,0,1-1.15-.18,2.38,2.38,0,0,1-.87-.52,2.2,2.2,0,0,1-.55-.81,2.6,2.6,0,0,1-.2-1.06Zm1.31.25h2.92v-.25a1.41,1.41,0,0,0-.38-1.06,1.69,1.69,0,0,0-2.16,0,1.41,1.41,0,0,0-.38,1.06Z" style="fill:#139c5a"/></svg>⏎ |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="107.81mm" height="51.72mm" viewBox="0 0 305.59 146.59"><path d="M209,116.05h1.36a.81.81,0,0,0,.41.52,1.67,1.67,0,0,0,.81.19H212a1.55,1.55,0,0,0,.92-.24.77.77,0,0,0,.33-.66c0-.48-.36-.77-1.06-.87l-1-.13a2.78,2.78,0,0,1-1.58-.62,1.94,1.94,0,0,1,.13-2.75,2.89,2.89,0,0,1,1.83-.5h.42a2.85,2.85,0,0,1,1.7.46,1.79,1.79,0,0,1,.78,1.25h-1.36a.72.72,0,0,0-.37-.47,1.44,1.44,0,0,0-.75-.18h-.42a1.5,1.5,0,0,0-.86.21.72.72,0,0,0-.3.63.65.65,0,0,0,.22.54,1.34,1.34,0,0,0,.72.26l1,.13a3.08,3.08,0,0,1,1.66.63,2,2,0,0,1-.14,2.85,3,3,0,0,1-1.89.53h-.43a3.09,3.09,0,0,1-1.8-.48A1.73,1.73,0,0,1,209,116.05Z" style="fill:#1d1d1b"/><path d="M216.36,117.71v-8.64h1.32v2c0,.11,0,.23,0,.36s0,.26,0,.37,0,.25,0,.41h.11a1.49,1.49,0,0,1,.59-.93,2,2,0,0,1,1.17-.33,2.36,2.36,0,0,1,.9.16,2.2,2.2,0,0,1,.68.48,2.32,2.32,0,0,1,.44.75,3.05,3.05,0,0,1,.15,1v4.35h-1.3v-4.17a1.45,1.45,0,0,0-.36-1.05,1.26,1.26,0,0,0-1-.38,1.29,1.29,0,0,0-1,.4,1.53,1.53,0,0,0-.36,1.08v4.12Z" style="fill:#1d1d1b"/><path d="M223.29,115.82a1.88,1.88,0,0,1,.66-1.52,2.71,2.71,0,0,1,1.81-.57h1.79v-.55a1,1,0,0,0-.37-.82,1.59,1.59,0,0,0-1-.29,1.93,1.93,0,0,0-.87.18,1,1,0,0,0-.48.52h-1.3a1.93,1.93,0,0,1,.88-1.3,3.65,3.65,0,0,1,3.77.09,1.93,1.93,0,0,1,.73,1.59v4.56h-1.23v-1.29h-.09a1.59,1.59,0,0,1-.71,1,2.51,2.51,0,0,1-1.41.38,2.21,2.21,0,0,1-1.56-.55A1.89,1.89,0,0,1,223.29,115.82Zm1.32-.11a.94.94,0,0,0,.32.78,1.34,1.34,0,0,0,.91.28,2.17,2.17,0,0,0,.69-.1,1.71,1.71,0,0,0,.54-.27,1.21,1.21,0,0,0,.35-.4,1.14,1.14,0,0,0,.13-.51v-.83h-1.78a1.26,1.26,0,0,0-.85.27A1,1,0,0,0,224.61,115.71Z" style="fill:#1d1d1b"/><path d="M230.77,111.11H232v1.14h.1a1.47,1.47,0,0,1,.61-.93,2,2,0,0,1,1.19-.33,2.29,2.29,0,0,1,.92.18,2.05,2.05,0,0,1,.72.51,2.46,2.46,0,0,1,.45.81,3.31,3.31,0,0,1,.17,1.08v1.65a3.41,3.41,0,0,1-.17,1.09,2.32,2.32,0,0,1-.45.82,2.08,2.08,0,0,1-.72.52,2.29,2.29,0,0,1-.92.18,2,2,0,0,1-1.19-.33,1.47,1.47,0,0,1-.61-.93H232s0,.05,0,.13,0,.17,0,.29,0,.23,0,.36,0,.24,0,.36v2h-1.32Zm1.32,2.48v1.63a1.54,1.54,0,0,0,.37,1.06,1.28,1.28,0,0,0,1,.39,1.38,1.38,0,0,0,1-.39,1.5,1.5,0,0,0,.38-1.07v-1.62a1.47,1.47,0,0,0-.38-1.07,1.38,1.38,0,0,0-1-.39,1.27,1.27,0,0,0-1,.39A1.5,1.5,0,0,0,232.09,113.59Z" style="fill:#1d1d1b"/><path d="M237.84,113.54a3,3,0,0,1,.19-1.06,2.18,2.18,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.3a2.18,2.18,0,0,1-1,1.26,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.52,2.52,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.34,1.34,0,0,0-1.47-1.45,1.32,1.32,0,0,0-1.46,1.45Z" style="fill:#1d1d1b"/><path d="M244.63,110.18V109h3.46v6.69a.8.8,0,0,0,.22.61.78.78,0,0,0,.6.23h2v1.2h-2a2.11,2.11,0,0,1-1.53-.55,2,2,0,0,1-.55-1.49v-5.49Z" style="fill:#1d1d1b"/><path d="M251.92,111.11h1.44l1.39,3.63a4.42,4.42,0,0,1,.15.48,4.15,4.15,0,0,1,.09.43c0,.16,0,.3.06.44h.1a3.59,3.59,0,0,1,0-.44c0-.12.06-.27.09-.43s.09-.32.14-.48l1.3-3.63h1.39l-3.17,8.64h-1.39l.91-2.46Z" style="fill:#1d1d1b"/><path d="M205.39,127.93a3.5,3.5,0,0,1,.16-1.05,2.1,2.1,0,0,1,.46-.8,2.05,2.05,0,0,1,.72-.51,2.34,2.34,0,0,1,.93-.18,2.1,2.1,0,0,1,1.23.34,1.61,1.61,0,0,1,.63,1h.09v-1.2h1.24v6.2a2.27,2.27,0,0,1-.71,1.78,2.78,2.78,0,0,1-2,.66h-1.71V133h1.71a1.4,1.4,0,0,0,1-.35,1.27,1.27,0,0,0,.37-1v-.23l0-1.2h-.07a1.59,1.59,0,0,1-.65,1,2,2,0,0,1-1.22.35,2.34,2.34,0,0,1-.93-.18,2,2,0,0,1-.71-.51,2.22,2.22,0,0,1-.45-.8,3.11,3.11,0,0,1-.17-1Zm1.34,1.12a1.41,1.41,0,1,0,2.82,0v-1.11a1.43,1.43,0,0,0-.38-1,1.63,1.63,0,0,0-2.07,0,1.42,1.42,0,0,0-.37,1Z" style="fill:#1d1d1b"/><path d="M212.64,127.94a3,3,0,0,1,.19-1.06,2.18,2.18,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21H214v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.3a2.18,2.18,0,0,1-.95,1.26,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.52,2.52,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.34,1.34,0,0,0-1.47-1.45,1.32,1.32,0,0,0-1.46,1.45Z" style="fill:#1d1d1b"/><path d="M222.61,132.23a3.41,3.41,0,0,1-1.14-.19,2.4,2.4,0,0,1-.87-.53,2.35,2.35,0,0,1-.56-.82,2.83,2.83,0,0,1-.2-1.07V128a2.83,2.83,0,0,1,.2-1.07,2.35,2.35,0,0,1,.56-.82,2.4,2.4,0,0,1,.87-.53,3.64,3.64,0,0,1,2.29,0,2.4,2.4,0,0,1,.87.53,2.31,2.31,0,0,1,.56.81,2.85,2.85,0,0,1,.2,1.08v1.65a2.92,2.92,0,0,1-.2,1.08,2.32,2.32,0,0,1-.55.82,2.52,2.52,0,0,1-.87.52A3.53,3.53,0,0,1,222.61,132.23Zm-1.45-2.61a1.43,1.43,0,0,0,.39,1.06,1.45,1.45,0,0,0,1.06.38,1.49,1.49,0,0,0,1.07-.38,1.42,1.42,0,0,0,.38-1.06V128a1.42,1.42,0,0,0-.38-1.06,1.68,1.68,0,0,0-2.13,0,1.43,1.43,0,0,0-.39,1.06Z" style="fill:#1d1d1b"/><path d="M226.87,132.11v-6.6H228v1h.09a1.27,1.27,0,0,1,.32-.81,1,1,0,0,1,.76-.28.94.94,0,0,1,.72.27,1.7,1.7,0,0,1,.39.82h.08a1.22,1.22,0,0,1,.32-.81,1.08,1.08,0,0,1,.78-.28,1.2,1.2,0,0,1,1,.45,1.93,1.93,0,0,1,.37,1.24v5h-1.22v-5a1,1,0,0,0-.15-.59.54.54,0,0,0-.46-.2q-.63,0-.63.84v4.92h-1v-5a.92.92,0,0,0-.16-.59.59.59,0,0,0-.48-.2q-.63,0-.63.84v4.92Z" style="fill:#1d1d1b"/><path d="M234.24,127.94a3,3,0,0,1,.19-1.06,2.18,2.18,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.3a2.18,2.18,0,0,1-.95,1.26,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.52,2.52,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.34,1.34,0,0,0-1.47-1.45,1.32,1.32,0,0,0-1.46,1.45Z" style="fill:#1d1d1b"/><path d="M241.24,125.51h1.86v-2h1.32v2H247v1.18h-2.53v3.52a.66.66,0,0,0,.19.51.77.77,0,0,0,.54.19h1.68v1.2h-1.74a2,2,0,0,1-1.46-.52,1.8,1.8,0,0,1-.53-1.38v-3.52h-1.86Z" style="fill:#1d1d1b"/><path d="M250.28,125.51v1.14h.11a1.48,1.48,0,0,1,.62-.93,2.43,2.43,0,0,1,2.88.32,2.64,2.64,0,0,1,.59,1.82v.43h-1.35V128a1.56,1.56,0,0,0-.37-1.09,1.35,1.35,0,0,0-1-.39,1.33,1.33,0,0,0-1,.4,1.49,1.49,0,0,0-.37,1.08v4.12H249v-6.6Z" style="fill:#1d1d1b"/><path d="M255.52,125.51H257l1.39,3.63a4.42,4.42,0,0,1,.15.48c0,.15.06.3.09.43s0,.3.06.44h.1a3.59,3.59,0,0,1,0-.44c0-.12.06-.27.09-.43s.09-.32.14-.48l1.3-3.63h1.39l-3.17,8.64h-1.39l.91-2.46Z" style="fill:#1d1d1b"/><rect x="11" y="31.75" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="11" y="50.85" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="11" y="69.95" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="11" y="89.05" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="11" y="108.16" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="53.8" y="31.75" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="12.65" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="53.8" y="50.85" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="69.95" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="89.05" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="108.16" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><line x1="144.24" y1="41.3" x2="193.76" y2="79.5" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><line x1="144.24" y1="60.4" x2="193.76" y2="79.5" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><line x1="144.24" y1="79.5" x2="193.76" y2="79.5" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="192.3 84.49 200.94 79.5 192.3 74.52 192.3 84.49" style="fill:#1d1d1b"/><line x1="144.24" y1="98.6" x2="193.76" y2="79.5" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><line x1="144.24" y1="117.71" x2="193.76" y2="79.5" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10"/><polygon points="254.5 75.67 247.28 95.85 226.19 99.69 212.32 83.34 219.55 63.16 240.64 59.32 254.5 75.67" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/></svg>⏎ | |
0 | <svg xmlns="http://www.w3.org/2000/svg" width="107.81mm" height="51.72mm" viewBox="0 0 305.59 146.59"><path d="M209,116.05h1.36a.81.81,0,0,0,.41.52,1.67,1.67,0,0,0,.81.19H212a1.55,1.55,0,0,0,.92-.24.77.77,0,0,0,.33-.66c0-.48-.36-.77-1.06-.87l-1-.13a2.78,2.78,0,0,1-1.58-.62,1.94,1.94,0,0,1,.13-2.75,2.89,2.89,0,0,1,1.83-.5h.42a2.85,2.85,0,0,1,1.7.46,1.79,1.79,0,0,1,.78,1.25h-1.36a.72.72,0,0,0-.37-.47,1.44,1.44,0,0,0-.75-.18h-.42a1.5,1.5,0,0,0-.86.21.72.72,0,0,0-.3.63.65.65,0,0,0,.22.54,1.34,1.34,0,0,0,.72.26l1,.13a3.08,3.08,0,0,1,1.66.63,2,2,0,0,1-.14,2.85,3,3,0,0,1-1.89.53h-.43a3.09,3.09,0,0,1-1.8-.48A1.73,1.73,0,0,1,209,116.05Z" style="fill:#139c5a"/><path d="M216.36,117.71v-8.64h1.32v2c0,.11,0,.23,0,.36s0,.26,0,.37,0,.25,0,.41h.11a1.49,1.49,0,0,1,.59-.93,2,2,0,0,1,1.17-.33,2.36,2.36,0,0,1,.9.16,2.2,2.2,0,0,1,.68.48,2.32,2.32,0,0,1,.44.75,3.05,3.05,0,0,1,.15,1v4.35h-1.3v-4.17a1.45,1.45,0,0,0-.36-1.05,1.26,1.26,0,0,0-1-.38,1.29,1.29,0,0,0-1,.4,1.53,1.53,0,0,0-.36,1.08v4.12Z" style="fill:#139c5a"/><path d="M223.29,115.82a1.88,1.88,0,0,1,.66-1.52,2.71,2.71,0,0,1,1.81-.57h1.79v-.55a1,1,0,0,0-.37-.82,1.59,1.59,0,0,0-1-.29,1.93,1.93,0,0,0-.87.18,1,1,0,0,0-.48.52h-1.3a1.93,1.93,0,0,1,.88-1.3,3.65,3.65,0,0,1,3.77.09,1.93,1.93,0,0,1,.73,1.59v4.56h-1.23v-1.29h-.09a1.59,1.59,0,0,1-.71,1,2.51,2.51,0,0,1-1.41.38,2.21,2.21,0,0,1-1.56-.55A1.89,1.89,0,0,1,223.29,115.82Zm1.32-.11a.94.94,0,0,0,.32.78,1.34,1.34,0,0,0,.91.28,2.17,2.17,0,0,0,.69-.1,1.71,1.71,0,0,0,.54-.27,1.21,1.21,0,0,0,.35-.4,1.14,1.14,0,0,0,.13-.51v-.83h-1.78a1.26,1.26,0,0,0-.85.27A1,1,0,0,0,224.61,115.71Z" style="fill:#139c5a"/><path d="M230.77,111.11H232v1.14h.1a1.47,1.47,0,0,1,.61-.93,2,2,0,0,1,1.19-.33,2.29,2.29,0,0,1,.92.18,2.05,2.05,0,0,1,.72.51,2.46,2.46,0,0,1,.45.81,3.31,3.31,0,0,1,.17,1.08v1.65a3.41,3.41,0,0,1-.17,1.09,2.32,2.32,0,0,1-.45.82,2.08,2.08,0,0,1-.72.52,2.29,2.29,0,0,1-.92.18,2,2,0,0,1-1.19-.33,1.47,1.47,0,0,1-.61-.93H232s0,.05,0,.13,0,.17,0,.29,0,.23,0,.36,0,.24,0,.36v2h-1.32Zm1.32,2.48v1.63a1.54,1.54,0,0,0,.37,1.06,1.28,1.28,0,0,0,1,.39,1.38,1.38,0,0,0,1-.39,1.5,1.5,0,0,0,.38-1.07v-1.62a1.47,1.47,0,0,0-.38-1.07,1.38,1.38,0,0,0-1-.39,1.27,1.27,0,0,0-1,.39A1.5,1.5,0,0,0,232.09,113.59Z" style="fill:#139c5a"/><path d="M237.84,113.54a3,3,0,0,1,.19-1.06,2.18,2.18,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.3a2.18,2.18,0,0,1-1,1.26,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.52,2.52,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.34,1.34,0,0,0-1.47-1.45,1.32,1.32,0,0,0-1.46,1.45Z" style="fill:#139c5a"/><path d="M244.63,110.18V109h3.46v6.69a.8.8,0,0,0,.22.61.78.78,0,0,0,.6.23h2v1.2h-2a2.11,2.11,0,0,1-1.53-.55,2,2,0,0,1-.55-1.49v-5.49Z" style="fill:#139c5a"/><path d="M251.92,111.11h1.44l1.39,3.63a4.42,4.42,0,0,1,.15.48,4.15,4.15,0,0,1,.09.43c0,.16,0,.3.06.44h.1a3.59,3.59,0,0,1,0-.44c0-.12.06-.27.09-.43s.09-.32.14-.48l1.3-3.63h1.39l-3.17,8.64h-1.39l.91-2.46Z" style="fill:#139c5a"/><path d="M205.39,127.93a3.5,3.5,0,0,1,.16-1.05,2.1,2.1,0,0,1,.46-.8,2.05,2.05,0,0,1,.72-.51,2.34,2.34,0,0,1,.93-.18,2.1,2.1,0,0,1,1.23.34,1.61,1.61,0,0,1,.63,1h.09v-1.2h1.24v6.2a2.27,2.27,0,0,1-.71,1.78,2.78,2.78,0,0,1-2,.66h-1.71V133h1.71a1.4,1.4,0,0,0,1-.35,1.27,1.27,0,0,0,.37-1v-.23l0-1.2h-.07a1.59,1.59,0,0,1-.65,1,2,2,0,0,1-1.22.35,2.34,2.34,0,0,1-.93-.18,2,2,0,0,1-.71-.51,2.22,2.22,0,0,1-.45-.8,3.11,3.11,0,0,1-.17-1Zm1.34,1.12a1.41,1.41,0,1,0,2.82,0v-1.11a1.43,1.43,0,0,0-.38-1,1.63,1.63,0,0,0-2.07,0,1.42,1.42,0,0,0-.37,1Z" style="fill:#139c5a"/><path d="M212.64,127.94a3,3,0,0,1,.19-1.06,2.18,2.18,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21H214v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.3a2.18,2.18,0,0,1-.95,1.26,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.52,2.52,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.34,1.34,0,0,0-1.47-1.45,1.32,1.32,0,0,0-1.46,1.45Z" style="fill:#139c5a"/><path d="M222.61,132.23a3.41,3.41,0,0,1-1.14-.19,2.4,2.4,0,0,1-.87-.53,2.35,2.35,0,0,1-.56-.82,2.83,2.83,0,0,1-.2-1.07V128a2.83,2.83,0,0,1,.2-1.07,2.35,2.35,0,0,1,.56-.82,2.4,2.4,0,0,1,.87-.53,3.64,3.64,0,0,1,2.29,0,2.4,2.4,0,0,1,.87.53,2.31,2.31,0,0,1,.56.81,2.85,2.85,0,0,1,.2,1.08v1.65a2.92,2.92,0,0,1-.2,1.08,2.32,2.32,0,0,1-.55.82,2.52,2.52,0,0,1-.87.52A3.53,3.53,0,0,1,222.61,132.23Zm-1.45-2.61a1.43,1.43,0,0,0,.39,1.06,1.45,1.45,0,0,0,1.06.38,1.49,1.49,0,0,0,1.07-.38,1.42,1.42,0,0,0,.38-1.06V128a1.42,1.42,0,0,0-.38-1.06,1.68,1.68,0,0,0-2.13,0,1.43,1.43,0,0,0-.39,1.06Z" style="fill:#139c5a"/><path d="M226.87,132.11v-6.6H228v1h.09a1.27,1.27,0,0,1,.32-.81,1,1,0,0,1,.76-.28.94.94,0,0,1,.72.27,1.7,1.7,0,0,1,.39.82h.08a1.22,1.22,0,0,1,.32-.81,1.08,1.08,0,0,1,.78-.28,1.2,1.2,0,0,1,1,.45,1.93,1.93,0,0,1,.37,1.24v5h-1.22v-5a1,1,0,0,0-.15-.59.54.54,0,0,0-.46-.2q-.63,0-.63.84v4.92h-1v-5a.92.92,0,0,0-.16-.59.59.59,0,0,0-.48-.2q-.63,0-.63.84v4.92Z" style="fill:#139c5a"/><path d="M234.24,127.94a3,3,0,0,1,.19-1.06,2.18,2.18,0,0,1,.56-.8,2.5,2.5,0,0,1,.87-.51,3.51,3.51,0,0,1,1.15-.18,3.43,3.43,0,0,1,1.15.18,2.5,2.5,0,0,1,.87.51,2.16,2.16,0,0,1,.55.8,2.78,2.78,0,0,1,.19,1.06v1.21h-4.22v.47a1.53,1.53,0,0,0,.39,1.11,1.45,1.45,0,0,0,1.07.39,2.35,2.35,0,0,0,.88-.15,1.05,1.05,0,0,0,.54-.47h1.3a2.18,2.18,0,0,1-.95,1.26,2.77,2.77,0,0,1-.8.35,3.74,3.74,0,0,1-1,.12,3.41,3.41,0,0,1-1.14-.18,2.52,2.52,0,0,1-.87-.52,2.22,2.22,0,0,1-.56-.81,2.78,2.78,0,0,1-.2-1.06Zm1.31.25h2.93v-.25a1.34,1.34,0,0,0-1.47-1.45,1.32,1.32,0,0,0-1.46,1.45Z" style="fill:#139c5a"/><path d="M241.24,125.51h1.86v-2h1.32v2H247v1.18h-2.53v3.52a.66.66,0,0,0,.19.51.77.77,0,0,0,.54.19h1.68v1.2h-1.74a2,2,0,0,1-1.46-.52,1.8,1.8,0,0,1-.53-1.38v-3.52h-1.86Z" style="fill:#139c5a"/><path d="M250.28,125.51v1.14h.11a1.48,1.48,0,0,1,.62-.93,2.43,2.43,0,0,1,2.88.32,2.64,2.64,0,0,1,.59,1.82v.43h-1.35V128a1.56,1.56,0,0,0-.37-1.09,1.35,1.35,0,0,0-1-.39,1.33,1.33,0,0,0-1,.4,1.49,1.49,0,0,0-.37,1.08v4.12H249v-6.6Z" style="fill:#139c5a"/><path d="M255.52,125.51H257l1.39,3.63a4.42,4.42,0,0,1,.15.48c0,.15.06.3.09.43s0,.3.06.44h.1a3.59,3.59,0,0,1,0-.44c0-.12.06-.27.09-.43s.09-.32.14-.48l1.3-3.63h1.39l-3.17,8.64h-1.39l.91-2.46Z" style="fill:#139c5a"/><rect x="11" y="31.75" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="11" y="50.85" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="11" y="69.95" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.6"/><rect x="11" y="89.05" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10;opacity:0.8"/><rect x="11" y="108.16" width="38.2" height="19.1" style="fill:#0f9c5a;stroke:#fff;stroke-miterlimit:10"/><rect x="53.8" y="31.75" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="12.65" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.4"/><rect x="53.8" y="50.85" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="69.95" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="89.05" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><rect x="53.8" y="108.16" width="83.41" height="19.1" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/><line x1="144.24" y1="41.3" x2="193.76" y2="79.5" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><line x1="144.24" y1="60.4" x2="193.76" y2="79.5" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><line x1="144.24" y1="79.5" x2="193.76" y2="79.5" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="192.3 84.49 200.94 79.5 192.3 74.52 192.3 84.49" style="fill:#139c5a"/><line x1="144.24" y1="98.6" x2="193.76" y2="79.5" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><line x1="144.24" y1="117.71" x2="193.76" y2="79.5" style="fill:none;stroke:#139c5a;stroke-miterlimit:10"/><polygon points="254.5 75.67 247.28 95.85 226.19 99.69 212.32 83.34 219.55 63.16 240.64 59.32 254.5 75.67" style="fill:#e21c84;stroke:#fff;stroke-miterlimit:10;opacity:0.2"/></svg>⏎ |
0 | 0 | /* colors */ |
1 | 1 | |
2 | :root { | |
3 | --pst-color-primary: 19, 156, 90; | |
4 | --pst-color-active-navigation: 19, 156, 90; | |
5 | --pst-color-h2: var(--color-text-base); | |
2 | html[data-theme="light"] { | |
3 | --pst-color-primary: rgb(19, 156, 90); | |
4 | --pst-color-secondary: rgb(231, 4, 136); | |
6 | 5 | } |
6 | ||
7 | html[data-theme="dark"] { | |
8 | --pst-color-primary: rgb(19, 156, 90); | |
9 | --pst-color-secondary: rgb(231, 4, 136); | |
10 | } | |
11 | ||
7 | 12 | |
8 | 13 | /* buttons */ |
9 | 14 | |
10 | 15 | .button>p>a { |
11 | 16 | box-shadow: 0px 4px 14px -7px #999999; |
12 | background-color: white; | |
17 | /* background-color: white; */ | |
13 | 18 | border: 1px solid #bbbbbb; |
14 | 19 | display: inline-block; |
15 | 20 | cursor: pointer; |
23 | 28 | } |
24 | 29 | |
25 | 30 | .button>p>a:hover { |
26 | border-color: #139C5A; | |
27 | color: #e32e00; | |
31 | border-color: rgb(19, 156, 90); | |
32 | color: rgb(231, 4, 136); | |
28 | 33 | } |
29 | 34 | |
30 | 35 | .button>p>a:active { |
31 | 36 | position: relative; |
32 | 37 | top: 1px; |
33 | } | |
38 | }⏎ |
45 | 45 | |
46 | 46 | You can download all version in SVG and PNG from [GitHub repository](https://github.com/geopandas/geopandas/tree/main/doc/source/_static/logo). |
47 | 47 | |
48 | ||
49 | 48 | ## Colors |
50 | 49 | |
51 | 50 | Pink and yellow accent colors are shared with `pandas`. |
52 | 51 | |
53 | 52 | ### Green |
53 | ||
54 | 54 | ```{raw} html |
55 | 55 | <svg xmlns="http://www.w3.org/2000/svg" width="75" height="75" style="float: left"> |
56 | 56 | <circle cx="33" cy="33" r="33" fill="#139C5A"></circle> |
57 | 57 | </svg> |
58 | 58 | ``` |
59 | ||
59 | 60 | **HEX:** #139C5A |
60 | 61 | |
61 | 62 | **RGB:** (19, 156, 90) |
62 | 63 | |
63 | 64 | ### Yellow |
65 | ||
64 | 66 | ```{raw} html |
65 | 67 | <svg xmlns="http://www.w3.org/2000/svg" width="75" height="75" style="float: left"> |
66 | 68 | <circle cx="33" cy="33" r="33" fill="#FFCA00"></circle> |
67 | 69 | </svg> |
68 | 70 | ``` |
71 | ||
69 | 72 | **HEX:** #FFCA00 |
70 | 73 | |
71 | 74 | **RGB:** (255, 202, 0) |
72 | 75 | |
73 | 76 | ### Pink |
77 | ||
74 | 78 | ```{raw} html |
75 | 79 | <svg xmlns="http://www.w3.org/2000/svg" width="75" height="75" style="float: left"> |
76 | 80 | <circle cx="33" cy="33" r="33" fill="#E70488"></circle> |
77 | 81 | </svg> |
78 | 82 | ``` |
83 | ||
79 | 84 | **HEX:** #E70488 |
80 | 85 | |
81 | **RGB:** ((31, 4, 136) | |
82 | ||
83 | ||
86 | **RGB:** (231, 4, 136) |
0 | 0 | # Roadmap |
1 | ||
2 | This page provides an overview of the strategic goals for development of GeoPandas. Some | |
3 | of the tasks may happen sooner given the appropriate funding, other later with no | |
4 | specified date, and some may not happen at all if the implementation proves to be | |
5 | against the will of the community or face technical issues preventing their inclusion in | |
6 | the code base. | |
7 | ||
8 | The current roadmap is divided into two milestones. The first milestone aims at a | |
9 | release of the first major version of GeoPandas, while the second milestone is a | |
10 | longer-term vision covering enhancements that should happen in subsequent releases. | |
1 | 11 | |
2 | 12 | ## Roadmap for GeoPandas 1.0 |
3 | 13 | |
4 | WIP | |
14 | ### Fully vectorized geometry engine | |
15 | ||
16 | GeoPandas uses `shapely` as its geometry engine, based on scalar geometries, requiring a | |
17 | loop-based implementation of most GeoPandas methods. That comes at a significant | |
18 | performance cost, which is being resolved in shapely 2.0, a new major release resulting | |
19 | from a complete rewrite of the internals using the vectorized implementation prototyped | |
20 | in the `PyGEOS` project. At this moment, GeoPandas supports `shapely<2.0`, | |
21 | `shapely>=2.0`, and `PyGEOS` as possible geometry engines, which causes friction in the | |
22 | development process and uneven performance on the user side based on what geometry | |
23 | engine the user happens to be using. | |
24 | ||
25 | GeoPandas 1.0 will require `shapely>=2.0` and deprecate both older shapely and `PyGEOS` | |
26 | engines. This change should simplify the code base allowing more manageable maintenance | |
27 | and a lower barrier to entry for new contributors. | |
28 | ||
29 | ### Feature parity with shapely | |
30 | ||
31 | Even though GeoPandas uses shapely as the geometry engine, not all its functions are | |
32 | exposed at a GeoPandas level. This has resulted in a less convenient API and a need to | |
33 | switch between `GeoSeries` objects and lists or arrays of geometries, potentially | |
34 | risking the data loss or corruption as the CRS is not included in such operations. In | |
35 | the first phase, all element-wise operations (e.g. `segmentize`, or | |
36 | `minimum_bounding_circle`) should be exposed as `GeoSeries` methods. The feature parity | |
37 | should be reached in the second phase, covering all relevant functions. | |
38 | ||
39 | ### Clarity of the API | |
40 | ||
41 | The first version of the GeoPandas API is nearly ten years old. The PyData ecosystem has | |
42 | significantly changed in the meantime, and some of the early decisions may no longer be | |
43 | future-proof. Ahead of GeoPandas 1.0, the API will be revised to ensure that all the | |
44 | necessary deprecations occur before the major release to provide the stability of the | |
45 | API for the coming years. | |
46 | ||
47 | ### Pruned dependencies | |
48 | ||
49 | GeoPandas offers functionality for every step of a typical geospatial workflow, from | |
50 | reading of the GIS file formats to geometry operations and handling of Coordinate | |
51 | Reference Systems (CRS) and transformation of geometries between them. However, GIS I/O | |
52 | depends on a relatively heavy C++ library `GDAL` and CRS management on another C++ | |
53 | library `PROJ`, even though not every application based on GeoPandas is necessarily | |
54 | geospatial. GeoPandas 1.0 should eliminate the hard dependency on both `GDAL` and `PROJ` | |
55 | and offer the basic capability of a GeoDataFrame with a minimal set of dependencies | |
56 | limited to `pandas` and `shapely`. | |
57 | ||
58 | ## Beyond GeoPandas 1.0 | |
59 | ||
60 | Additional work is planned for a longer time frame, stretching beyond GeoPandas 1.0 | |
61 | without a specific target release. | |
62 | ||
63 | ### S2 geometry engine | |
64 | ||
65 | The geometry engine used in GeoPandas is `shapely`, which serves as a Python API for | |
66 | `GEOS`. It means that all geometry operations in GeoPandas are planar, using (possibly) | |
67 | projected coordinate reference systems. Some applications focusing on the global context | |
68 | may find planar operations limiting as they come with troubles around anti-meridian and | |
69 | poles. One solution is an implementation of a spherical geometry engine, namely `S2`, | |
70 | that should eliminate these limitations and offer an alternative to `GEOS`. | |
71 | ||
72 | The GeoPandas community is currently working together with the R-spatial community that | |
73 | has already exposed `S2` in an R counterpart of GeoPandas `sf` on Python bindings for | |
74 | `S2`, that should be used as a secondary geometry engine in GeoPandas. | |
75 | ||
76 | ### Lighter-weight geospatial I/O | |
77 | ||
78 | In order to support lighter-weight installations of GeoPandas that do not depend on | |
79 | heavier and difficult to install libraries such as GDAL, additional I/O libraries should | |
80 | be developed and integrated into GeoPandas as optional dependencies. These should be | |
81 | simpler to install and not require binary dependencies, which would lower the barrier to | |
82 | entry for GeoPandas users that need basic I/O support for a limited number of GIS | |
83 | formats such as ESRI Shapefiles or GeoPackages. | |
84 | ||
85 | ### Prepared geometries | |
86 | ||
87 | GeoPandas is using spatial indexing for the operations that may benefit from it. Further | |
88 | performance gains can be achieved using prepared geometries. Preparation creates a | |
89 | spatial index of individual line segments of geometries, greatly enhancing the speed of | |
90 | spatial predicates like `intersects` or `contains`. Given that the preparation has | |
91 | become less computationally expensive in `shapely` 2.0, GeoPandas should expose the | |
92 | preparation to the user but, more importantly, use smart automatic geometry preparation | |
93 | under the hood. | |
94 | ||
95 | ### Static plotting improvements | |
96 | ||
97 | GeoPandas currently covers a broad range of geospatial tasks, from data exploration to | |
98 | advanced analysis. However, one moment may tempt the user to use different software - | |
99 | plotting. GeoPandas can create static maps based on ``matplotlib``, but they are a bit | |
100 | basic at the moment. It isn't straightforward to generate a complex map in a | |
101 | production-quality which can go straight to an academic journal or an infographic. We | |
102 | want to change this and remove barriers which we currently have and make it simple to | |
103 | create beautiful maps. |
4 | 4 | GeoPandas is developed by more than [100 volunteer contributors](https://github.com/geopandas/geopandas/graphs/contributors). |
5 | 5 | |
6 | 6 | ## Core developers |
7 | ||
7 | 8 | - Joris Van den Bossche - **lead maintainer** | [@jorisvandenbossche](https://github.com/jorisvandenbossche) |
8 | 9 | - Martin Fleischmann | [@martinfleis](https://github.com/martinfleis) |
9 | 10 | - James McBride | [@jdmcbr](https://github.com/jdmcbr) |
10 | 11 | - Brendan Ward | [@brendan-ward](https://github.com/brendan-ward) |
11 | 12 | - Levi Wolf | [@ljwolf](https://github.com/ljwolf) |
13 | - Matt Richards | [@m-richards](https://github.com/m-richards) | |
12 | 14 | |
13 | 15 | ## Founder |
14 | 16 |
5 | 5 | :hidden: |
6 | 6 | |
7 | 7 | Team <about/team> |
8 | Roadmap <about/roadmap> | |
8 | 9 | Citing <about/citing> |
9 | 10 | Logo <about/logo> |
10 | 11 | ``` |
11 | ||
12 | 12 | |
13 | 13 | GeoPandas is an open source project to add support for geographic data to pandas objects. It |
14 | 14 | currently implements `GeoSeries` and `GeoDataFrame` types which are subclasses of |
24 | 24 | |
25 | 25 | ```{container} button |
26 | 26 | |
27 | {doc}`Team <about/team>` | |
27 | {doc}`Team <about/team>` {doc}`Roadmap <about/roadmap>` | |
28 | 28 | {doc}`Citing <about/citing>` {doc}`Logo <about/logo>` |
29 | 29 | ``` |
30 | 30 | |
48 | 48 | - **2014**: GeoPandas 0.1.0 released |
49 | 49 | - **2020**: GeoPandas became [NumFOCUS Affiliated |
50 | 50 | Project](https://numfocus.org/sponsored-projects/affiliated-projects) |
51 | ||
52 |
13 | 13 | import sys, os |
14 | 14 | import warnings |
15 | 15 | |
16 | sys.path.insert(0, os.path.abspath("../..")) | |
17 | ||
18 | import geopandas # noqa | |
19 | ||
16 | 20 | # If extensions (or modules to document with autodoc) are in another directory, |
17 | 21 | # add these directories to sys.path here. If the directory is relative to the |
18 | 22 | # documentation root, use os.path.abspath to make it absolute, like shown here. |
32 | 36 | "sphinx.ext.autosummary", |
33 | 37 | "sphinx.ext.intersphinx", |
34 | 38 | "sphinx.ext.autodoc", |
39 | "sphinx.ext.linkcode", | |
35 | 40 | "myst_parser", |
36 | 41 | "nbsphinx", |
37 | 42 | "numpydoc", |
84 | 89 | master_doc = "index" |
85 | 90 | |
86 | 91 | # General information about the project. |
87 | project = u"GeoPandas" | |
88 | copyright = u"2013–2022, GeoPandas developers" | |
92 | project = "GeoPandas" | |
93 | copyright = "2013–2022, GeoPandas developers" | |
89 | 94 | |
90 | 95 | # The version info for the project you're documenting, acts as replacement for |
91 | 96 | # |version| and |release|, also used in various other places throughout the |
259 | 264 | # Grouping the document tree into LaTeX files. List of tuples |
260 | 265 | # (source start file, target name, title, author, documentclass [howto/manual]). |
261 | 266 | latex_documents = [ |
262 | ("index", "GeoPandas.tex", u"GeoPandas Documentation", u"Kelsey Jordahl", "manual") | |
267 | ("index", "GeoPandas.tex", "GeoPandas Documentation", "Kelsey Jordahl", "manual") | |
263 | 268 | ] |
264 | 269 | |
265 | 270 | # The name of an image file (relative to this directory) to place at the top of |
287 | 292 | |
288 | 293 | # One entry per manual page. List of tuples |
289 | 294 | # (source start file, name, description, authors, manual section). |
290 | man_pages = [("index", "geopandas", u"GeoPandas Documentation", [u"Kelsey Jordahl"], 1)] | |
295 | man_pages = [("index", "geopandas", "GeoPandas Documentation", ["Kelsey Jordahl"], 1)] | |
291 | 296 | |
292 | 297 | # If true, show URL addresses after external links. |
293 | 298 | # man_show_urls = False |
302 | 307 | ( |
303 | 308 | "index", |
304 | 309 | "GeoPandas", |
305 | u"GeoPandas Documentation", | |
306 | u"Kelsey Jordahl", | |
310 | "GeoPandas Documentation", | |
311 | "Kelsey Jordahl", | |
307 | 312 | "GeoPandas", |
308 | 313 | "One line description of project.", |
309 | 314 | "Miscellaneous", |
416 | 421 | "https://xyzservices.readthedocs.io/en/stable/", |
417 | 422 | "https://xyzservices.readthedocs.io/en/stable/objects.inv", |
418 | 423 | ), |
424 | "pyogrio": ( | |
425 | "https://pyogrio.readthedocs.io/en/stable/", | |
426 | "https://pyogrio.readthedocs.io/en/stable/objects.inv", | |
427 | ), | |
419 | 428 | } |
429 | ||
430 | ||
431 | # based on pandas implementation with added support of properties | |
432 | def linkcode_resolve(domain, info): | |
433 | """ | |
434 | Determine the URL corresponding to Python object | |
435 | """ | |
436 | import inspect | |
437 | ||
438 | if domain != "py": | |
439 | return None | |
440 | ||
441 | modname = info["module"] | |
442 | fullname = info["fullname"] | |
443 | ||
444 | submod = sys.modules.get(modname) | |
445 | if submod is None: | |
446 | return None | |
447 | ||
448 | obj = submod | |
449 | for part in fullname.split("."): | |
450 | try: | |
451 | with warnings.catch_warnings(): | |
452 | # Accessing deprecated objects will generate noisy warnings | |
453 | warnings.simplefilter("ignore", FutureWarning) | |
454 | obj = getattr(obj, part) | |
455 | except AttributeError: | |
456 | return None | |
457 | ||
458 | try: | |
459 | fn = inspect.getsourcefile(inspect.unwrap(obj)) | |
460 | except TypeError: | |
461 | try: # property | |
462 | fn = inspect.getsourcefile(inspect.unwrap(obj.fget)) | |
463 | except AttributeError: | |
464 | fn = None | |
465 | if not fn: | |
466 | return None | |
467 | ||
468 | try: | |
469 | source, lineno = inspect.getsourcelines(obj) | |
470 | except TypeError: | |
471 | try: # property | |
472 | source, lineno = inspect.getsourcelines(obj.fget) | |
473 | except AttributeError: | |
474 | lineno = None | |
475 | except OSError: | |
476 | lineno = None | |
477 | ||
478 | if lineno: | |
479 | linespec = f"#L{lineno}-L{lineno + len(source) - 1}" | |
480 | else: | |
481 | linespec = "" | |
482 | ||
483 | fn = os.path.relpath(fn, start=os.path.dirname(geopandas.__file__)) | |
484 | ||
485 | if "+" in geopandas.__version__: | |
486 | return ( | |
487 | f"https://github.com/geopandas/geopandas/blob/main/geopandas/{fn}{linespec}" | |
488 | ) | |
489 | else: | |
490 | return ( | |
491 | f"https://github.com/geopandas/geopandas/blob/" | |
492 | f"v{geopandas.__version__}/geopandas/{fn}{linespec}" | |
493 | ) |
86 | 86 | GeoSeries.convex_hull |
87 | 87 | GeoSeries.envelope |
88 | 88 | GeoSeries.simplify |
89 | GeoSeries.normalize | |
89 | 90 | |
90 | 91 | Affine transformations |
91 | 92 | ---------------------- |
17 | 17 | .. attribute:: GeoSeries.boundary |
18 | 18 | |
19 | 19 | Returns a :class:`~geopandas.GeoSeries` of lower dimensional objects representing |
20 | each geometries's set-theoretic `boundary`. | |
20 | each geometry's set-theoretic `boundary`. | |
21 | 21 | |
22 | 22 | .. attribute:: GeoSeries.centroid |
23 | 23 |
176 | 176 | ) |
177 | 177 | |
178 | 178 | |
179 | SQL WHERE Filter | |
180 | ^^^^^^^^^^^^^^^^^ | |
181 | ||
182 | .. versionadded:: 0.12 | |
183 | ||
184 | Load in a subset of data with a `SQL WHERE clause <https://gdal.org/user/ogr_sql_dialect.html#where>`__. | |
185 | ||
186 | .. note:: Requires Fiona 1.9+ or the pyogrio engine. | |
187 | ||
188 | .. code-block:: python | |
189 | ||
190 | gdf = geopandas.read_file( | |
191 | geopandas.datasets.get_path("naturalearth_lowres"), | |
192 | where="continent='Africa'", | |
193 | ) | |
194 | ||
195 | ||
179 | 196 | Writing Spatial Data |
180 | 197 | --------------------- |
181 | 198 |
39 | 39 | "metadata": {}, |
40 | 40 | "outputs": [], |
41 | 41 | "source": [ |
42 | "df1 = pd.read_csv('volcano_data_2010.csv')\n", | |
42 | "df1 = pd.read_csv(\"volcano_data_2010.csv\")\n", | |
43 | 43 | "\n", |
44 | 44 | "# Keep only relevant columns\n", |
45 | 45 | "df = df1.loc[:, (\"Year\", \"Name\", \"Country\", \"Latitude\", \"Longitude\", \"Type\")]\n", |
54 | 54 | "source": [ |
55 | 55 | "# Create point geometries\n", |
56 | 56 | "geometry = geopandas.points_from_xy(df.Longitude, df.Latitude)\n", |
57 | "geo_df = geopandas.GeoDataFrame(df[['Year','Name','Country', 'Latitude', 'Longitude', 'Type']], geometry=geometry)\n", | |
57 | "geo_df = geopandas.GeoDataFrame(\n", | |
58 | " df[[\"Year\", \"Name\", \"Country\", \"Latitude\", \"Longitude\", \"Type\"]], geometry=geometry\n", | |
59 | ")\n", | |
58 | 60 | "\n", |
59 | 61 | "geo_df.head()" |
60 | 62 | ] |
65 | 67 | "metadata": {}, |
66 | 68 | "outputs": [], |
67 | 69 | "source": [ |
68 | "world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))\n", | |
70 | "world = geopandas.read_file(geopandas.datasets.get_path(\"naturalearth_lowres\"))\n", | |
69 | 71 | "df.Type.unique()" |
70 | 72 | ] |
71 | 73 | }, |
79 | 81 | }, |
80 | 82 | "outputs": [], |
81 | 83 | "source": [ |
82 | "fig, ax = plt.subplots(figsize=(24,18))\n", | |
83 | "world.plot(ax=ax, alpha=0.4, color='grey')\n", | |
84 | "geo_df.plot(column='Type', ax=ax, legend=True)\n", | |
85 | "plt.title('Volcanoes')" | |
84 | "fig, ax = plt.subplots(figsize=(24, 18))\n", | |
85 | "world.plot(ax=ax, alpha=0.4, color=\"grey\")\n", | |
86 | "geo_df.plot(column=\"Type\", ax=ax, legend=True)\n", | |
87 | "plt.title(\"Volcanoes\")" | |
86 | 88 | ] |
87 | 89 | }, |
88 | 90 | { |
100 | 102 | "outputs": [], |
101 | 103 | "source": [ |
102 | 104 | "# Stamen Terrain\n", |
103 | "map = folium.Map(location = [13.406,80.110], tiles = \"Stamen Terrain\", zoom_start = 9)\n", | |
105 | "map = folium.Map(location=[13.406, 80.110], tiles=\"Stamen Terrain\", zoom_start=9)\n", | |
104 | 106 | "map" |
105 | 107 | ] |
106 | 108 | }, |
111 | 113 | "outputs": [], |
112 | 114 | "source": [ |
113 | 115 | "# OpenStreetMap\n", |
114 | "map = folium.Map(location = [13.406,80.110], tiles='OpenStreetMap' , zoom_start = 9)\n", | |
116 | "map = folium.Map(location=[13.406, 80.110], tiles=\"OpenStreetMap\", zoom_start=9)\n", | |
115 | 117 | "map" |
116 | 118 | ] |
117 | 119 | }, |
122 | 124 | "outputs": [], |
123 | 125 | "source": [ |
124 | 126 | "# Stamen Toner\n", |
125 | "map = folium.Map(location = [13.406,80.110], tiles='Stamen Toner', zoom_start = 9)\n", | |
127 | "map = folium.Map(location=[13.406, 80.110], tiles=\"Stamen Toner\", zoom_start=9)\n", | |
126 | 128 | "map" |
127 | 129 | ] |
128 | 130 | }, |
140 | 142 | "outputs": [], |
141 | 143 | "source": [ |
142 | 144 | "# Use terrain map layer to see volcano terrain\n", |
143 | "map = folium.Map(location = [4,10], tiles = \"Stamen Terrain\", zoom_start = 3)" | |
145 | "map = folium.Map(location=[4, 10], tiles=\"Stamen Terrain\", zoom_start=3)" | |
144 | 146 | ] |
145 | 147 | }, |
146 | 148 | { |
158 | 160 | "outputs": [], |
159 | 161 | "source": [ |
160 | 162 | "# Create a geometry list from the GeoDataFrame\n", |
161 | "geo_df_list = [[point.xy[1][0], point.xy[0][0]] for point in geo_df.geometry ]\n", | |
163 | "geo_df_list = [[point.xy[1][0], point.xy[0][0]] for point in geo_df.geometry]\n", | |
162 | 164 | "\n", |
163 | 165 | "# Iterate through list and add a marker for each volcano, color-coded by its type.\n", |
164 | 166 | "i = 0\n", |
165 | 167 | "for coordinates in geo_df_list:\n", |
166 | " #assign a color marker for the type of volcano, Strato being the most common\n", | |
168 | " # assign a color marker for the type of volcano, Strato being the most common\n", | |
167 | 169 | " if geo_df.Type[i] == \"Stratovolcano\":\n", |
168 | 170 | " type_color = \"green\"\n", |
169 | 171 | " elif geo_df.Type[i] == \"Complex volcano\":\n", |
175 | 177 | " else:\n", |
176 | 178 | " type_color = \"purple\"\n", |
177 | 179 | "\n", |
178 | "\n", | |
179 | 180 | " # Place the markers with the popup labels and data\n", |
180 | " map.add_child(folium.Marker(location = coordinates,\n", | |
181 | " popup =\n", | |
182 | " \"Year: \" + str(geo_df.Year[i]) + '<br>' +\n", | |
183 | " \"Name: \" + str(geo_df.Name[i]) + '<br>' +\n", | |
184 | " \"Country: \" + str(geo_df.Country[i]) + '<br>'\n", | |
185 | " \"Type: \" + str(geo_df.Type[i]) + '<br>'\n", | |
186 | " \"Coordinates: \" + str(geo_df_list[i]),\n", | |
187 | " icon = folium.Icon(color = \"%s\" % type_color)))\n", | |
181 | " map.add_child(\n", | |
182 | " folium.Marker(\n", | |
183 | " location=coordinates,\n", | |
184 | " popup=\n", | |
185 | " \"Year: \" + str(geo_df.Year[i]) + \"<br>\"\n", | |
186 | " + \"Name: \" + str(geo_df.Name[i]) + \"<br>\"\n", | |
187 | " + \"Country: \" + str(geo_df.Country[i]) + \"<br>\"\n", | |
188 | " + \"Type: \" + str(geo_df.Type[i]) + \"<br>\"\n", | |
189 | " + \"Coordinates: \" + str(geo_df_list[i]),\n", | |
190 | " icon=folium.Icon(color=\"%s\" % type_color),\n", | |
191 | " )\n", | |
192 | " )\n", | |
188 | 193 | " i = i + 1" |
189 | 194 | ] |
190 | 195 | }, |
217 | 222 | "\n", |
218 | 223 | "from folium import plugins\n", |
219 | 224 | "\n", |
220 | "map = folium.Map(location = [15,30], tiles='Cartodb dark_matter', zoom_start = 2)\n", | |
221 | "\n", | |
222 | "heat_data = [[point.xy[1][0], point.xy[0][0]] for point in geo_df.geometry ]\n", | |
225 | "map = folium.Map(location=[15, 30], tiles=\"Cartodb dark_matter\", zoom_start=2)\n", | |
226 | "\n", | |
227 | "heat_data = [[point.xy[1][0], point.xy[0][0]] for point in geo_df.geometry]\n", | |
223 | 228 | "\n", |
224 | 229 | "heat_data\n", |
225 | 230 | "plugins.HeatMap(heat_data).add_to(map)\n", |
201 | 201 | "cell_type": "markdown", |
202 | 202 | "metadata": {}, |
203 | 203 | "source": [ |
204 | "We're not limited to using the `intersection` binary predicate. Any of the `Shapely` geometry methods that return a Boolean can be used by specifying the `op` kwarg." | |
204 | "We're not limited to using the `intersection` binary predicate. Any of the `Shapely` geometry methods that return a Boolean can be used by specifying the `predicate` kwarg." | |
205 | 205 | ] |
206 | 206 | }, |
207 | 207 | { |
160 | 160 | Using the optional PyGEOS dependency |
161 | 161 | ------------------------------------ |
162 | 162 | |
163 | .. attention:: | |
164 | ||
165 | The upcoming Shapely 2.0 release will absorb all improvements from PyGEOS. | |
166 | If you are considering trying out those improvements, you can also test | |
167 | the prerelease of Shapely instead. | |
168 | See https://shapely.readthedocs.io/en/latest/release/2.x.html#version-2-0-0 | |
169 | for the release notes of Shapely 2.0, and https://github.com/shapely/shapely/discussions/1464 | |
170 | on how to install this and give feedback. | |
171 | ||
163 | 172 | Work is ongoing to improve the performance of GeoPandas. Currently, the |
164 | 173 | fast implementations of basic spatial operations live in the `PyGEOS`_ |
165 | package (but work is under way to contribute those improvements to Shapely). | |
174 | package (but work is under way to contribute those improvements to Shapely, | |
175 | coming to Shapely 2.0). | |
166 | 176 | Starting with GeoPandas 0.8, it is possible to optionally use those |
167 | 177 | experimental speedups by installing PyGEOS. This can be done with conda |
168 | 178 | (using the conda-forge channel) or pip:: |
181 | 191 | - You can still toggle the use of PyGEOS when it is available, by: |
182 | 192 | |
183 | 193 | - Setting an environment variable (``USE_PYGEOS=0/1``). Note this variable |
184 | is only checked at first import of GeoPandas. | |
194 | is only checked at first import of GeoPandas. You can set this environment | |
195 | variable before starting the python process, or in your code right before | |
196 | importing geopandas: | |
197 | ||
198 | .. code-block:: python | |
199 | ||
200 | import os | |
201 | os.environ["USE_PYGEOS"] = "0" | |
202 | import geopandas | |
203 | ||
185 | 204 | - Setting an option: ``geopandas.options.use_pygeos = True/False``. Note, |
186 | 205 | although this variable can be set during an interactive session, it will |
187 | 206 | only work if the GeoDataFrames you use are created (e.g. reading a file |
188 | 207 | with ``read_file``) after changing this value. |
208 | Attention: changing this option will no longer work in all cases when | |
209 | having Shapely >=2.0 installed. In that case, use the environment variable | |
210 | (see option above). | |
189 | 211 | |
190 | 212 | .. warning:: |
191 | 213 |
28 | 28 | |
29 | 29 | SHAPELY_GE_18 = Version(shapely.__version__) >= Version("1.8") |
30 | 30 | SHAPELY_GE_182 = Version(shapely.__version__) >= Version("1.8.2") |
31 | SHAPELY_GE_20 = Version(shapely.__version__) >= Version("2.0") | |
31 | SHAPELY_GE_20 = Version(shapely.__version__) >= Version("2.0.0.dev0") | |
32 | SHAPELY_G_20a1 = Version(shapely.__version__) > Version("2.0a1") | |
32 | 33 | |
33 | 34 | GEOS_GE_390 = shapely.geos.geos_version >= (3, 9, 0) |
34 | 35 | |
35 | 36 | |
36 | 37 | HAS_PYGEOS = None |
37 | 38 | USE_PYGEOS = None |
39 | USE_SHAPELY_20 = None | |
38 | 40 | PYGEOS_SHAPELY_COMPAT = None |
39 | 41 | |
40 | 42 | PYGEOS_GE_09 = None |
73 | 75 | Alternatively, pass a value here to force a True/False value. |
74 | 76 | """ |
75 | 77 | global USE_PYGEOS |
78 | global USE_SHAPELY_20 | |
76 | 79 | global PYGEOS_SHAPELY_COMPAT |
80 | ||
81 | env_use_pygeos = os.getenv("USE_PYGEOS", None) | |
77 | 82 | |
78 | 83 | if val is not None: |
79 | 84 | USE_PYGEOS = bool(val) |
82 | 87 | |
83 | 88 | USE_PYGEOS = HAS_PYGEOS |
84 | 89 | |
85 | env_use_pygeos = os.getenv("USE_PYGEOS", None) | |
86 | 90 | if env_use_pygeos is not None: |
87 | 91 | USE_PYGEOS = bool(int(env_use_pygeos)) |
88 | 92 | |
93 | 97 | |
94 | 98 | # validate the pygeos version |
95 | 99 | if not Version(pygeos.__version__) >= Version("0.8"): |
96 | raise ImportError( | |
97 | "PyGEOS >= 0.8 is required, version {0} is installed".format( | |
98 | pygeos.__version__ | |
100 | if SHAPELY_GE_20: | |
101 | USE_PYGEOS = False | |
102 | warnings.warn( | |
103 | "The PyGEOS version is too old, and Shapely >= 2 is installed, " | |
104 | "thus using Shapely by default and not PyGEOS." | |
99 | 105 | ) |
100 | ) | |
106 | else: | |
107 | raise ImportError( | |
108 | "PyGEOS >= 0.8 is required, version {0} is installed".format( | |
109 | pygeos.__version__ | |
110 | ) | |
111 | ) | |
101 | 112 | |
102 | 113 | # Check whether Shapely and PyGEOS use the same GEOS version. |
103 | 114 | # Based on PyGEOS from_shapely implementation. |
122 | 133 | except ImportError: |
123 | 134 | raise ImportError(INSTALL_PYGEOS_ERROR) |
124 | 135 | |
136 | if USE_PYGEOS and env_use_pygeos is None and SHAPELY_GE_20: | |
137 | warnings.warn( | |
138 | "Shapely 2.0 is installed, but because PyGEOS is also installed, " | |
139 | "GeoPandas will still use PyGEOS by default for now. To force to use and " | |
140 | "test Shapely 2.0, you have to set the environment variable USE_PYGEOS=0. " | |
141 | "You can do this before starting the Python process, or in your code " | |
142 | "before importing geopandas:" | |
143 | "\n\nimport os\nos.environ['USE_PYGEOS'] = '0'\nimport geopandas\n\n" | |
144 | "In a future release, GeoPandas will switch to using Shapely by default. " | |
145 | "If you are using PyGEOS directly (calling PyGEOS functions on geometries " | |
146 | "from GeoPandas), this will then stop working and you are encouraged to " | |
147 | "migrate from PyGEOS to Shapely 2.0 " | |
148 | "(https://shapely.readthedocs.io/en/latest/migration_pygeos.html).", | |
149 | stacklevel=6, | |
150 | ) | |
151 | ||
152 | USE_SHAPELY_20 = (not USE_PYGEOS) and SHAPELY_GE_20 | |
153 | ||
125 | 154 | |
126 | 155 | set_use_pygeos() |
127 | 156 |
8 | 8 | import numpy as np |
9 | 9 | import pandas as pd |
10 | 10 | |
11 | import shapely | |
11 | 12 | import shapely.geometry |
12 | 13 | import shapely.geos |
13 | 14 | import shapely.ops |
14 | 15 | import shapely.wkb |
15 | 16 | import shapely.wkt |
17 | import shapely.validation | |
16 | 18 | |
17 | 19 | from shapely.geometry.base import BaseGeometry |
18 | 20 | |
37 | 39 | "GEOMETRYCOLLECTION": "GeometryCollection", |
38 | 40 | } |
39 | 41 | |
40 | if compat.USE_PYGEOS: | |
41 | type_mapping = {p.value: _names[p.name] for p in pygeos.GeometryType} | |
42 | if compat.USE_SHAPELY_20 or compat.USE_PYGEOS: | |
43 | if compat.USE_SHAPELY_20: | |
44 | type_mapping = {p.value: _names[p.name] for p in shapely.GeometryType} | |
45 | else: | |
46 | type_mapping = {p.value: _names[p.name] for p in pygeos.GeometryType} | |
42 | 47 | geometry_type_ids = list(type_mapping.keys()) |
43 | 48 | geometry_type_values = np.array(list(type_mapping.values()), dtype=object) |
44 | 49 | else: |
67 | 72 | return None |
68 | 73 | |
69 | 74 | if compat.PYGEOS_SHAPELY_COMPAT: |
70 | geom = shapely.geos.lgeos.GEOSGeom_clone(geom._ptr) | |
71 | return shapely.geometry.base.geom_factory(geom) | |
75 | # we can only use this compatible fast path for shapely < 2, because | |
76 | # shapely 2+ doesn't expose clone | |
77 | if not compat.SHAPELY_GE_20: | |
78 | geom = shapely.geos.lgeos.GEOSGeom_clone(geom._ptr) | |
79 | return shapely.geometry.base.geom_factory(geom) | |
72 | 80 | |
73 | 81 | # fallback going through WKB |
74 | 82 | if pygeos.is_empty(geom) and pygeos.get_type_id(geom) == 0: |
160 | 168 | """ |
161 | 169 | Convert a list or array of WKB objects to a np.ndarray[geoms]. |
162 | 170 | """ |
171 | if compat.USE_SHAPELY_20: | |
172 | return shapely.from_wkb(data) | |
163 | 173 | if compat.USE_PYGEOS: |
164 | 174 | return pygeos.from_wkb(data) |
165 | ||
166 | import shapely.wkb | |
167 | 175 | |
168 | 176 | out = [] |
169 | 177 | |
181 | 189 | |
182 | 190 | |
183 | 191 | def to_wkb(data, hex=False, **kwargs): |
184 | if compat.USE_PYGEOS: | |
192 | if compat.USE_SHAPELY_20: | |
193 | return shapely.to_wkb(data, hex=hex, **kwargs) | |
194 | elif compat.USE_PYGEOS: | |
185 | 195 | return pygeos.to_wkb(data, hex=hex, **kwargs) |
186 | 196 | else: |
187 | 197 | if hex: |
195 | 205 | """ |
196 | 206 | Convert a list or array of WKT objects to a np.ndarray[geoms]. |
197 | 207 | """ |
208 | if compat.USE_SHAPELY_20: | |
209 | return shapely.from_wkt(data) | |
198 | 210 | if compat.USE_PYGEOS: |
199 | 211 | return pygeos.from_wkt(data) |
200 | ||
201 | import shapely.wkt | |
202 | 212 | |
203 | 213 | out = [] |
204 | 214 | |
218 | 228 | |
219 | 229 | |
220 | 230 | def to_wkt(data, **kwargs): |
221 | if compat.USE_PYGEOS: | |
231 | if compat.USE_SHAPELY_20: | |
232 | return shapely.to_wkt(data, **kwargs) | |
233 | elif compat.USE_PYGEOS: | |
222 | 234 | return pygeos.to_wkt(data, **kwargs) |
223 | 235 | else: |
224 | 236 | out = [geom.wkt if geom is not None else None for geom in data] |
245 | 257 | if z is not None: |
246 | 258 | z = np.asarray(z, dtype="float64") |
247 | 259 | |
248 | if compat.USE_PYGEOS: | |
260 | if compat.USE_SHAPELY_20: | |
261 | return shapely.points(x, y, z) | |
262 | elif compat.USE_PYGEOS: | |
249 | 263 | return pygeos.points(x, y, z) |
250 | 264 | else: |
251 | 265 | out = _points_from_xy(x, y, z) |
463 | 477 | |
464 | 478 | |
465 | 479 | def is_valid(data): |
466 | if compat.USE_PYGEOS: | |
480 | if compat.USE_SHAPELY_20: | |
481 | return shapely.is_valid(data) | |
482 | elif compat.USE_PYGEOS: | |
467 | 483 | return pygeos.is_valid(data) |
468 | 484 | else: |
469 | 485 | return _unary_op("is_valid", data, null_value=False) |
470 | 486 | |
471 | 487 | |
472 | 488 | def is_empty(data): |
473 | if compat.USE_PYGEOS: | |
489 | if compat.USE_SHAPELY_20: | |
490 | return shapely.is_empty(data) | |
491 | elif compat.USE_PYGEOS: | |
474 | 492 | return pygeos.is_empty(data) |
475 | 493 | else: |
476 | 494 | return _unary_op("is_empty", data, null_value=False) |
477 | 495 | |
478 | 496 | |
479 | 497 | def is_simple(data): |
480 | if compat.USE_PYGEOS: | |
498 | if compat.USE_SHAPELY_20: | |
499 | return shapely.is_simple(data) | |
500 | elif compat.USE_PYGEOS: | |
481 | 501 | return pygeos.is_simple(data) |
482 | 502 | else: |
483 | 503 | return _unary_op("is_simple", data, null_value=False) |
499 | 519 | for geom in data: |
500 | 520 | if geom is None: |
501 | 521 | results.append(False) |
502 | elif geom.type == "Polygon": | |
522 | elif geom.geom_type == "Polygon": | |
503 | 523 | results.append(geom.exterior.is_ring) |
504 | elif geom.type in ["LineString", "LinearRing"]: | |
524 | elif geom.geom_type in ["LineString", "LinearRing"]: | |
505 | 525 | results.append(geom.is_ring) |
506 | 526 | else: |
507 | 527 | results.append(False) |
509 | 529 | |
510 | 530 | |
511 | 531 | def is_closed(data): |
512 | if compat.USE_PYGEOS: | |
532 | if compat.USE_SHAPELY_20: | |
533 | return shapely.is_closed(data) | |
534 | elif compat.USE_PYGEOS: | |
513 | 535 | return pygeos.is_closed(data) |
514 | 536 | else: |
515 | 537 | return _unary_op("is_closed", data, null_value=False) |
516 | 538 | |
517 | 539 | |
518 | 540 | def has_z(data): |
519 | if compat.USE_PYGEOS: | |
541 | if compat.USE_SHAPELY_20: | |
542 | return shapely.has_z(data) | |
543 | elif compat.USE_PYGEOS: | |
520 | 544 | return pygeos.has_z(data) |
521 | 545 | else: |
522 | 546 | return _unary_op("has_z", data, null_value=False) |
523 | 547 | |
524 | 548 | |
525 | 549 | def geom_type(data): |
526 | if compat.USE_PYGEOS: | |
550 | if compat.USE_SHAPELY_20: | |
551 | res = shapely.get_type_id(data) | |
552 | return geometry_type_values[np.searchsorted(geometry_type_ids, res)] | |
553 | elif compat.USE_PYGEOS: | |
527 | 554 | res = pygeos.get_type_id(data) |
528 | 555 | return geometry_type_values[np.searchsorted(geometry_type_ids, res)] |
529 | 556 | else: |
531 | 558 | |
532 | 559 | |
533 | 560 | def area(data): |
534 | if compat.USE_PYGEOS: | |
561 | if compat.USE_SHAPELY_20: | |
562 | return shapely.area(data) | |
563 | elif compat.USE_PYGEOS: | |
535 | 564 | return pygeos.area(data) |
536 | 565 | else: |
537 | 566 | return _unary_op("area", data, null_value=np.nan) |
538 | 567 | |
539 | 568 | |
540 | 569 | def length(data): |
541 | if compat.USE_PYGEOS: | |
570 | if compat.USE_SHAPELY_20: | |
571 | return shapely.length(data) | |
572 | elif compat.USE_PYGEOS: | |
542 | 573 | return pygeos.length(data) |
543 | 574 | else: |
544 | 575 | return _unary_op("length", data, null_value=np.nan) |
560 | 591 | |
561 | 592 | |
562 | 593 | def boundary(data): |
563 | if compat.USE_PYGEOS: | |
594 | if compat.USE_SHAPELY_20: | |
595 | return shapely.boundary(data) | |
596 | elif compat.USE_PYGEOS: | |
564 | 597 | return pygeos.boundary(data) |
565 | 598 | else: |
566 | 599 | return _unary_geo("boundary", data) |
567 | 600 | |
568 | 601 | |
569 | 602 | def centroid(data): |
570 | if compat.USE_PYGEOS: | |
603 | if compat.USE_SHAPELY_20: | |
604 | return shapely.centroid(data) | |
605 | elif compat.USE_PYGEOS: | |
571 | 606 | return pygeos.centroid(data) |
572 | 607 | else: |
573 | 608 | return _unary_geo("centroid", data) |
574 | 609 | |
575 | 610 | |
576 | 611 | def convex_hull(data): |
577 | if compat.USE_PYGEOS: | |
612 | if compat.USE_SHAPELY_20: | |
613 | return shapely.convex_hull(data) | |
614 | elif compat.USE_PYGEOS: | |
578 | 615 | return pygeos.convex_hull(data) |
579 | 616 | else: |
580 | 617 | return _unary_geo("convex_hull", data) |
581 | 618 | |
582 | 619 | |
583 | 620 | def envelope(data): |
584 | if compat.USE_PYGEOS: | |
621 | if compat.USE_SHAPELY_20: | |
622 | return shapely.envelope(data) | |
623 | elif compat.USE_PYGEOS: | |
585 | 624 | return pygeos.envelope(data) |
586 | 625 | else: |
587 | 626 | return _unary_geo("envelope", data) |
588 | 627 | |
589 | 628 | |
590 | 629 | def exterior(data): |
591 | if compat.USE_PYGEOS: | |
630 | if compat.USE_SHAPELY_20: | |
631 | return shapely.get_exterior_ring(data) | |
632 | elif compat.USE_PYGEOS: | |
592 | 633 | return pygeos.get_exterior_ring(data) |
593 | 634 | else: |
594 | 635 | return _unary_geo("exterior", data) |
638 | 679 | |
639 | 680 | |
640 | 681 | def covers(data, other): |
641 | if compat.USE_PYGEOS: | |
682 | if compat.USE_SHAPELY_20: | |
683 | return shapely.covers(data, other) | |
684 | elif compat.USE_PYGEOS: | |
642 | 685 | return _binary_method("covers", data, other) |
643 | 686 | else: |
644 | 687 | return _binary_predicate("covers", data, other) |
645 | 688 | |
646 | 689 | |
647 | 690 | def covered_by(data, other): |
648 | if compat.USE_PYGEOS: | |
691 | if compat.USE_SHAPELY_20: | |
692 | return shapely.covered_by(data, other) | |
693 | elif compat.USE_PYGEOS: | |
649 | 694 | return _binary_method("covered_by", data, other) |
650 | 695 | else: |
651 | 696 | raise NotImplementedError( |
654 | 699 | |
655 | 700 | |
656 | 701 | def contains(data, other): |
657 | if compat.USE_PYGEOS: | |
702 | if compat.USE_SHAPELY_20: | |
703 | return shapely.contains(data, other) | |
704 | elif compat.USE_PYGEOS: | |
658 | 705 | return _binary_method("contains", data, other) |
659 | 706 | else: |
660 | 707 | return _binary_predicate("contains", data, other) |
661 | 708 | |
662 | 709 | |
663 | 710 | def crosses(data, other): |
664 | if compat.USE_PYGEOS: | |
711 | if compat.USE_SHAPELY_20: | |
712 | return shapely.crosses(data, other) | |
713 | elif compat.USE_PYGEOS: | |
665 | 714 | return _binary_method("crosses", data, other) |
666 | 715 | else: |
667 | 716 | return _binary_predicate("crosses", data, other) |
668 | 717 | |
669 | 718 | |
670 | 719 | def disjoint(data, other): |
671 | if compat.USE_PYGEOS: | |
720 | if compat.USE_SHAPELY_20: | |
721 | return shapely.disjoint(data, other) | |
722 | elif compat.USE_PYGEOS: | |
672 | 723 | return _binary_method("disjoint", data, other) |
673 | 724 | else: |
674 | 725 | return _binary_predicate("disjoint", data, other) |
675 | 726 | |
676 | 727 | |
677 | 728 | def equals(data, other): |
678 | if compat.USE_PYGEOS: | |
729 | if compat.USE_SHAPELY_20: | |
730 | return shapely.equals(data, other) | |
731 | elif compat.USE_PYGEOS: | |
679 | 732 | return _binary_method("equals", data, other) |
680 | 733 | else: |
681 | 734 | return _binary_predicate("equals", data, other) |
682 | 735 | |
683 | 736 | |
684 | 737 | def intersects(data, other): |
685 | if compat.USE_PYGEOS: | |
738 | if compat.USE_SHAPELY_20: | |
739 | return shapely.intersects(data, other) | |
740 | elif compat.USE_PYGEOS: | |
686 | 741 | return _binary_method("intersects", data, other) |
687 | 742 | else: |
688 | 743 | return _binary_predicate("intersects", data, other) |
689 | 744 | |
690 | 745 | |
691 | 746 | def overlaps(data, other): |
692 | if compat.USE_PYGEOS: | |
747 | if compat.USE_SHAPELY_20: | |
748 | return shapely.overlaps(data, other) | |
749 | elif compat.USE_PYGEOS: | |
693 | 750 | return _binary_method("overlaps", data, other) |
694 | 751 | else: |
695 | 752 | return _binary_predicate("overlaps", data, other) |
696 | 753 | |
697 | 754 | |
698 | 755 | def touches(data, other): |
699 | if compat.USE_PYGEOS: | |
756 | if compat.USE_SHAPELY_20: | |
757 | return shapely.touches(data, other) | |
758 | elif compat.USE_PYGEOS: | |
700 | 759 | return _binary_method("touches", data, other) |
701 | 760 | else: |
702 | 761 | return _binary_predicate("touches", data, other) |
703 | 762 | |
704 | 763 | |
705 | 764 | def within(data, other): |
706 | if compat.USE_PYGEOS: | |
765 | if compat.USE_SHAPELY_20: | |
766 | return shapely.within(data, other) | |
767 | elif compat.USE_PYGEOS: | |
707 | 768 | return _binary_method("within", data, other) |
708 | 769 | else: |
709 | 770 | return _binary_predicate("within", data, other) |
710 | 771 | |
711 | 772 | |
712 | 773 | def equals_exact(data, other, tolerance): |
713 | if compat.USE_PYGEOS: | |
774 | if compat.USE_SHAPELY_20: | |
775 | return shapely.equals_exact(data, other, tolerance=tolerance) | |
776 | elif compat.USE_PYGEOS: | |
714 | 777 | return _binary_method("equals_exact", data, other, tolerance=tolerance) |
715 | 778 | else: |
716 | 779 | return _binary_predicate("equals_exact", data, other, tolerance=tolerance) |
717 | 780 | |
718 | 781 | |
719 | 782 | def almost_equals(self, other, decimal): |
720 | if compat.USE_PYGEOS: | |
783 | if compat.USE_PYGEOS or compat.USE_SHAPELY_20: | |
721 | 784 | return self.equals_exact(other, 0.5 * 10 ** (-decimal)) |
722 | 785 | else: |
723 | 786 | return _binary_predicate("almost_equals", self, other, decimal=decimal) |
733 | 796 | return pygeos.clip_by_rect(data, xmin, ymin, xmax, ymax) |
734 | 797 | else: |
735 | 798 | clipped_geometries = np.empty(len(data), dtype=object) |
736 | clipped_geometries[:] = [ | |
737 | shapely.ops.clip_by_rect(s, xmin, ymin, xmax, ymax) | |
738 | if s is not None | |
739 | else None | |
740 | for s in data | |
741 | ] | |
799 | with compat.ignore_shapely2_warnings(): | |
800 | clipped_geometries[:] = [ | |
801 | shapely.ops.clip_by_rect(s, xmin, ymin, xmax, ymax) | |
802 | if s is not None | |
803 | else None | |
804 | for s in data | |
805 | ] | |
742 | 806 | return clipped_geometries |
743 | 807 | |
744 | 808 | |
745 | 809 | def difference(data, other): |
746 | if compat.USE_PYGEOS: | |
810 | if compat.USE_SHAPELY_20: | |
811 | return shapely.difference(data, other) | |
812 | elif compat.USE_PYGEOS: | |
747 | 813 | return _binary_method("difference", data, other) |
748 | 814 | else: |
749 | 815 | return _binary_geo("difference", data, other) |
750 | 816 | |
751 | 817 | |
752 | 818 | def intersection(data, other): |
753 | if compat.USE_PYGEOS: | |
819 | if compat.USE_SHAPELY_20: | |
820 | return shapely.intersection(data, other) | |
821 | elif compat.USE_PYGEOS: | |
754 | 822 | return _binary_method("intersection", data, other) |
755 | 823 | else: |
756 | 824 | return _binary_geo("intersection", data, other) |
757 | 825 | |
758 | 826 | |
759 | 827 | def symmetric_difference(data, other): |
760 | if compat.USE_PYGEOS: | |
828 | if compat.USE_SHAPELY_20: | |
829 | return shapely.symmetric_difference(data, other) | |
830 | elif compat.USE_PYGEOS: | |
761 | 831 | return _binary_method("symmetric_difference", data, other) |
762 | 832 | else: |
763 | 833 | return _binary_geo("symmetric_difference", data, other) |
764 | 834 | |
765 | 835 | |
766 | 836 | def union(data, other): |
767 | if compat.USE_PYGEOS: | |
837 | if compat.USE_SHAPELY_20: | |
838 | return shapely.union(data, other) | |
839 | elif compat.USE_PYGEOS: | |
768 | 840 | return _binary_method("union", data, other) |
769 | 841 | else: |
770 | 842 | return _binary_geo("union", data, other) |
776 | 848 | |
777 | 849 | |
778 | 850 | def distance(data, other): |
779 | if compat.USE_PYGEOS: | |
851 | if compat.USE_SHAPELY_20: | |
852 | return shapely.distance(data, other) | |
853 | elif compat.USE_PYGEOS: | |
780 | 854 | return _binary_method("distance", data, other) |
781 | 855 | else: |
782 | 856 | return _binary_op_float("distance", data, other) |
783 | 857 | |
784 | 858 | |
785 | 859 | def buffer(data, distance, resolution=16, **kwargs): |
786 | if compat.USE_PYGEOS: | |
860 | if compat.USE_SHAPELY_20: | |
861 | if compat.SHAPELY_G_20a1: | |
862 | return shapely.buffer(data, distance, quad_segs=resolution, **kwargs) | |
863 | else: | |
864 | # TODO: temporary keep this (so geopandas works with latest released | |
865 | # shapely, currently alpha1) until shapely beta1 is out | |
866 | return shapely.buffer(data, distance, quadsegs=resolution, **kwargs) | |
867 | elif compat.USE_PYGEOS: | |
787 | 868 | return pygeos.buffer(data, distance, quadsegs=resolution, **kwargs) |
788 | 869 | else: |
789 | 870 | out = np.empty(len(data), dtype=object) |
814 | 895 | |
815 | 896 | |
816 | 897 | def interpolate(data, distance, normalized=False): |
817 | if compat.USE_PYGEOS: | |
898 | if compat.USE_SHAPELY_20: | |
899 | return shapely.line_interpolate_point(data, distance, normalized=normalized) | |
900 | elif compat.USE_PYGEOS: | |
818 | 901 | try: |
819 | 902 | return pygeos.line_interpolate_point(data, distance, normalized=normalized) |
820 | 903 | except TypeError: # support for pygeos<0.9 |
842 | 925 | |
843 | 926 | |
844 | 927 | def simplify(data, tolerance, preserve_topology=True): |
845 | if compat.USE_PYGEOS: | |
928 | if compat.USE_SHAPELY_20: | |
929 | return shapely.simplify(data, tolerance, preserve_topology=preserve_topology) | |
930 | elif compat.USE_PYGEOS: | |
846 | 931 | # preserve_topology has different default as pygeos! |
847 | 932 | return pygeos.simplify(data, tolerance, preserve_topology=preserve_topology) |
848 | 933 | else: |
873 | 958 | |
874 | 959 | |
875 | 960 | def normalize(data): |
876 | if compat.USE_PYGEOS: | |
961 | if compat.USE_SHAPELY_20: | |
962 | return shapely.normalize(data) | |
963 | elif compat.USE_PYGEOS: | |
877 | 964 | return pygeos.normalize(data) |
965 | elif compat.SHAPELY_GE_18: | |
966 | out = np.empty(len(data), dtype=object) | |
967 | with compat.ignore_shapely2_warnings(): | |
968 | out[:] = [geom.normalize() if geom is not None else None for geom in data] | |
878 | 969 | else: |
879 | 970 | out = np.empty(len(data), dtype=object) |
880 | 971 | with compat.ignore_shapely2_warnings(): |
881 | 972 | out[:] = [ |
882 | 973 | _shapely_normalize(geom) if geom is not None else None for geom in data |
883 | 974 | ] |
884 | return out | |
975 | return out | |
976 | ||
977 | ||
978 | def make_valid(data): | |
979 | if compat.USE_SHAPELY_20: | |
980 | return shapely.make_valid(data) | |
981 | elif compat.USE_PYGEOS: | |
982 | return pygeos.make_valid(data) | |
983 | elif not compat.SHAPELY_GE_18: | |
984 | raise NotImplementedError( | |
985 | f"shapely >= 1.8 or PyGEOS is required, " | |
986 | f"version {shapely.__version__} is installed" | |
987 | ) | |
988 | else: | |
989 | out = np.empty(len(data), dtype=object) | |
990 | with compat.ignore_shapely2_warnings(): | |
991 | out[:] = [ | |
992 | shapely.validation.make_valid(geom) if geom is not None else None | |
993 | for geom in data | |
994 | ] | |
995 | return out | |
885 | 996 | |
886 | 997 | |
887 | 998 | def project(data, other, normalized=False): |
888 | if compat.USE_PYGEOS: | |
999 | if compat.USE_SHAPELY_20: | |
1000 | return shapely.line_locate_point(data, other, normalized=normalized) | |
1001 | elif compat.USE_PYGEOS: | |
889 | 1002 | try: |
890 | 1003 | return pygeos.line_locate_point(data, other, normalized=normalized) |
891 | 1004 | except TypeError: # support for pygeos<0.9 |
895 | 1008 | |
896 | 1009 | |
897 | 1010 | def relate(data, other): |
1011 | if compat.USE_SHAPELY_20: | |
1012 | return shapely.relate(data, other) | |
898 | 1013 | data = to_shapely(data) |
899 | 1014 | if isinstance(other, np.ndarray): |
900 | 1015 | other = to_shapely(other) |
902 | 1017 | |
903 | 1018 | |
904 | 1019 | def unary_union(data): |
905 | if compat.USE_PYGEOS: | |
1020 | if compat.USE_SHAPELY_20: | |
1021 | data = shapely.union_all(data) | |
1022 | if data is None: # shapely 2.0a1 | |
1023 | return None | |
1024 | if data.is_empty: # shapely 2.0 | |
1025 | return None | |
1026 | else: | |
1027 | return data | |
1028 | elif compat.USE_PYGEOS: | |
906 | 1029 | return _pygeos_to_shapely(pygeos.union_all(data)) |
907 | 1030 | else: |
908 | 1031 | data = [g for g in data if g is not None] |
918 | 1041 | |
919 | 1042 | |
920 | 1043 | def get_x(data): |
921 | if compat.USE_PYGEOS: | |
1044 | if compat.USE_SHAPELY_20: | |
1045 | return shapely.get_x(data) | |
1046 | elif compat.USE_PYGEOS: | |
922 | 1047 | return pygeos.get_x(data) |
923 | 1048 | else: |
924 | 1049 | return _unary_op("x", data, null_value=np.nan) |
925 | 1050 | |
926 | 1051 | |
927 | 1052 | def get_y(data): |
928 | if compat.USE_PYGEOS: | |
1053 | if compat.USE_SHAPELY_20: | |
1054 | return shapely.get_y(data) | |
1055 | elif compat.USE_PYGEOS: | |
929 | 1056 | return pygeos.get_y(data) |
930 | 1057 | else: |
931 | 1058 | return _unary_op("y", data, null_value=np.nan) |
932 | 1059 | |
933 | 1060 | |
934 | 1061 | def get_z(data): |
935 | if compat.USE_PYGEOS: | |
1062 | if compat.USE_SHAPELY_20: | |
1063 | return shapely.get_z(data) | |
1064 | elif compat.USE_PYGEOS: | |
936 | 1065 | return pygeos.get_z(data) |
937 | 1066 | else: |
938 | 1067 | data = [geom.z if geom.has_z else np.nan for geom in data] |
940 | 1069 | |
941 | 1070 | |
942 | 1071 | def bounds(data): |
943 | if compat.USE_PYGEOS: | |
1072 | if compat.USE_SHAPELY_20: | |
1073 | return shapely.bounds(data) | |
1074 | elif compat.USE_PYGEOS: | |
944 | 1075 | return pygeos.bounds(data) |
945 | 1076 | # ensure that for empty arrays, the result has the correct shape |
946 | 1077 | if len(data) == 0: |
964 | 1095 | |
965 | 1096 | |
966 | 1097 | def transform(data, func): |
1098 | if compat.USE_SHAPELY_20: | |
1099 | coords = shapely.get_coordinates(data) | |
1100 | new_coords = func(coords[:, 0], coords[:, 1]) | |
1101 | result = shapely.set_coordinates(data.copy(), np.array(new_coords).T) | |
1102 | return result | |
967 | 1103 | if compat.USE_PYGEOS: |
968 | 1104 | coords = pygeos.get_coordinates(data) |
969 | 1105 | new_coords = func(coords[:, 0], coords[:, 1]) |
22 | 22 | # setup.py/versioneer.py will grep for the variable names, so they must |
23 | 23 | # each be defined on a line of their own. _version.py will just call |
24 | 24 | # get_keywords(). |
25 | git_refnames = " (HEAD -> main, tag: v0.11.1)" | |
26 | git_full = "1f7160050b31e0f39410c7f4163a2cbb2648ff62" | |
27 | git_date = "2022-07-24 13:17:38 +0200" | |
25 | git_refnames = " (HEAD -> main, tag: v0.12.0)" | |
26 | git_full = "31f8e6af3b6ec57dc50633e2e1cdfb3a6e932a95" | |
27 | git_date = "2022-10-24 09:06:05 +0200" | |
28 | 28 | keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} |
29 | 29 | return keywords |
30 | 30 |
107 | 107 | """ |
108 | 108 | Convert internal representation (PyGEOS or Shapely) to external Shapely object. |
109 | 109 | """ |
110 | if not compat.USE_PYGEOS: | |
110 | if compat.USE_SHAPELY_20: | |
111 | return geom | |
112 | elif not compat.USE_PYGEOS: | |
111 | 113 | return geom |
112 | 114 | else: |
113 | 115 | return vectorized._pygeos_to_shapely(geom) |
117 | 119 | """ |
118 | 120 | Convert external Shapely object to internal representation (PyGEOS or Shapely). |
119 | 121 | """ |
120 | if not compat.USE_PYGEOS: | |
122 | if compat.USE_SHAPELY_20: | |
123 | return geom | |
124 | elif not compat.USE_PYGEOS: | |
121 | 125 | return geom |
122 | 126 | else: |
123 | 127 | return vectorized._shapely_to_pygeos(geom) |
409 | 413 | # ) |
410 | 414 | |
411 | 415 | def __getstate__(self): |
412 | if compat.USE_PYGEOS: | |
416 | if compat.USE_SHAPELY_20: | |
417 | return (shapely.to_wkb(self.data), self._crs) | |
418 | elif compat.USE_PYGEOS: | |
413 | 419 | return (pygeos.to_wkb(self.data), self._crs) |
414 | 420 | else: |
415 | 421 | return self.__dict__ |
503 | 509 | |
504 | 510 | def representative_point(self): |
505 | 511 | return GeometryArray(vectorized.representative_point(self.data), crs=self.crs) |
512 | ||
513 | def normalize(self): | |
514 | return GeometryArray(vectorized.normalize(self.data), crs=self.crs) | |
515 | ||
516 | def make_valid(self): | |
517 | return GeometryArray(vectorized.make_valid(self.data), crs=self.crs) | |
506 | 518 | |
507 | 519 | # |
508 | 520 | # Binary predicates |
1064 | 1076 | """ |
1065 | 1077 | Boolean NumPy array indicating if each value is missing |
1066 | 1078 | """ |
1067 | if compat.USE_PYGEOS: | |
1079 | if compat.USE_SHAPELY_20: | |
1080 | return shapely.is_missing(self.data) | |
1081 | elif compat.USE_PYGEOS: | |
1068 | 1082 | return pygeos.is_missing(self.data) |
1069 | 1083 | else: |
1070 | 1084 | return np.array([g is None for g in self.data], dtype="bool") |
1305 | 1319 | ExtensionArray |
1306 | 1320 | """ |
1307 | 1321 | data = np.concatenate([ga.data for ga in to_concat]) |
1308 | return GeometryArray(data, crs=to_concat[0].crs) | |
1322 | return GeometryArray(data, crs=_get_common_crs(to_concat)) | |
1309 | 1323 | |
1310 | 1324 | def _reduce(self, name, skipna=True, **kwargs): |
1311 | 1325 | # including the base class version here (that raises by default) |
1376 | 1390 | else: |
1377 | 1391 | return False |
1378 | 1392 | return (self == item).any() |
1393 | ||
1394 | ||
1395 | def _get_common_crs(arr_seq): | |
1396 | ||
1397 | crs_set = {arr.crs for arr in arr_seq} | |
1398 | crs_not_none = [crs for crs in crs_set if crs is not None] | |
1399 | names = [crs.name for crs in crs_not_none] | |
1400 | ||
1401 | if len(crs_not_none) == 0: | |
1402 | return None | |
1403 | if len(crs_not_none) == 1: | |
1404 | if len(crs_set) != 1: | |
1405 | warnings.warn( | |
1406 | "CRS not set for some of the concatenation inputs. " | |
1407 | f"Setting output's CRS as {names[0]} " | |
1408 | "(the single non-null crs provided)." | |
1409 | ) | |
1410 | return crs_not_none[0] | |
1411 | ||
1412 | raise ValueError( | |
1413 | f"Cannot determine common CRS for concatenation inputs, got {names}. " | |
1414 | "Use `to_crs()` to transform geometries to the same CRS before merging." | |
1415 | ) |
423 | 423 | @property |
424 | 424 | def boundary(self): |
425 | 425 | """Returns a ``GeoSeries`` of lower dimensional objects representing |
426 | each geometries's set-theoretic `boundary`. | |
426 | each geometry's set-theoretic `boundary`. | |
427 | 427 | |
428 | 428 | Examples |
429 | 429 | -------- |
690 | 690 | GeoSeries.centroid : geometric centroid |
691 | 691 | """ |
692 | 692 | return _delegate_geo_method("representative_point", self) |
693 | ||
694 | def normalize(self): | |
695 | """Returns a ``GeoSeries`` of normalized geometries to normal form (or canonical form). | |
696 | ||
697 | This method orders the coordinates, rings of a polygon and parts of | |
698 | multi geometries consistently. Typically useful for testing purposes | |
699 | (for example in combination with `equals_exact`). | |
700 | ||
701 | Examples | |
702 | -------- | |
703 | ||
704 | >>> from shapely.geometry import Polygon, LineString, Point | |
705 | >>> s = geopandas.GeoSeries( | |
706 | ... [ | |
707 | ... Polygon([(0, 0), (1, 1), (0, 1)]), | |
708 | ... LineString([(0, 0), (1, 1), (1, 0)]), | |
709 | ... Point(0, 0), | |
710 | ... ], | |
711 | ... crs='EPSG:3857' | |
712 | ... ) | |
713 | >>> s | |
714 | 0 POLYGON ((0.000 0.000, 1.000 1.000, 0.000 1.00... | |
715 | 1 LINESTRING (0.000 0.000, 1.000 1.000, 1.000 0.... | |
716 | 2 POINT (0.000 0.000) | |
717 | dtype: geometry | |
718 | ||
719 | >>> s.normalize() | |
720 | 0 POLYGON ((0.000 0.000, 0.000 1.000, 1.000 1.00... | |
721 | 1 LINESTRING (0.000 0.000, 1.000 1.000, 1.000 0.... | |
722 | 2 POINT (0.000 0.000) | |
723 | dtype: geometry | |
724 | """ | |
725 | return _delegate_geo_method("normalize", self) | |
726 | ||
727 | def make_valid(self): | |
728 | """ | |
729 | Repairs invalid geometries. | |
730 | ||
731 | Returns a ``GeoSeries`` with valid geometries. | |
732 | If the input geometry is already valid, then it will be preserved. | |
733 | In many cases, in order to create a valid geometry, the input | |
734 | geometry must be split into multiple parts or multiple geometries. | |
735 | If the geometry must be split into multiple parts of the same type | |
736 | to be made valid, then a multi-part geometry will be returned | |
737 | (e.g. a MultiPolygon). | |
738 | If the geometry must be split into multiple parts of different types | |
739 | to be made valid, then a GeometryCollection will be returned. | |
740 | ||
741 | Examples | |
742 | -------- | |
743 | >>> from shapely.geometry import MultiPolygon, Polygon, LineString, Point | |
744 | >>> s = geopandas.GeoSeries( | |
745 | ... [ | |
746 | ... Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]), | |
747 | ... Polygon([(0, 2), (0, 1), (2, 0), (0, 0), (0, 2)]), | |
748 | ... LineString([(0, 0), (1, 1), (1, 0)]), | |
749 | ... ], | |
750 | ... crs='EPSG:3857', | |
751 | ... ) | |
752 | >>> s | |
753 | 0 POLYGON ((0.000 0.000, 0.000 2.000, 1.000 1.00... | |
754 | 1 POLYGON ((0.000 2.000, 0.000 1.000, 2.000 0.00... | |
755 | 2 LINESTRING (0.000 0.000, 1.000 1.000, 1.000 0.... | |
756 | dtype: geometry | |
757 | ||
758 | >>> s.make_valid() | |
759 | 0 MULTIPOLYGON (((1.000 1.000, 0.000 0.000, 0.00... | |
760 | 1 GEOMETRYCOLLECTION (POLYGON ((2.000 0.000, 0.0... | |
761 | 2 LINESTRING (0.000 0.000, 1.000 1.000, 1.000 0.... | |
762 | dtype: geometry | |
763 | """ | |
764 | return _delegate_geo_method("make_valid", self) | |
693 | 765 | |
694 | 766 | # |
695 | 767 | # Reduction operations that return a Shapely geometry |
2626 | 2698 | 0 2.0 1.0 2.0 1.0 |
2627 | 2699 | 1 0.0 0.0 1.0 1.0 |
2628 | 2700 | 2 0.0 1.0 1.0 2.0 |
2701 | ||
2702 | You can assign the bounds to the ``GeoDataFrame`` as: | |
2703 | ||
2704 | >>> import pandas as pd | |
2705 | >>> gdf = pd.concat([gdf, gdf.bounds], axis=1) | |
2706 | >>> gdf | |
2707 | geometry minx miny maxx maxy | |
2708 | 0 POINT (2.00000 1.00000) 2.0 1.0 2.0 1.0 | |
2709 | 1 POLYGON ((0.00000 0.00000, 1.00000 1.00000, 1.... 0.0 0.0 1.0 1.0 | |
2710 | 2 LINESTRING (0.00000 1.00000, 1.00000 2.00000) 0.0 1.0 1.0 2.0 | |
2629 | 2711 | """ |
2630 | 2712 | bounds = GeometryArray(self.geometry.values).bounds |
2631 | 2713 | return DataFrame( |
0 | 0 | # Datasets included with geopandas |
1 | 1 | |
2 | 2 | - `'nybb'`: Borough boundaries of New York City, data provided Department of City Planning (DCP), https://data.cityofnewyork.us/City-Government/Borough-Boundaries/tqmj-j8zm |
3 | - `'naturalearth_cities'`: capital cities, based on http://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-populated-places/ | |
3 | - `'naturalearth_cities'`: capital cities, based on http://www.naturalearthdata.com/downloads/10m-cultural-vectors/110m-populated-places/ | |
4 | 4 | - `'naturalearth_lowres'`: country boundaries, based on http://www.naturalearthdata.com/downloads/110m-cultural-vectors/110m-admin-0-countries/ |
5 | 5 | |
6 | 6 |
Binary diff not shown
0 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]⏎ | |
0 | GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]⏎ |
Binary diff not shown
Binary diff not shown
0 | 0 | """ |
1 | Script that generates the included dataset 'naturalearth_lowres.shp'. | |
1 | Script that generates the included dataset 'naturalearth_lowres.shp' | |
2 | and 'naturalearth_cities.shp'. | |
2 | 3 | |
3 | 4 | Raw data: https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/ne_110m_admin_0_countries.zip |
4 | Current version used: version 5.0.1 | |
5 | Current version used: see code | |
5 | 6 | """ # noqa (E501 link is longer than max line length) |
6 | 7 | |
7 | 8 | import geopandas as gpd |
9 | import requests | |
10 | from pathlib import Path | |
11 | from zipfile import ZipFile | |
12 | import tempfile | |
8 | 13 | |
9 | # assumes zipfile from naturalearthdata was downloaded to current directory | |
10 | world_raw = gpd.read_file("zip://./ne_110m_admin_0_countries.zip") | |
14 | version = "latest" | |
15 | urlbase = "https://www.naturalearthdata.com/" | |
16 | urlbase += "http//www.naturalearthdata.com/download/110m/cultural/" | |
11 | 17 | |
12 | # not ideal - fix some country codes | |
13 | mask = world_raw["ISO_A3"].eq("-99") & world_raw["TYPE"].isin( | |
14 | ["Sovereign country", "Country"] | |
15 | ) | |
16 | world_raw.loc[mask, "ISO_A3"] = world_raw.loc[mask, "ADM0_A3"] | |
17 | 18 | |
18 | # subsets columns of interest for geopandas examples | |
19 | world_df = world_raw[ | |
20 | ["POP_EST", "CONTINENT", "NAME", "ISO_A3", "GDP_MD", "geometry"] | |
21 | ].rename( | |
22 | columns={"GDP_MD": "GDP_MD_EST"} | |
23 | ) # column has changed name... | |
24 | world_df.columns = world_df.columns.str.lower() | |
19 | def countries_override(world_raw): | |
20 | # not ideal - fix some country codes | |
21 | mask = world_raw["ISO_A3"].eq("-99") & world_raw["TYPE"].isin( | |
22 | ["Sovereign country", "Country"] | |
23 | ) | |
24 | world_raw.loc[mask, "ISO_A3"] = world_raw.loc[mask, "ADM0_A3"] | |
25 | # backwards compatibility | |
26 | return world_raw.rename(columns={"GDP_MD": "GDP_MD_EST"}) | |
25 | 27 | |
26 | world_df.to_file( | |
27 | driver="ESRI Shapefile", filename="./naturalearth_lowres/naturalearth_lowres.shp" | |
28 | ) | |
28 | ||
29 | # any change between versions? | |
30 | def df_same(new, old, dataset, log): | |
31 | assert (new.columns == old.columns).all(), "columns should be the same" | |
32 | if new.shape != old.shape: | |
33 | dfc = old.merge(new, on="name", how="outer", suffixes=("_old", "_new")).loc[ | |
34 | lambda d: d.isna().any(axis=1) | |
35 | ] | |
36 | log.append(f"### {dataset} row count changed ###\n{dfc.to_markdown()}") | |
37 | return False | |
38 | dfc = new.compare(old) | |
39 | if len(dfc) > 0: | |
40 | log.append(f"### {dataset} data changed ###\n{dfc.to_markdown()}") | |
41 | return len(dfc) == 0 | |
42 | ||
43 | ||
44 | config = [ | |
45 | { | |
46 | "file": "ne_110m_populated_places.zip", | |
47 | "cols": ["NAME", "geometry"], | |
48 | "current": gpd.datasets.get_path("naturalearth_cities"), | |
49 | }, | |
50 | { | |
51 | "file": "ne_110m_admin_0_countries.zip", | |
52 | "cols": ["POP_EST", "CONTINENT", "NAME", "ISO_A3", "GDP_MD_EST", "geometry"], | |
53 | "override": countries_override, | |
54 | "current": gpd.datasets.get_path("naturalearth_lowres"), | |
55 | }, | |
56 | ] | |
57 | ||
58 | downloads = {} | |
59 | log = [] | |
60 | for dl in config: | |
61 | with tempfile.TemporaryDirectory() as tmpdirname: | |
62 | url = urlbase + dl["file"] | |
63 | r = requests.get( | |
64 | url, | |
65 | stream=True, | |
66 | headers={"User-Agent": "XY"}, | |
67 | params=None if version == "latest" else {"version": version}, | |
68 | ) | |
69 | assert ( | |
70 | r.status_code == 200 | |
71 | ), f"version: {version} does not exist. status: {r.status_code}" | |
72 | ||
73 | f = Path(tmpdirname).joinpath(dl["file"]) | |
74 | with open(f, "wb") as fd: | |
75 | for chunk in r.iter_content(chunk_size=128): | |
76 | fd.write(chunk) | |
77 | # extract the natural earth version | |
78 | z = ZipFile(f) | |
79 | version_f = [i for i in z.infolist() if "VERSION" in i.filename] | |
80 | assert len(version_f) == 1, "failed to find VERSION file" | |
81 | with open(z.extract(version_f[0], Path(tmpdirname).joinpath("v.txt"))) as f_: | |
82 | dl_version = f_.read().strip() | |
83 | ||
84 | # extract geodataframe from zip | |
85 | gdf = gpd.read_file(f) | |
86 | # maintain structure that geopandas distributes | |
87 | if "override" in dl.keys(): | |
88 | gdf = dl["override"](gdf) | |
89 | gdf = gdf.loc[:, dl["cols"]] | |
90 | gdf = gdf.rename(columns={c: c.lower() for c in gdf.columns}) | |
91 | # get changes between current version and new version | |
92 | if not df_same(gdf, gpd.read_file(dl["current"]), dl["file"], log): | |
93 | downloads[dl["file"]] = gdf | |
94 | ||
95 | ||
96 | # create change log that can be pasted into PR | |
97 | with open(f"CHANGE_{dl_version}.md", "w") as f: | |
98 | f.write("\n\n".join(log)) | |
99 | ||
100 | # save downloaded geodataframe to appropriate place | |
101 | for k, gdf_ in downloads.items(): | |
102 | f = [Path(c["current"]) for c in config if c["file"] == k][0] | |
103 | gdf_.to_file(driver="ESRI Shapefile", filename=Path(f.parent.name).joinpath(f.name)) |
Binary diff not shown
3 | 3 | from shapely.geometry import LineString |
4 | 4 | import numpy as np |
5 | 5 | import pandas as pd |
6 | ||
7 | from packaging.version import Version | |
6 | 8 | |
7 | 9 | _MAP_KWARGS = [ |
8 | 10 | "location", |
261 | 263 | |
262 | 264 | >>> df.explore("pop_est", cmap="Blues") # doctest: +SKIP |
263 | 265 | """ |
266 | ||
267 | def _colormap_helper(_cmap, n_resample=None, idx=None): | |
268 | """Helper for MPL deprecation - GH#2596""" | |
269 | if not n_resample: | |
270 | return cm.get_cmap(_cmap) | |
271 | else: | |
272 | if MPL_361: | |
273 | return cm.get_cmap(_cmap).resampled(n_resample)(idx) | |
274 | else: | |
275 | return cm.get_cmap(_cmap, n_resample)(idx) | |
276 | ||
264 | 277 | try: |
265 | 278 | import branca as bc |
266 | 279 | import folium |
267 | import matplotlib.cm as cm | |
280 | import matplotlib | |
268 | 281 | import matplotlib.colors as colors |
269 | 282 | import matplotlib.pyplot as plt |
270 | 283 | from mapclassify import classify |
284 | ||
285 | # isolate MPL version - GH#2596 | |
286 | MPL_361 = Version(matplotlib.__version__) >= Version("3.6.1") | |
287 | if MPL_361: | |
288 | from matplotlib import colormaps as cm | |
289 | else: | |
290 | import matplotlib.cm as cm | |
291 | ||
271 | 292 | except (ImportError, ModuleNotFoundError): |
272 | 293 | raise ImportError( |
273 | 294 | "The 'folium', 'matplotlib' and 'mapclassify' packages are required for " |
377 | 398 | ) |
378 | 399 | categorical = True |
379 | 400 | elif ( |
380 | gdf[column].dtype is np.dtype("O") | |
381 | or gdf[column].dtype is np.dtype(bool) | |
401 | pd.api.types.is_object_dtype(gdf[column]) | |
402 | or pd.api.types.is_bool_dtype(gdf[column]) | |
403 | or pd.api.types.is_string_dtype(gdf[column]) | |
382 | 404 | or categories |
383 | 405 | ): |
384 | 406 | categorical = True |
394 | 416 | if cmap in plt.colormaps(): |
395 | 417 | |
396 | 418 | color = np.apply_along_axis( |
397 | colors.to_hex, 1, cm.get_cmap(cmap, N)(cat.codes) | |
419 | colors.to_hex, | |
420 | 1, | |
421 | _colormap_helper(cmap, n_resample=N, idx=cat.codes), | |
398 | 422 | ) |
399 | 423 | legend_colors = np.apply_along_axis( |
400 | colors.to_hex, 1, cm.get_cmap(cmap, N)(range(N)) | |
424 | colors.to_hex, 1, _colormap_helper(cmap, n_resample=N, idx=range(N)) | |
401 | 425 | ) |
402 | 426 | |
403 | 427 | # colormap is matplotlib.Colormap |
438 | 462 | np.asarray(gdf[column][~nan_idx]), scheme, **classification_kwds |
439 | 463 | ) |
440 | 464 | color = np.apply_along_axis( |
441 | colors.to_hex, 1, cm.get_cmap(cmap, k)(binning.yb) | |
465 | colors.to_hex, | |
466 | 1, | |
467 | _colormap_helper(cmap, n_resample=k, idx=binning.yb), | |
442 | 468 | ) |
443 | 469 | |
444 | 470 | else: |
449 | 475 | ) |
450 | 476 | |
451 | 477 | color = np.apply_along_axis( |
452 | colors.to_hex, 1, cm.get_cmap(cmap, 256)(binning.yb) | |
478 | colors.to_hex, | |
479 | 1, | |
480 | _colormap_helper(cmap, n_resample=256, idx=binning.yb), | |
453 | 481 | ) |
454 | 482 | |
455 | 483 | # set default style |
620 | 648 | colormap_kwds["max_labels"] = legend_kwds.pop("max_labels") |
621 | 649 | if scheme: |
622 | 650 | cb_colors = np.apply_along_axis( |
623 | colors.to_hex, 1, cm.get_cmap(cmap, binning.k)(range(binning.k)) | |
651 | colors.to_hex, | |
652 | 1, | |
653 | _colormap_helper(cmap, n_resample=binning.k, idx=range(binning.k)), | |
624 | 654 | ) |
625 | 655 | if cbar: |
626 | 656 | if legend_kwds.pop("scale", True): |
654 | 684 | if isinstance(cmap, bc.colormap.ColorMap): |
655 | 685 | colorbar = cmap |
656 | 686 | else: |
657 | ||
658 | mp_cmap = cm.get_cmap(cmap) | |
687 | mp_cmap = _colormap_helper(cmap) | |
659 | 688 | cb_colors = np.apply_along_axis( |
660 | 689 | colors.to_hex, 1, mp_cmap(range(mp_cmap.N)) |
661 | 690 | ) |
691 | ||
662 | 692 | # linear legend |
663 | 693 | if mp_cmap.N > 20: |
664 | 694 | colorbar = bc.colormap.LinearColormap( |
122 | 122 | GeoSeries : Series object designed to store shapely geometry objects |
123 | 123 | """ |
124 | 124 | |
125 | # TODO: remove "_crs" in 0.12 | |
126 | _metadata = ["_crs", "_geometry_column_name"] | |
125 | _metadata = ["_geometry_column_name"] | |
126 | ||
127 | _internal_names = DataFrame._internal_names + ["geometry"] | |
128 | _internal_names_set = set(_internal_names) | |
127 | 129 | |
128 | 130 | _geometry_column_name = DEFAULT_GEO_COLUMN_NAME |
129 | 131 | |
130 | 132 | def __init__(self, data=None, *args, geometry=None, crs=None, **kwargs): |
131 | 133 | with compat.ignore_shapely2_warnings(): |
132 | 134 | super().__init__(data, *args, **kwargs) |
133 | ||
134 | # TODO: to be removed in 0.12 | |
135 | self._crs = None | |
136 | 135 | |
137 | 136 | # set_geometry ensures the geometry data have the proper dtype, |
138 | 137 | # but is not called if `geometry=None` ('geometry' column present |
346 | 345 | level = _ensure_geometry(level, crs=crs) |
347 | 346 | frame[geo_column_name] = level |
348 | 347 | frame._geometry_column_name = geo_column_name |
349 | ||
350 | # TODO: to be removed in 0.12 | |
351 | frame._crs = level.crs | |
352 | 348 | if not inplace: |
353 | 349 | return frame |
354 | 350 | |
430 | 426 | GeoDataFrame.to_crs : re-project to another CRS |
431 | 427 | |
432 | 428 | """ |
433 | # TODO: remove try/except in 0.12 | |
434 | 429 | try: |
435 | 430 | return self.geometry.crs |
436 | 431 | except AttributeError: |
437 | # the active geometry column might not be set | |
438 | warnings.warn( | |
439 | "Accessing CRS of a GeoDataFrame without a geometry column is " | |
440 | "deprecated and will be removed in GeoPandas 0.12. " | |
441 | "Use GeoDataFrame.set_geometry to set the active geometry column.", | |
442 | FutureWarning, | |
443 | stacklevel=2, | |
432 | raise AttributeError( | |
433 | "The CRS attribute of a GeoDataFrame without an active " | |
434 | "geometry column is not defined. Use GeoDataFrame.set_geometry " | |
435 | "to set the active geometry column." | |
444 | 436 | ) |
445 | return self._crs | |
446 | 437 | |
447 | 438 | @crs.setter |
448 | 439 | def crs(self, value): |
458 | 449 | self.geometry.values.crs = value |
459 | 450 | else: |
460 | 451 | # column called 'geometry' without geometry |
461 | self._crs = None if not value else CRS.from_user_input(value) | |
462 | ||
463 | # TODO: raise this error in 0.12. This already raises a FutureWarning | |
464 | # TODO: defined in the crs property above | |
465 | # raise ValueError( | |
466 | # "Assigning CRS to a GeoDataFrame without an active geometry " | |
467 | # "column is not supported. Use GeoDataFrame.set_geometry to set " | |
468 | # "the active geometry column.", | |
469 | # ) | |
452 | raise ValueError( | |
453 | "Assigning CRS to a GeoDataFrame without an active geometry " | |
454 | "column is not supported. Use GeoDataFrame.set_geometry to set " | |
455 | "the active geometry column.", | |
456 | ) | |
470 | 457 | |
471 | 458 | def __setstate__(self, state): |
472 | 459 | # overriding DataFrame method for compat with older pickles (CRS handling) |
1448 | 1435 | if pd.api.types.is_scalar(value) or isinstance(value, BaseGeometry): |
1449 | 1436 | value = [value] * self.shape[0] |
1450 | 1437 | try: |
1451 | # TODO: remove this use of _crs in 0.12 | |
1452 | warn = False | |
1453 | if not (hasattr(self, "geometry") and hasattr(self.geometry, "crs")): | |
1454 | crs = self._crs | |
1455 | warn = True | |
1456 | else: | |
1457 | crs = getattr(self, "crs", None) | |
1438 | crs = getattr(self, "crs", None) | |
1458 | 1439 | value = _ensure_geometry(value, crs=crs) |
1459 | if warn and crs is not None: | |
1460 | warnings.warn( | |
1461 | "Setting geometries to a GeoDataFrame without a geometry " | |
1462 | "column will currently preserve the CRS, if present. " | |
1463 | "This is deprecated, and in the future the CRS will be lost " | |
1464 | "in this case. You can use set_crs(..) on the result to " | |
1465 | "set the CRS manually.", | |
1466 | FutureWarning, | |
1467 | stacklevel=2, | |
1468 | ) | |
1469 | 1440 | except TypeError: |
1470 | 1441 | warnings.warn("Geometry column does not contain geometry.") |
1471 | 1442 | super().__setitem__(key, value) |
2054 | 2025 | 4 326625791 North America United States of America USA 18560000.0 \ |
2055 | 2026 | MULTIPOLYGON (((-122.84000 49.00000, -120.0000... |
2056 | 2027 | >>> cities.head() |
2057 | name geometry | |
2058 | 0 Vatican City POINT (12.45339 41.90328) | |
2059 | 1 San Marino POINT (12.44177 43.93610) | |
2060 | 2 Vaduz POINT (9.51667 47.13372) | |
2061 | 3 Luxembourg POINT (6.13000 49.61166) | |
2062 | 4 Palikir POINT (158.14997 6.91664) | |
2028 | name geometry | |
2029 | 0 Vatican City POINT (12.45339 41.90328) | |
2030 | 1 San Marino POINT (12.44177 43.93610) | |
2031 | 2 Vaduz POINT (9.51667 47.13372) | |
2032 | 3 Lobamba POINT (31.20000 -26.46667) | |
2033 | 4 Luxembourg POINT (6.13000 49.61166) | |
2063 | 2034 | |
2064 | 2035 | >>> cities_w_country_data = cities.sjoin(countries) |
2065 | 2036 | >>> cities_w_country_data.head() # doctest: +SKIP |
2252 | 2223 | >>> capitals = geopandas.read_file( |
2253 | 2224 | ... geopandas.datasets.get_path('naturalearth_cities')) |
2254 | 2225 | >>> capitals.shape |
2255 | (202, 2) | |
2226 | (243, 2) | |
2256 | 2227 | |
2257 | 2228 | >>> sa_capitals = capitals.clip(south_america) |
2258 | 2229 | >>> sa_capitals.shape |
2259 | (12, 2) | |
2230 | (15, 2) | |
2260 | 2231 | """ |
2261 | 2232 | return geopandas.clip(self, mask=mask, keep_geom_type=keep_geom_type) |
2262 | 2233 |
6 | 6 | from pandas.core.internals import SingleBlockManager |
7 | 7 | |
8 | 8 | from pyproj import CRS |
9 | import shapely | |
9 | 10 | from shapely.geometry.base import BaseGeometry |
11 | from shapely.geometry import GeometryCollection | |
10 | 12 | |
11 | 13 | from geopandas.base import GeoPandasBase, _delegate_property |
12 | 14 | from geopandas.plotting import plot_series |
786 | 788 | GeoSeries.isna : detect missing values |
787 | 789 | """ |
788 | 790 | if value is None: |
789 | value = BaseGeometry() | |
791 | value = GeometryCollection() if compat.SHAPELY_GE_20 else BaseGeometry() | |
790 | 792 | return super().fillna(value=value, method=method, inplace=inplace, **kwargs) |
791 | 793 | |
792 | 794 | def __contains__(self, other): |
870 | 872 | ) |
871 | 873 | index_parts = True |
872 | 874 | |
873 | if compat.USE_PYGEOS and compat.PYGEOS_GE_09: | |
874 | import pygeos # noqa | |
875 | ||
876 | geometries, outer_idx = pygeos.get_parts( | |
877 | self.values.data, return_index=True | |
878 | ) | |
875 | if compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_09): | |
876 | if compat.USE_SHAPELY_20: | |
877 | geometries, outer_idx = shapely.get_parts( | |
878 | self.values.data, return_index=True | |
879 | ) | |
880 | else: | |
881 | import pygeos # noqa | |
882 | ||
883 | geometries, outer_idx = pygeos.get_parts( | |
884 | self.values.data, return_index=True | |
885 | ) | |
879 | 886 | |
880 | 887 | if len(outer_idx): |
881 | 888 | # Generate inner index as a range per value of outer_idx |
917 | 924 | index = [] |
918 | 925 | geometries = [] |
919 | 926 | for idx, s in self.geometry.items(): |
920 | if s.type.startswith("Multi") or s.type == "GeometryCollection": | |
927 | if s.geom_type.startswith("Multi") or s.geom_type == "GeometryCollection": | |
921 | 928 | geoms = s.geoms |
922 | 929 | idxs = [(idx, i) for i in range(len(geoms))] |
923 | 930 | else: |
1338 | 1345 | >>> capitals = geopandas.read_file( |
1339 | 1346 | ... geopandas.datasets.get_path('naturalearth_cities')) |
1340 | 1347 | >>> capitals.shape |
1341 | (202, 2) | |
1348 | (243, 2) | |
1342 | 1349 | |
1343 | 1350 | >>> sa_capitals = capitals.geometry.clip(south_america) |
1344 | 1351 | >>> sa_capitals.shape |
1345 | (12,) | |
1352 | (15,) | |
1346 | 1353 | """ |
1347 | 1354 | return geopandas.clip(self, mask=mask, keep_geom_type=keep_geom_type) |
25 | 25 | fiona = None |
26 | 26 | fiona_env = None |
27 | 27 | fiona_import_error = None |
28 | FIONA_GE_19 = False | |
28 | 29 | |
29 | 30 | |
30 | 31 | def _import_fiona(): |
31 | 32 | global fiona |
32 | 33 | global fiona_env |
33 | 34 | global fiona_import_error |
35 | global FIONA_GE_19 | |
34 | 36 | |
35 | 37 | if fiona is None: |
36 | 38 | try: |
47 | 49 | except ImportError: |
48 | 50 | fiona_env = None |
49 | 51 | |
52 | FIONA_GE_19 = Version(Version(fiona.__version__).base_version) >= Version( | |
53 | "1.9.0" | |
54 | ) | |
50 | 55 | except ImportError as err: |
51 | 56 | fiona = False |
52 | 57 | fiona_import_error = str(err) |
195 | 200 | installed, otherwise tries "pyogrio". |
196 | 201 | **kwargs : |
197 | 202 | Keyword args to be passed to the engine. In case of the "fiona" engine, |
198 | the keyword arguments are passed to the `open` or `BytesCollection` | |
199 | method in the fiona library when opening the file. For more information | |
200 | on possible keywords, type: ``import fiona; help(fiona.open)``. In | |
201 | case of the "pyogrio" engine, the keyword arguments are passed to | |
202 | `pyogrio.read_dataframe`. | |
203 | the keyword arguments are passed to :func:`fiona.open` or | |
204 | :class:`fiona.collection.BytesCollection` when opening the file. | |
205 | For more information on possible keywords, type: | |
206 | ``import fiona; help(fiona.open)``. In case of the "pyogrio" engine, | |
207 | the keyword arguments are passed to :func:`pyogrio.read_dataframe`. | |
208 | ||
203 | 209 | |
204 | 210 | Examples |
205 | 211 | -------- |
261 | 267 | |
262 | 268 | |
263 | 269 | def _read_file_fiona( |
264 | path_or_bytes, from_bytes, bbox=None, mask=None, rows=None, **kwargs | |
270 | path_or_bytes, from_bytes, bbox=None, mask=None, rows=None, where=None, **kwargs | |
265 | 271 | ): |
272 | if where is not None and not FIONA_GE_19: | |
273 | raise NotImplementedError("where requires fiona 1.9+") | |
274 | ||
266 | 275 | if not from_bytes: |
267 | 276 | # Opening a file via URL or file-like-object above automatically detects a |
268 | 277 | # zipped file. In order to match that behavior, attempt to add a zip scheme |
291 | 300 | |
292 | 301 | with fiona_env(): |
293 | 302 | with reader(path_or_bytes, **kwargs) as features: |
294 | ||
295 | # In a future Fiona release the crs attribute of features will | |
296 | # no longer be a dict, but will behave like a dict. So this should | |
297 | # be forwards compatible | |
298 | crs = ( | |
299 | features.crs["init"] | |
300 | if features.crs and "init" in features.crs | |
301 | else features.crs_wkt | |
302 | ) | |
303 | crs = features.crs_wkt | |
304 | # attempt to get EPSG code | |
305 | try: | |
306 | # fiona 1.9+ | |
307 | epsg = features.crs.to_epsg(confidence_threshold=100) | |
308 | if epsg is not None: | |
309 | crs = epsg | |
310 | except AttributeError: | |
311 | # fiona <= 1.8 | |
312 | try: | |
313 | crs = features.crs["init"] | |
314 | except (TypeError, KeyError): | |
315 | pass | |
303 | 316 | |
304 | 317 | # handle loading the bounding box |
305 | 318 | if bbox is not None: |
313 | 326 | mask = mapping(mask.to_crs(crs).unary_union) |
314 | 327 | elif isinstance(mask, BaseGeometry): |
315 | 328 | mask = mapping(mask) |
329 | ||
330 | filters = {} | |
331 | if bbox is not None: | |
332 | filters["bbox"] = bbox | |
333 | if mask is not None: | |
334 | filters["mask"] = mask | |
335 | if where is not None: | |
336 | filters["where"] = where | |
337 | ||
316 | 338 | # setup the data loading filter |
317 | 339 | if rows is not None: |
318 | 340 | if isinstance(rows, int): |
319 | 341 | rows = slice(rows) |
320 | 342 | elif not isinstance(rows, slice): |
321 | 343 | raise TypeError("'rows' must be an integer or a slice.") |
322 | f_filt = features.filter( | |
323 | rows.start, rows.stop, rows.step, bbox=bbox, mask=mask | |
324 | ) | |
325 | elif any((bbox, mask)): | |
326 | f_filt = features.filter(bbox=bbox, mask=mask) | |
344 | f_filt = features.filter(rows.start, rows.stop, rows.step, **filters) | |
345 | elif filters: | |
346 | f_filt = features.filter(**filters) | |
327 | 347 | else: |
328 | 348 | f_filt = features |
329 | 349 | # get list of columns |
2 | 2 | |
3 | 3 | import pandas as pd |
4 | 4 | |
5 | import shapely | |
5 | 6 | import shapely.wkb |
6 | 7 | |
7 | 8 | from geopandas import GeoDataFrame |
84 | 85 | |
85 | 86 | df[geom_col] = geoms = geoms.apply(load_geom) |
86 | 87 | if crs is None: |
87 | srid = shapely.geos.lgeos.GEOSGetSRID(geoms.iat[0]._geom) | |
88 | if compat.SHAPELY_GE_20: | |
89 | srid = shapely.get_srid(geoms.iat[0]) | |
90 | else: | |
91 | srid = shapely.geos.lgeos.GEOSGetSRID(geoms.iat[0]._geom) | |
88 | 92 | # if no defined SRID in geodatabase, returns SRID of 0 |
89 | 93 | if srid != 0: |
90 | 94 | crs = "epsg:{}".format(srid) |
283 | 287 | |
284 | 288 | def _convert_to_ewkb(gdf, geom_name, srid): |
285 | 289 | """Convert geometries to ewkb.""" |
286 | if compat.USE_PYGEOS: | |
290 | if compat.USE_SHAPELY_20: | |
291 | geoms = shapely.to_wkb( | |
292 | shapely.set_srid(gdf[geom_name].values.data, srid=srid), | |
293 | hex=True, | |
294 | include_srid=True, | |
295 | ) | |
296 | ||
297 | elif compat.USE_PYGEOS: | |
287 | 298 | from pygeos import set_srid, to_wkb |
288 | 299 | |
289 | 300 | geoms = to_wkb( |
36 | 36 | FIONA_GE_1814 = Version(fiona.__version__) >= Version("1.8.14") |
37 | 37 | # invalid datetime handling |
38 | 38 | FIONA_GE_1821 = Version(fiona.__version__) >= Version("1.8.21") |
39 | FIONA_GE_19 = Version(Version(fiona.__version__).base_version) >= Version("1.9.0") | |
39 | 40 | except ImportError: |
40 | 41 | fiona = False |
41 | 42 | FIONA_GE_1814 = False |
42 | 43 | FIONA_GE_1821 = False |
44 | FIONA_GE_19 = False | |
43 | 45 | |
44 | 46 | |
45 | 47 | PYOGRIO_MARK = pytest.mark.skipif(not pyogrio, reason="pyogrio not installed") |
765 | 767 | assert gdf.columns.tolist() == ["geometry"] |
766 | 768 | |
767 | 769 | |
770 | def test_read_file__where_filter(engine): | |
771 | if FIONA_GE_19 or engine == "pyogrio": | |
772 | gdf = geopandas.read_file( | |
773 | geopandas.datasets.get_path("naturalearth_lowres"), | |
774 | where="continent='Africa'", | |
775 | engine=engine, | |
776 | ) | |
777 | assert gdf.continent.unique().tolist() == ["Africa"] | |
778 | else: | |
779 | with pytest.raises(NotImplementedError): | |
780 | geopandas.read_file( | |
781 | geopandas.datasets.get_path("naturalearth_lowres"), | |
782 | where="continent='Africa'", | |
783 | engine="fiona", | |
784 | ) | |
785 | ||
786 | ||
768 | 787 | @PYOGRIO_MARK |
769 | 788 | def test_read_file__columns(): |
770 | 789 | # TODO: this is only support for pyogrio, but we could mimic it for fiona as well |
805 | 824 | engine=engine, |
806 | 825 | ) |
807 | 826 | filtered_df_shape = gdf.shape |
808 | assert filtered_df_shape == (50, 2) | |
827 | assert filtered_df_shape == (57, 2) | |
809 | 828 | |
810 | 829 | |
811 | 830 | def test_read_file_filtered_with_gdf_boundary__mask__polygon(df_nybb, engine): |
47 | 47 | |
48 | 48 | |
49 | 49 | @pytest.mark.skipif( |
50 | compat.USE_PYGEOS or (Version(pyproj.__version__) < Version("2.4")), | |
50 | compat.USE_SHAPELY_20 | |
51 | or compat.USE_PYGEOS | |
52 | or (Version(pyproj.__version__) < Version("2.4")), | |
51 | 53 | reason=( |
52 | "pygeos-based unpickling currently only works for pygeos-written files; " | |
54 | "shapely 2.0/pygeos-based unpickling currently only works for " | |
55 | "shapely-2.0/pygeos-written files; " | |
53 | 56 | "old pyproj versions can't read pickles from newer pyproj versions" |
54 | 57 | ), |
55 | 58 | ) |
47 | 47 | return geoms, np.arange(len(geoms)) |
48 | 48 | |
49 | 49 | for ix, geom in enumerate(geoms): |
50 | if geom is not None and geom.type.startswith(prefix) and not geom.is_empty: | |
50 | if geom is not None and geom.geom_type.startswith(prefix) and not geom.is_empty: | |
51 | 51 | for poly in geom.geoms: |
52 | 52 | components.append(poly) |
53 | 53 | component_index.append(ix) |
434 | 434 | expl_color = np.take(color, multiindex, axis=0) if color_given else color |
435 | 435 | expl_series = geopandas.GeoSeries(geoms) |
436 | 436 | |
437 | geom_types = expl_series.type | |
437 | geom_types = expl_series.geom_type | |
438 | 438 | poly_idx = np.asarray((geom_types == "Polygon") | (geom_types == "MultiPolygon")) |
439 | 439 | line_idx = np.asarray( |
440 | 440 | (geom_types == "LineString") |
732 | 732 | "Cannot specify 'categories' when column has categorical dtype" |
733 | 733 | ) |
734 | 734 | categorical = True |
735 | elif values.dtype is np.dtype("O") or categories: | |
735 | elif ( | |
736 | pd.api.types.is_object_dtype(values.dtype) | |
737 | or pd.api.types.is_bool_dtype(values.dtype) | |
738 | or pd.api.types.is_string_dtype(values.dtype) | |
739 | or categories | |
740 | ): | |
736 | 741 | categorical = True |
737 | 742 | |
738 | 743 | nan_idx = np.asarray(pd.isna(values), dtype="bool") |
828 | 833 | nan_idx = np.take(nan_idx, multiindex, axis=0) |
829 | 834 | expl_series = geopandas.GeoSeries(geoms) |
830 | 835 | |
831 | geom_types = expl_series.type | |
836 | geom_types = expl_series.geom_type | |
832 | 837 | poly_idx = np.asarray((geom_types == "Polygon") | (geom_types == "MultiPolygon")) |
833 | 838 | line_idx = np.asarray( |
834 | 839 | (geom_types == "LineString") |
13 | 13 | Required to comply with _compat.USE_PYGEOS. |
14 | 14 | The selection order goes PyGEOS > RTree > Error. |
15 | 15 | """ |
16 | if compat.USE_PYGEOS: | |
16 | if compat.USE_SHAPELY_20 or compat.USE_PYGEOS: | |
17 | 17 | return PyGEOSSTRTreeIndex |
18 | 18 | if compat.HAS_RTREE: |
19 | 19 | return RTreeIndex |
54 | 54 | ---------- |
55 | 55 | geometry : shapely geometry |
56 | 56 | A single shapely geometry to query against the spatial index. |
57 | predicate : {None, 'intersects', 'within', 'contains', \ | |
58 | 'overlaps', 'crosses', 'touches'}, optional | |
57 | predicate : {None, "contains", "contains_properly", "covered_by", "covers", \ | |
58 | "crosses", "intersects", "overlaps", "touches", "within"}, optional | |
59 | 59 | If predicate is provided, the input geometry is |
60 | 60 | tested using the predicate function against each item |
61 | 61 | in the tree whose extent intersects the envelope of the |
117 | 117 | geometry : {GeoSeries, GeometryArray, numpy.array of PyGEOS geometries} |
118 | 118 | Accepts GeoPandas geometry iterables (GeoSeries, GeometryArray) |
119 | 119 | or a numpy array of PyGEOS geometries. |
120 | predicate : {None, 'intersects', 'within', 'contains', 'overlaps', \ | |
121 | 'crosses', 'touches'}, optional | |
120 | predicate : {None, "contains", "contains_properly", "covered_by", "covers", \ | |
121 | "crosses", "intersects", "overlaps", "touches", "within"}, optional | |
122 | 122 | If predicate is provided, the input geometries are tested using |
123 | 123 | the predicate function against each item in the tree whose extent |
124 | 124 | intersects the envelope of the each input geometry: |
436 | 436 | "overlaps", |
437 | 437 | "crosses", |
438 | 438 | "touches", |
439 | "covered_by", | |
439 | 440 | "covers", |
440 | 441 | "contains_properly", |
441 | 442 | } |
501 | 502 | if predicate in ( |
502 | 503 | "contains", |
503 | 504 | "intersects", |
505 | "covered_by", | |
504 | 506 | "covers", |
505 | 507 | "contains_properly", |
506 | 508 | ): |
624 | 626 | return self.size |
625 | 627 | |
626 | 628 | |
627 | if compat.HAS_PYGEOS: | |
629 | if compat.SHAPELY_GE_20 or compat.HAS_PYGEOS: | |
628 | 630 | |
629 | 631 | from . import geoseries # noqa |
630 | 632 | from . import array # noqa |
631 | import pygeos # noqa | |
632 | ||
633 | _PYGEOS_PREDICATES = {p.name for p in pygeos.strtree.BinaryPredicate} | set([None]) | |
633 | ||
634 | if compat.USE_SHAPELY_20: | |
635 | import shapely as mod # noqa | |
636 | ||
637 | _PYGEOS_PREDICATES = {p.name for p in mod.strtree.BinaryPredicate} | set([None]) | |
638 | else: | |
639 | import pygeos as mod # noqa | |
640 | ||
641 | _PYGEOS_PREDICATES = {p.name for p in mod.strtree.BinaryPredicate} | set([None]) | |
634 | 642 | |
635 | 643 | class PyGEOSSTRTreeIndex(BaseSpatialIndex): |
636 | 644 | """A simple wrapper around pygeos's STRTree. |
648 | 656 | # https://github.com/pygeos/pygeos/issues/146 |
649 | 657 | # https://github.com/pygeos/pygeos/issues/147 |
650 | 658 | non_empty = geometry.copy() |
651 | non_empty[pygeos.is_empty(non_empty)] = None | |
659 | non_empty[mod.is_empty(non_empty)] = None | |
652 | 660 | # set empty geometries to None to maintain indexing |
653 | self._tree = pygeos.STRtree(non_empty) | |
661 | self._tree = mod.STRtree(non_empty) | |
654 | 662 | # store geometries, including empty geometries for user access |
655 | 663 | self.geometries = geometry.copy() |
656 | 664 | |
668 | 676 | >>> from shapely.geometry import Point |
669 | 677 | >>> s = geopandas.GeoSeries([Point(0, 0), Point(1, 1)]) |
670 | 678 | >>> s.sindex.valid_query_predicates # doctest: +SKIP |
671 | {'contains', 'crosses', 'covered_by', None, 'intersects', 'within', \ | |
672 | 'touches', 'overlaps', 'contains_properly', 'covers'} | |
679 | {None, "contains", "contains_properly", "covered_by", "covers", \ | |
680 | "crosses", "intersects", "overlaps", "touches", "within"} | |
673 | 681 | """ |
674 | 682 | return _PYGEOS_PREDICATES |
675 | 683 | |
716 | 724 | return geometry.data |
717 | 725 | elif isinstance(geometry, BaseGeometry): |
718 | 726 | return array._shapely_to_geom(geometry) |
727 | elif geometry is None: | |
728 | return None | |
719 | 729 | elif isinstance(geometry, list): |
720 | 730 | return np.asarray( |
721 | 731 | [ |
739 | 749 | |
740 | 750 | geometry = self._as_geometry_array(geometry) |
741 | 751 | |
742 | res = self._tree.query_bulk(geometry, predicate) | |
752 | if compat.USE_SHAPELY_20: | |
753 | res = self._tree.query(geometry, predicate) | |
754 | else: | |
755 | res = self._tree.query_bulk(geometry, predicate) | |
743 | 756 | |
744 | 757 | if sort: |
745 | 758 | # sort by first array (geometry) and then second (tree) |
753 | 766 | def nearest( |
754 | 767 | self, geometry, return_all=True, max_distance=None, return_distance=False |
755 | 768 | ): |
756 | if not compat.PYGEOS_GE_010: | |
757 | raise NotImplementedError("sindex.nearest requires pygeos >= 0.10") | |
769 | if not (compat.USE_SHAPELY_20 or compat.PYGEOS_GE_010): | |
770 | raise NotImplementedError( | |
771 | "sindex.nearest requires shapely >= 2.0 or pygeos >= 0.10" | |
772 | ) | |
758 | 773 | |
759 | 774 | geometry = self._as_geometry_array(geometry) |
760 | ||
761 | if not return_all and max_distance is None and not return_distance: | |
762 | return self._tree.nearest(geometry) | |
763 | ||
764 | result = self._tree.nearest_all( | |
765 | geometry, max_distance=max_distance, return_distance=return_distance | |
766 | ) | |
775 | if isinstance(geometry, BaseGeometry) or geometry is None: | |
776 | geometry = [geometry] | |
777 | ||
778 | if compat.USE_SHAPELY_20: | |
779 | result = self._tree.query_nearest( | |
780 | geometry, | |
781 | max_distance=max_distance, | |
782 | return_distance=return_distance, | |
783 | all_matches=return_all, | |
784 | ) | |
785 | else: | |
786 | if not return_all and max_distance is None and not return_distance: | |
787 | return self._tree.nearest(geometry) | |
788 | result = self._tree.nearest_all( | |
789 | geometry, max_distance=max_distance, return_distance=return_distance | |
790 | ) | |
767 | 791 | if return_distance: |
768 | 792 | indices, distances = result |
769 | 793 | else: |
770 | 794 | indices = result |
771 | 795 | |
772 | if not return_all: | |
796 | if not return_all and not compat.USE_SHAPELY_20: | |
773 | 797 | # first subarray of geometry indices is sorted, so we can use this |
774 | 798 | # trick to get the first of each index value |
775 | 799 | mask = np.diff(indices[0, :]).astype("bool") |
803 | 827 | |
804 | 828 | # need to convert tuple of bounds to a geometry object |
805 | 829 | if len(coordinates) == 4: |
806 | indexes = self._tree.query(pygeos.box(*coordinates)) | |
830 | indexes = self._tree.query(mod.box(*coordinates)) | |
807 | 831 | elif len(coordinates) == 2: |
808 | indexes = self._tree.query(pygeos.points(*coordinates)) | |
832 | indexes = self._tree.query(mod.points(*coordinates)) | |
809 | 833 | else: |
810 | 834 | raise TypeError( |
811 | 835 | "Invalid coordinates, must be iterable in format " |
179 | 179 | assert left.index.equals(right.index), "index: %s != %s" % (left.index, right.index) |
180 | 180 | |
181 | 181 | if check_geom_type: |
182 | assert (left.type == right.type).all(), "type: %s != %s" % ( | |
183 | left.type, | |
184 | right.type, | |
182 | assert (left.geom_type == right.geom_type).all(), "type: %s != %s" % ( | |
183 | left.geom_type, | |
184 | right.geom_type, | |
185 | 185 | ) |
186 | 186 | |
187 | 187 | if normalize: |
10 | 10 | from shapely.geometry.base import CAP_STYLE, JOIN_STYLE |
11 | 11 | import shapely.wkb |
12 | 12 | import shapely.wkt |
13 | from shapely._buildcfg import geos_version | |
13 | ||
14 | try: | |
15 | from shapely import geos_version | |
16 | except ImportError: | |
17 | from shapely._buildcfg import geos_version | |
14 | 18 | |
15 | 19 | import geopandas |
16 | 20 | from geopandas.array import ( |
141 | 145 | # missing values |
142 | 146 | # TODO(pygeos) does not support empty strings, np.nan, or pd.NA |
143 | 147 | missing_values = [None] |
144 | if not compat.USE_PYGEOS: | |
148 | if not (compat.USE_SHAPELY_20 or compat.USE_PYGEOS): | |
145 | 149 | missing_values.extend([b"", np.nan]) |
146 | 150 | missing_values.append(pd.NA) |
147 | 151 | |
215 | 219 | # missing values |
216 | 220 | # TODO(pygeos) does not support empty strings, np.nan, or pd.NA |
217 | 221 | missing_values = [None] |
218 | if not compat.USE_PYGEOS: | |
222 | if not (compat.USE_SHAPELY_20 or compat.USE_PYGEOS): | |
219 | 223 | missing_values.extend([f(""), np.nan]) |
220 | 224 | missing_values.append(pd.NA) |
221 | 225 |
296 | 296 | |
297 | 297 | # geometry column without geometry |
298 | 298 | df = GeoDataFrame({"geometry": [0, 1]}) |
299 | with pytest.warns( | |
300 | FutureWarning, match="Accessing CRS of a GeoDataFrame without a geometry" | |
299 | with pytest.raises( | |
300 | ValueError, | |
301 | match="Assigning CRS to a GeoDataFrame without an active geometry", | |
301 | 302 | ): |
302 | 303 | df.crs = 27700 |
303 | with pytest.warns( | |
304 | FutureWarning, match="Accessing CRS of a GeoDataFrame without a geometry" | |
304 | with pytest.raises( | |
305 | AttributeError, | |
306 | match="The CRS attribute of a GeoDataFrame without an active", | |
305 | 307 | ): |
306 | 308 | assert df.crs == self.osgb |
307 | 309 | |
309 | 311 | df = GeoDataFrame({"col": range(10)}, geometry=self.arr) |
310 | 312 | df["geom2"] = df.geometry.centroid |
311 | 313 | subset = df[["col", "geom2"]] |
312 | with pytest.warns( | |
313 | FutureWarning, match="Accessing CRS of a GeoDataFrame without a geometry" | |
314 | with pytest.raises( | |
315 | AttributeError, | |
316 | match="The CRS attribute of a GeoDataFrame without an active", | |
314 | 317 | ): |
315 | 318 | assert subset.crs == self.osgb |
316 | 319 | |
354 | 357 | arr = from_shapely(self.geoms) |
355 | 358 | df = GeoDataFrame({"col1": [1, 2], "geometry": arr}, crs=4326) |
356 | 359 | |
357 | # create a dataframe without geometry column, but currently has cached _crs | |
360 | # override geometry with non geometry | |
358 | 361 | with pytest.warns(UserWarning): |
359 | 362 | df["geometry"] = 1 |
360 | 363 | |
361 | # assigning a list of geometry object will currently use _crs | |
362 | with pytest.warns( | |
363 | FutureWarning, | |
364 | match="Setting geometries to a GeoDataFrame without a geometry", | |
365 | ): | |
366 | df["geometry"] = self.geoms | |
367 | assert df.crs == self.wgs | |
364 | # assigning a list of geometry object doesn't have cached access to 4326 | |
365 | df["geometry"] = self.geoms | |
366 | assert df.crs is None | |
368 | 367 | |
369 | 368 | @pytest.mark.parametrize( |
370 | 369 | "scalar", [None, Point(0, 0), LineString([(0, 0), (1, 1)])] |
0 | import warnings | |
1 | ||
0 | 2 | import numpy as np |
1 | 3 | import pandas as pd |
2 | 4 | |
288 | 290 | ) |
289 | 291 | def test_dissolve_dropna_warn(nybb_polydf): |
290 | 292 | # No warning with default params |
291 | with pytest.warns(None) as record: | |
293 | with warnings.catch_warnings(record=True) as record: | |
292 | 294 | nybb_polydf.dissolve() |
293 | 295 | |
294 | 296 | for r in record: |
307 | 309 | merged_shapes[("BoroCode", "max")] = [5, 2] |
308 | 310 | merged_shapes[("BoroName", "count")] = [3, 2] |
309 | 311 | |
310 | with pytest.warns(None) as record: | |
312 | with warnings.catch_warnings(record=True) as record: | |
311 | 313 | test = nybb_polydf.dissolve( |
312 | 314 | by="manhattan_bronx", |
313 | 315 | aggfunc={ |
255 | 255 | def test_bool(self): |
256 | 256 | df = self.nybb.copy() |
257 | 257 | df["bool"] = [True, False, True, False, True] |
258 | m = df.explore("bool") | |
259 | out_str = self._fetch_map_string(m) | |
260 | assert '"__folium_color":"#9edae5","bool":true' in out_str | |
261 | assert '"__folium_color":"#1f77b4","bool":false' in out_str | |
258 | df["bool_extension"] = pd.array([True, False, True, False, True]) | |
259 | m1 = df.explore("bool") | |
260 | m2 = df.explore("bool_extension") | |
261 | ||
262 | out1_str = self._fetch_map_string(m1) | |
263 | assert '"__folium_color":"#9edae5","bool":true' in out1_str | |
264 | assert '"__folium_color":"#1f77b4","bool":false' in out1_str | |
265 | ||
266 | out2_str = self._fetch_map_string(m2) | |
267 | assert '"__folium_color":"#9edae5","bool":true' in out2_str | |
268 | assert '"__folium_color":"#1f77b4","bool":false' in out2_str | |
269 | ||
270 | def test_string(self): | |
271 | df = self.nybb.copy() | |
272 | df["string"] = pd.array([1, 2, 3, 4, 5], dtype="string") | |
273 | m = df.explore("string") | |
274 | out_str = self._fetch_map_string(m) | |
275 | assert '"__folium_color":"#9edae5","string":"5"' in out_str | |
262 | 276 | |
263 | 277 | def test_column_values(self): |
264 | 278 | """ |
22 | 22 | import pytest |
23 | 23 | |
24 | 24 | |
25 | TEST_NEAREST = compat.PYGEOS_GE_010 and compat.USE_PYGEOS | |
25 | TEST_NEAREST = compat.USE_SHAPELY_20 or (compat.PYGEOS_GE_010 and compat.USE_PYGEOS) | |
26 | 26 | pandas_133 = Version(pd.__version__) == Version("1.3.3") |
27 | 27 | |
28 | 28 | |
359 | 359 | |
360 | 360 | with pytest.raises(AttributeError, match=msg_no_other_geo_cols): |
361 | 361 | GeoDataFrame().geometry |
362 | ||
363 | def test_get_geometry_geometry_inactive(self): | |
364 | # https://github.com/geopandas/geopandas/issues/2574 | |
365 | df = self.df.assign(geom2=self.df.geometry).set_geometry("geom2") | |
366 | df = df.loc[:, ["BoroName", "geometry"]] | |
367 | assert df._geometry_column_name == "geom2" | |
368 | msg_geo_col_missing = "is not present. " | |
369 | # Check that df.geometry raises if active geometry column is missing, | |
370 | # it should not fall back to column named "geometry" | |
371 | with pytest.raises(AttributeError, match=msg_geo_col_missing): | |
372 | df.geometry | |
362 | 373 | |
363 | 374 | def test_align(self): |
364 | 375 | df = self.df2 |
894 | 905 | @pytest.mark.parametrize("how", ["left", "inner", "right"]) |
895 | 906 | @pytest.mark.parametrize("predicate", ["intersects", "within", "contains"]) |
896 | 907 | @pytest.mark.skipif( |
897 | not compat.USE_PYGEOS and not compat.HAS_RTREE, | |
908 | not (compat.USE_PYGEOS and compat.HAS_RTREE and compat.USE_SHAPELY_20), | |
898 | 909 | reason="sjoin needs `rtree` or `pygeos` dependency", |
899 | 910 | ) |
900 | 911 | def test_sjoin(self, how, predicate): |
0 | 0 | import string |
1 | import warnings | |
1 | 2 | |
2 | 3 | import numpy as np |
3 | 4 | from numpy.testing import assert_array_equal |
4 | 5 | from pandas import DataFrame, Index, MultiIndex, Series |
5 | 6 | |
6 | from shapely.geometry import LinearRing, LineString, MultiPoint, Point, Polygon | |
7 | import shapely | |
8 | ||
9 | from shapely.geometry import ( | |
10 | LinearRing, | |
11 | LineString, | |
12 | MultiPoint, | |
13 | Point, | |
14 | Polygon, | |
15 | MultiPolygon, | |
16 | ) | |
7 | 17 | from shapely.geometry.collection import GeometryCollection |
8 | 18 | from shapely.ops import unary_union |
9 | 19 | from shapely import wkt |
590 | 600 | assert_series_equal(res, exp) |
591 | 601 | |
592 | 602 | @pytest.mark.skipif( |
593 | not compat.USE_PYGEOS, | |
603 | not (compat.USE_PYGEOS or compat.USE_SHAPELY_20), | |
594 | 604 | reason="covered_by is only implemented for pygeos, not shapely", |
595 | 605 | ) |
596 | 606 | def test_covered_by(self): |
671 | 681 | with pytest.warns(UserWarning, match="Geometry is in a geographic CRS"): |
672 | 682 | self.g4.centroid |
673 | 683 | |
684 | def test_normalize(self): | |
685 | polygon = Polygon([(0, 0), (1, 1), (0, 1)]) | |
686 | linestring = LineString([(0, 0), (1, 1), (1, 0)]) | |
687 | point = Point(0, 0) | |
688 | series = GeoSeries([polygon, linestring, point]) | |
689 | polygon2 = Polygon([(0, 0), (0, 1), (1, 1)]) | |
690 | expected = GeoSeries([polygon2, linestring, point]) | |
691 | assert_geoseries_equal(series.normalize(), expected) | |
692 | ||
693 | @pytest.mark.skipif( | |
694 | not compat.SHAPELY_GE_18, | |
695 | reason="make_valid keyword introduced in shapely 1.8.0", | |
696 | ) | |
697 | def test_make_valid(self): | |
698 | polygon1 = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]) | |
699 | polygon2 = Polygon([(0, 2), (0, 1), (2, 0), (0, 0), (0, 2)]) | |
700 | linestring = LineString([(0, 0), (1, 1), (1, 0)]) | |
701 | series = GeoSeries([polygon1, polygon2, linestring]) | |
702 | out_polygon1 = MultiPolygon( | |
703 | [ | |
704 | Polygon([(1, 1), (0, 0), (0, 2), (1, 1)]), | |
705 | Polygon([(2, 0), (1, 1), (2, 2), (2, 0)]), | |
706 | ] | |
707 | ) | |
708 | out_polygon2 = GeometryCollection( | |
709 | [Polygon([(2, 0), (0, 0), (0, 1), (2, 0)]), LineString([(0, 2), (0, 1)])] | |
710 | ) | |
711 | expected = GeoSeries([out_polygon1, out_polygon2, linestring]) | |
712 | assert not series.is_valid.all() | |
713 | result = series.make_valid() | |
714 | assert_geoseries_equal(result, expected) | |
715 | assert result.is_valid.all() | |
716 | ||
717 | @pytest.mark.skipif( | |
718 | compat.SHAPELY_GE_18, | |
719 | reason="make_valid keyword introduced in shapely 1.8.0", | |
720 | ) | |
721 | def test_make_valid_shapely_pre18(self): | |
722 | s = GeoSeries([Point(1, 1)]) | |
723 | with pytest.raises( | |
724 | NotImplementedError, | |
725 | match=f"shapely >= 1.8 or PyGEOS is required, " | |
726 | f"version {shapely.__version__} is installed", | |
727 | ): | |
728 | s.make_valid() | |
729 | ||
674 | 730 | def test_convex_hull(self): |
675 | 731 | # the convex hull of a square should be the same as the square |
676 | 732 | squares = GeoSeries([self.sq for i in range(3)]) |
857 | 913 | with pytest.warns(UserWarning, match="Geometry is in a geographic CRS"): |
858 | 914 | self.g4.buffer(1) |
859 | 915 | |
860 | with pytest.warns(None) as record: | |
916 | with warnings.catch_warnings(record=True) as record: | |
861 | 917 | # do not warn for 0 |
862 | 918 | self.g4.buffer(0) |
863 | 919 |
2 | 2 | import random |
3 | 3 | import shutil |
4 | 4 | import tempfile |
5 | import warnings | |
5 | 6 | |
6 | 7 | import numpy as np |
7 | 8 | from numpy.testing import assert_array_equal |
10 | 11 | |
11 | 12 | from pyproj import CRS |
12 | 13 | from shapely.geometry import ( |
14 | GeometryCollection, | |
13 | 15 | LineString, |
14 | 16 | MultiLineString, |
15 | 17 | MultiPoint, |
120 | 122 | # Test that warning is not issued when operating on aligned series |
121 | 123 | a1, a2 = self.a1.align(self.a2) |
122 | 124 | |
123 | with pytest.warns(None) as warnings: | |
125 | with warnings.catch_warnings(record=True) as record: | |
124 | 126 | a1.contains(a2) # _series_op, explicitly aligned |
125 | 127 | self.g1.intersects(self.g2) # _series_op, implicitly aligned |
126 | 128 | a2.union(a1) # _geo_op, explicitly aligned |
127 | 129 | self.g2.intersection(self.g1) # _geo_op, implicitly aligned |
128 | 130 | |
129 | user_warnings = [w for w in warnings if w.category is UserWarning] | |
131 | user_warnings = [w for w in record if w.category is UserWarning] | |
130 | 132 | assert not user_warnings, user_warnings[0].message |
131 | 133 | |
132 | 134 | def test_geom_equals(self): |
383 | 385 | |
384 | 386 | @pytest.mark.filterwarnings("ignore::UserWarning") |
385 | 387 | def test_missing_values(): |
386 | s = GeoSeries([Point(1, 1), None, np.nan, BaseGeometry(), Polygon()]) | |
388 | s = GeoSeries([Point(1, 1), None, np.nan, GeometryCollection(), Polygon()]) | |
387 | 389 | |
388 | 390 | # construction -> missing values get normalized to None |
389 | 391 | assert s[1] is None |
508 | 510 | np.array([], dtype="float64"), |
509 | 511 | np.array([], dtype="str"), |
510 | 512 | ]: |
511 | with pytest.warns(None) as record: | |
513 | with warnings.catch_warnings(record=True) as record: | |
512 | 514 | s = GeoSeries(arr) |
513 | 515 | assert not record |
514 | 516 | assert isinstance(s, GeoSeries) |
53 | 53 | self._check_metadata(res) |
54 | 54 | exp = GeoDataFrame(pd.concat([pd.DataFrame(self.gdf), pd.DataFrame(self.gdf)])) |
55 | 55 | assert_geodataframe_equal(exp, res) |
56 | # check metadata comes from first gdf | |
57 | res4 = pd.concat([self.gdf.set_crs("epsg:4326"), self.gdf], axis=0) | |
58 | # Note: this behaviour potentially does not make sense. If geom cols are | |
59 | # concatenated but have different CRS, then the CRS will be overridden. | |
60 | self._check_metadata(res4, crs="epsg:4326") | |
61 | 56 | |
62 | 57 | # series |
63 | 58 | res = pd.concat([self.gdf.geometry, self.gdf.geometry]) |
64 | 59 | assert res.shape == (6,) |
65 | 60 | assert isinstance(res, GeoSeries) |
66 | 61 | assert isinstance(res.geometry, GeoSeries) |
62 | ||
63 | def test_concat_axis0_crs(self): | |
64 | ||
65 | # CRS not set for both GeoDataFrame | |
66 | res = pd.concat([self.gdf, self.gdf]) | |
67 | self._check_metadata(res) | |
68 | ||
69 | # CRS set for both GeoDataFrame, same CRS | |
70 | res1 = pd.concat([self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4326")]) | |
71 | self._check_metadata(res1, crs="epsg:4326") | |
72 | ||
73 | # CRS not set for one GeoDataFrame, but set for the other GeoDataFrame | |
74 | with pytest.warns( | |
75 | UserWarning, match=r"CRS not set for some of the concatenation inputs.*" | |
76 | ): | |
77 | res2 = pd.concat([self.gdf, self.gdf.set_crs("epsg:4326")]) | |
78 | self._check_metadata(res2, crs="epsg:4326") | |
79 | ||
80 | # CRS set for both GeoDataFrame, different CRS | |
81 | with pytest.raises( | |
82 | ValueError, match=r"Cannot determine common CRS for concatenation inputs.*" | |
83 | ): | |
84 | pd.concat([self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4327")]) | |
85 | ||
86 | # CRS not set for one GeoDataFrame, but set for the other GeoDataFrames, | |
87 | # same CRS | |
88 | with pytest.warns( | |
89 | UserWarning, match=r"CRS not set for some of the concatenation inputs.*" | |
90 | ): | |
91 | res3 = pd.concat( | |
92 | [self.gdf, self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4326")] | |
93 | ) | |
94 | self._check_metadata(res3, crs="epsg:4326") | |
95 | ||
96 | # CRS not set for one GeoDataFrame, but set for the other GeoDataFrames, | |
97 | # different CRS | |
98 | with pytest.raises( | |
99 | ValueError, match=r"Cannot determine common CRS for concatenation inputs.*" | |
100 | ): | |
101 | pd.concat( | |
102 | [self.gdf, self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4327")] | |
103 | ) | |
67 | 104 | |
68 | 105 | def test_concat_axis1(self): |
69 | 106 | |
111 | 148 | # Note this is not consistent with concat([gdf, gdf], axis=1) where the |
112 | 149 | # left metadata is set on the result. This is deliberate for now. |
113 | 150 | assert type(result) is GeoDataFrame |
114 | self._check_metadata(result, geometry_column_name=None, crs=None) | |
151 | assert result._geometry_column_name is None | |
115 | 152 | assert_index_equal(pd.Index([0, 1]), result.columns) |
116 | 153 | |
117 | 154 | gseries2.name = "foo" |
118 | 155 | result2 = pd.concat([gseries2, self.gseries], axis=1) |
119 | 156 | assert type(result2) is GeoDataFrame |
120 | self._check_metadata(result2, geometry_column_name=None, crs=None) | |
157 | assert result._geometry_column_name is None | |
121 | 158 | assert_index_equal(pd.Index(["foo", 0]), result2.columns) |
301 | 301 | assert df.to_csv(index=False) == exp |
302 | 302 | |
303 | 303 | |
304 | @pytest.mark.filterwarnings( | |
305 | "ignore:Dropping of nuisance columns in DataFrame reductions" | |
306 | ) | |
304 | 307 | def test_numerical_operations(s, df): |
305 | ||
306 | 308 | # df methods ignore the geometry column |
307 | 309 | exp = pd.Series([3, 4], index=["value1", "value2"]) |
308 | 310 | assert_series_equal(df.sum(), exp) |
662 | 664 | assert result.dtype == "object" |
663 | 665 | |
664 | 666 | |
667 | def test_df_apply_geometry_dtypes(df): | |
668 | # https://github.com/geopandas/geopandas/issues/1852 | |
669 | apply_types = [] | |
670 | ||
671 | def get_dtypes(srs): | |
672 | apply_types.append((srs.name, type(srs))) | |
673 | ||
674 | df["geom2"] = df.geometry | |
675 | df.apply(get_dtypes) | |
676 | expected = [ | |
677 | ("geometry", GeoSeries), | |
678 | ("value1", pd.Series), | |
679 | ("value2", pd.Series), | |
680 | ("geom2", GeoSeries), | |
681 | ] | |
682 | assert apply_types == expected | |
683 | ||
684 | ||
665 | 685 | def test_pivot(df): |
666 | 686 | # https://github.com/geopandas/geopandas/issues/2057 |
667 | 687 | # pivot failing due to creating a MultiIndex |
373 | 373 | self.df["cats_ordered"] = pd.Categorical( |
374 | 374 | ["cat2", "cat1"] * 5, categories=["cat2", "cat1"] |
375 | 375 | ) |
376 | self.df["bool"] = [False, True] * 5 | |
377 | self.df["bool_extension"] = pd.array([False, True] * 5) | |
378 | self.df["cats_string"] = pd.array(["cat1", "cat2"] * 5, dtype="string") | |
376 | 379 | |
377 | 380 | ax1 = self.df.plot("cats_object", legend=True) |
378 | 381 | ax2 = self.df.plot("cats", legend=True) |
380 | 383 | ax4 = self.df.plot("singlecat", legend=True) |
381 | 384 | ax5 = self.df.plot("cats_ordered", legend=True) |
382 | 385 | ax6 = self.df.plot("nums", categories=[1, 2], legend=True) |
386 | ax7 = self.df.plot("bool", legend=True) | |
387 | ax8 = self.df.plot("bool_extension", legend=True) | |
388 | ax9 = self.df.plot("cats_string", legend=True) | |
383 | 389 | |
384 | 390 | point_colors1 = ax1.collections[0].get_facecolors() |
385 | for ax in [ax2, ax3, ax4, ax5, ax6]: | |
391 | for ax in [ax2, ax3, ax4, ax5, ax6, ax7, ax8, ax9]: | |
386 | 392 | point_colors2 = ax.collections[0].get_facecolors() |
387 | 393 | np.testing.assert_array_equal(point_colors1[1], point_colors2[1]) |
388 | 394 | |
389 | 395 | legend1 = [x.get_markerfacecolor() for x in ax1.get_legend().get_lines()] |
390 | for ax in [ax2, ax3, ax4, ax5, ax6]: | |
396 | for ax in [ax2, ax3, ax4, ax5, ax6, ax7, ax8, ax9]: | |
391 | 397 | legend2 = [x.get_markerfacecolor() for x in ax.get_legend().get_lines()] |
392 | 398 | np.testing.assert_array_equal(legend1, legend2) |
393 | 399 | |
613 | 619 | |
614 | 620 | t3 = Polygon([(2, 0), (3, 0), (3, 1)]) |
615 | 621 | df_nan = GeoDataFrame({"geometry": t3, "values": [np.nan]}) |
616 | self.df3 = self.df.append(df_nan) | |
622 | self.df3 = pd.concat([self.df, df_nan]) | |
617 | 623 | |
618 | 624 | def test_single_color(self): |
619 | 625 | |
881 | 887 | color += ["red", "green", "blue"] |
882 | 888 | |
883 | 889 | self.gdf = GeoDataFrame({"geometry": geom, "color_rgba": color}) |
884 | self.mgdf = self.gdf.dissolve(self.gdf.type) | |
890 | self.mgdf = self.gdf.dissolve(self.gdf.geom_type) | |
885 | 891 | |
886 | 892 | def test_color_single(self): |
887 | 893 | ax = self.gdf.plot(color=self.gdf["color_rgba"]) |
1739 | 1745 | ModuleNotFoundError, match="No module named 'scipy'" |
1740 | 1746 | ): |
1741 | 1747 | self.gdf.plot(kind=kind) |
1748 | return | |
1742 | 1749 | elif kind in _y_kinds: |
1743 | 1750 | kwargs = {"y": "y"} |
1744 | 1751 | elif kind in _xy_kinds: |
16 | 16 | import pytest |
17 | 17 | import numpy as np |
18 | 18 | |
19 | if compat.USE_PYGEOS: | |
20 | import pygeos | |
19 | if compat.USE_SHAPELY_20: | |
20 | import shapely as mod | |
21 | elif compat.USE_PYGEOS: | |
22 | import pygeos as mod | |
21 | 23 | |
22 | 24 | |
23 | 25 | @pytest.mark.skip_no_sindex |
72 | 74 | s = GeoSeries([t1, t2, sq]) |
73 | 75 | assert s.sindex.size == 3 |
74 | 76 | |
77 | @pytest.mark.filterwarnings("ignore:The series.append method is deprecated") | |
75 | 78 | def test_polygons_append(self): |
76 | 79 | t1 = Polygon([(0, 0), (1, 0), (1, 1)]) |
77 | 80 | t2 = Polygon([(0, 0), (1, 1), (0, 1)]) |
697 | 700 | |
698 | 701 | # ------------------------- `nearest` tests ------------------------- # |
699 | 702 | @pytest.mark.skipif( |
700 | compat.USE_PYGEOS, | |
703 | compat.USE_PYGEOS or compat.USE_SHAPELY_20, | |
701 | 704 | reason=("RTree supports sindex.nearest with different behaviour"), |
702 | 705 | ) |
703 | 706 | def test_rtree_nearest_warns(self): |
708 | 711 | df.sindex.nearest((0, 0, 1, 1), num_results=2) |
709 | 712 | |
710 | 713 | @pytest.mark.skipif( |
711 | not (compat.USE_PYGEOS and not compat.PYGEOS_GE_010), | |
714 | compat.USE_SHAPELY_20 or not (compat.USE_PYGEOS and not compat.PYGEOS_GE_010), | |
712 | 715 | reason=("PyGEOS < 0.10 does not support sindex.nearest"), |
713 | 716 | ) |
714 | 717 | def test_pygeos_error(self): |
717 | 720 | df.sindex.nearest(None) |
718 | 721 | |
719 | 722 | @pytest.mark.skipif( |
720 | not (compat.USE_PYGEOS and compat.PYGEOS_GE_010), | |
723 | not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)), | |
721 | 724 | reason=("PyGEOS >= 0.10 is required to test sindex.nearest"), |
722 | 725 | ) |
723 | 726 | @pytest.mark.parametrize("return_all", [True, False]) |
729 | 732 | ], |
730 | 733 | ) |
731 | 734 | def test_nearest_single(self, geometry, expected, return_all): |
732 | geoms = pygeos.points(np.arange(10), np.arange(10)) | |
735 | geoms = mod.points(np.arange(10), np.arange(10)) | |
733 | 736 | df = geopandas.GeoDataFrame({"geometry": geoms}) |
734 | 737 | |
735 | 738 | p = Point(geometry) |
736 | 739 | res = df.sindex.nearest(p, return_all=return_all) |
737 | 740 | assert_array_equal(res, expected) |
738 | 741 | |
739 | p = pygeos.points(geometry) | |
742 | p = mod.points(geometry) | |
740 | 743 | res = df.sindex.nearest(p, return_all=return_all) |
741 | 744 | assert_array_equal(res, expected) |
742 | 745 | |
743 | 746 | @pytest.mark.skipif( |
744 | not compat.USE_PYGEOS or not compat.PYGEOS_GE_010, | |
747 | not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)), | |
745 | 748 | reason=("PyGEOS >= 0.10 is required to test sindex.nearest"), |
746 | 749 | ) |
747 | 750 | @pytest.mark.parametrize("return_all", [True, False]) |
753 | 756 | ], |
754 | 757 | ) |
755 | 758 | def test_nearest_multi(self, geometry, expected, return_all): |
756 | geoms = pygeos.points(np.arange(10), np.arange(10)) | |
759 | geoms = mod.points(np.arange(10), np.arange(10)) | |
757 | 760 | df = geopandas.GeoDataFrame({"geometry": geoms}) |
758 | 761 | |
759 | 762 | ps = [Point(p) for p in geometry] |
760 | 763 | res = df.sindex.nearest(ps, return_all=return_all) |
761 | 764 | assert_array_equal(res, expected) |
762 | 765 | |
763 | ps = pygeos.points(geometry) | |
766 | ps = mod.points(geometry) | |
764 | 767 | res = df.sindex.nearest(ps, return_all=return_all) |
765 | 768 | assert_array_equal(res, expected) |
766 | 769 | |
774 | 777 | assert_array_equal(res, expected) |
775 | 778 | |
776 | 779 | @pytest.mark.skipif( |
777 | not compat.USE_PYGEOS or not compat.PYGEOS_GE_010, | |
780 | not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)), | |
778 | 781 | reason=("PyGEOS >= 0.10 is required to test sindex.nearest"), |
779 | 782 | ) |
780 | 783 | @pytest.mark.parametrize("return_all", [True, False]) |
786 | 789 | ], |
787 | 790 | ) |
788 | 791 | def test_nearest_none(self, geometry, expected, return_all): |
789 | geoms = pygeos.points(np.arange(10), np.arange(10)) | |
792 | geoms = mod.points(np.arange(10), np.arange(10)) | |
790 | 793 | df = geopandas.GeoDataFrame({"geometry": geoms}) |
791 | 794 | |
792 | 795 | res = df.sindex.nearest(geometry, return_all=return_all) |
793 | 796 | assert_array_equal(res, expected) |
794 | 797 | |
795 | 798 | @pytest.mark.skipif( |
796 | not compat.USE_PYGEOS or not compat.PYGEOS_GE_010, | |
799 | not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)), | |
797 | 800 | reason=("PyGEOS >= 0.10 is required to test sindex.nearest"), |
798 | 801 | ) |
799 | 802 | @pytest.mark.parametrize("return_distance", [True, False]) |
809 | 812 | def test_nearest_max_distance( |
810 | 813 | self, expected, max_distance, return_all, return_distance |
811 | 814 | ): |
812 | geoms = pygeos.points(np.arange(10), np.arange(10)) | |
815 | geoms = mod.points(np.arange(10), np.arange(10)) | |
813 | 816 | df = geopandas.GeoDataFrame({"geometry": geoms}) |
814 | 817 | |
815 | 818 | ps = [Point(0.5, 0.5), Point(0, 10)] |
857 | 860 | @pytest.mark.parametrize( |
858 | 861 | "predicate, expected_shape", |
859 | 862 | [ |
860 | (None, (2, 396)), | |
861 | ("intersects", (2, 172)), | |
862 | ("within", (2, 172)), | |
863 | (None, (2, 470)), | |
864 | ("intersects", (2, 213)), | |
865 | ("within", (2, 213)), | |
863 | 866 | ("contains", (2, 0)), |
864 | 867 | ("overlaps", (2, 0)), |
865 | 868 | ("crosses", (2, 0)), |
0 | import warnings | |
1 | ||
0 | 2 | import numpy as np |
1 | 3 | |
2 | 4 | from shapely.geometry import Point, Polygon |
124 | 126 | |
125 | 127 | # assert that with `check_crs=False` the assert passes, and also does not |
126 | 128 | # generate any warning from comparing both geometries with different crs |
127 | with pytest.warns(None) as record: | |
129 | with warnings.catch_warnings(record=True) as record: | |
128 | 130 | assert_geodataframe_equal(df1, df2, check_crs=False) |
129 | 131 | |
130 | 132 | assert len(record) == 0 |
33 | 33 | else: |
34 | 34 | for col in columns: |
35 | 35 | assert col.lower() in (dfcol.lower() for dfcol in df.columns) |
36 | assert Series(df.geometry.type).dropna().eq("MultiPolygon").all() | |
36 | assert Series(df.geometry.geom_type).dropna().eq("MultiPolygon").all() | |
37 | 37 | |
38 | 38 | |
39 | 39 | def get_srid(df): |
47 | 47 | geos_version = "{}.{}.{}".format(*shapely._buildcfg.geos_version) |
48 | 48 | geos_dir = shapely._buildcfg.geos_library_path |
49 | 49 | except Exception: |
50 | geos_version = None | |
51 | geos_dir = None | |
50 | try: | |
51 | from shapely import geos_version_string | |
52 | ||
53 | geos_version = geos_version_string | |
54 | geos_dir = None | |
55 | except Exception: | |
56 | geos_version = None | |
57 | geos_dir = None | |
52 | 58 | |
53 | 59 | try: |
54 | 60 | import fiona |
62 | 68 | gdal_dir = fiona.env.GDALDataFinder().search() |
63 | 69 | except Exception: |
64 | 70 | gdal_dir = None |
71 | ||
72 | if gdal_version is None: | |
73 | try: | |
74 | import pyogrio | |
75 | ||
76 | gdal_version = pyogrio.__gdal_version_string__ | |
77 | gdal_dir = None | |
78 | except Exception: | |
79 | pass | |
80 | try: | |
81 | # get_gdal_data_path is only available in pyogrio >= 0.4.2 | |
82 | from pyogrio import get_gdal_data_path | |
83 | ||
84 | gdal_dir = get_gdal_data_path() | |
85 | except Exception: | |
86 | pass | |
65 | 87 | |
66 | 88 | blob = [ |
67 | 89 | ("GEOS", geos_version), |
6 | 6 | """ |
7 | 7 | import warnings |
8 | 8 | |
9 | import numpy as np | |
9 | 10 | import pandas.api.types |
10 | 11 | from shapely.geometry import Polygon, MultiPolygon, box |
11 | 12 | |
132 | 133 | >>> capitals = geopandas.read_file( |
133 | 134 | ... geopandas.datasets.get_path('naturalearth_cities')) |
134 | 135 | >>> capitals.shape |
135 | (202, 2) | |
136 | (243, 2) | |
136 | 137 | |
137 | 138 | >>> sa_capitals = geopandas.clip(capitals, south_america) |
138 | 139 | >>> sa_capitals.shape |
139 | (12, 2) | |
140 | (15, 2) | |
140 | 141 | """ |
141 | 142 | if not isinstance(gdf, (GeoDataFrame, GeoSeries)): |
142 | 143 | raise TypeError( |
167 | 168 | elif mask_is_list_like: |
168 | 169 | box_mask = mask |
169 | 170 | else: |
170 | box_mask = mask.bounds | |
171 | # Avoid empty tuple returned by .bounds when geometry is empty. A tuple of | |
172 | # all nan values is consistent with the behavior of | |
173 | # {GeoSeries, GeoDataFrame}.total_bounds for empty geometries. | |
174 | # TODO(shapely) can simpely use mask.bounds once relying on Shapely 2.0 | |
175 | box_mask = mask.bounds if not mask.is_empty else (np.nan,) * 4 | |
171 | 176 | box_gdf = gdf.total_bounds |
172 | 177 | if not ( |
173 | 178 | ((box_mask[0] <= box_gdf[2]) and (box_gdf[0] <= box_mask[2])) |
34 | 34 | right = df2.geometry.take(idx2) |
35 | 35 | right.reset_index(drop=True, inplace=True) |
36 | 36 | intersections = left.intersection(right) |
37 | poly_ix = intersections.type.isin(["Polygon", "MultiPolygon"]) | |
37 | poly_ix = intersections.geom_type.isin(["Polygon", "MultiPolygon"]) | |
38 | 38 | intersections.loc[poly_ix] = intersections[poly_ix].buffer(0) |
39 | 39 | |
40 | 40 | # only keep actual intersecting geometries |
91 | 91 | ) |
92 | 92 | new_g.append(new) |
93 | 93 | differences = GeoSeries(new_g, index=df1.index, crs=df1.crs) |
94 | poly_ix = differences.type.isin(["Polygon", "MultiPolygon"]) | |
94 | poly_ix = differences.geom_type.isin(["Polygon", "MultiPolygon"]) | |
95 | 95 | differences.loc[poly_ix] = differences[poly_ix].buffer(0) |
96 | 96 | geom_diff = differences[~differences.is_empty].copy() |
97 | 97 | dfdiff = df1[~differences.is_empty].copy() |
62 | 62 | 4 326625791 North America United States of America USA 18560000.0 MULTIPOLY\ |
63 | 63 | GON (((-122.84000 49.00000, -120.0000... |
64 | 64 | >>> cities.head() |
65 | name geometry | |
66 | 0 Vatican City POINT (12.45339 41.90328) | |
67 | 1 San Marino POINT (12.44177 43.93610) | |
68 | 2 Vaduz POINT (9.51667 47.13372) | |
69 | 3 Luxembourg POINT (6.13000 49.61166) | |
70 | 4 Palikir POINT (158.14997 6.91664) | |
65 | name geometry | |
66 | 0 Vatican City POINT (12.45339 41.90328) | |
67 | 1 San Marino POINT (12.44177 43.93610) | |
68 | 2 Vaduz POINT (9.51667 47.13372) | |
69 | 3 Lobamba POINT (31.20000 -26.46667) | |
70 | 4 Luxembourg POINT (6.13000 49.61166) | |
71 | 71 | |
72 | 72 | >>> cities_w_country_data = geopandas.sjoin(cities, countries) |
73 | 73 | >>> cities_w_country_data.head() # doctest: +SKIP |
359 | 359 | how: str, |
360 | 360 | return_distance: bool, |
361 | 361 | ): |
362 | if not (compat.PYGEOS_GE_010 and compat.USE_PYGEOS): | |
362 | if not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)): | |
363 | 363 | raise NotImplementedError( |
364 | "Currently, only PyGEOS >= 0.10.0 supports `nearest_all`. " | |
365 | + compat.INSTALL_PYGEOS_ERROR | |
364 | "Currently, only PyGEOS >= 0.10.0 or Shapely >= 2.0 supports " | |
365 | "`nearest_all`. " + compat.INSTALL_PYGEOS_ERROR | |
366 | 366 | ) |
367 | 367 | # use the opposite of the join direction for the index |
368 | 368 | use_left_as_sindex = how == "right" |
449 | 449 | multi = buffered_locations.dissolve(by="type").reset_index() |
450 | 450 | clipped = clip(multi, masks) |
451 | 451 | assert clipped.geom_type[0] == "Polygon" |
452 | ||
453 | ||
454 | @pytest.mark.filterwarnings("ignore:All-NaN slice encountered") | |
455 | @pytest.mark.parametrize( | |
456 | "mask", | |
457 | [ | |
458 | Polygon(), | |
459 | (np.nan,) * 4, | |
460 | (np.nan, 0, np.nan, 1), | |
461 | GeoSeries([Polygon(), Polygon()], crs="EPSG:3857"), | |
462 | GeoSeries([Polygon(), Polygon()], crs="EPSG:3857").to_frame(), | |
463 | GeoSeries([], crs="EPSG:3857"), | |
464 | GeoSeries([], crs="EPSG:3857").to_frame(), | |
465 | ], | |
466 | ) | |
467 | def test_clip_empty_mask(buffered_locations, mask): | |
468 | """Test that clipping with empty mask returns an empty result.""" | |
469 | clipped = clip(buffered_locations, mask) | |
470 | assert_geodataframe_equal( | |
471 | clipped, | |
472 | GeoDataFrame([], columns=["geometry", "type"], crs="EPSG:3857"), | |
473 | check_index_type=False, | |
474 | ) | |
475 | clipped = clip(buffered_locations.geometry, mask) | |
476 | assert_geoseries_equal(clipped, GeoSeries([], crs="EPSG:3857")) |
16 | 16 | import pytest |
17 | 17 | |
18 | 18 | |
19 | TEST_NEAREST = compat.PYGEOS_GE_010 and compat.USE_PYGEOS | |
19 | TEST_NEAREST = compat.USE_SHAPELY_20 or (compat.PYGEOS_GE_010 and compat.USE_PYGEOS) | |
20 | 20 | |
21 | 21 | |
22 | 22 | pytestmark = pytest.mark.skip_no_sindex |
268 | 268 | empty = sjoin(not_in, polygons, how="inner", predicate="intersects") |
269 | 269 | assert empty.empty |
270 | 270 | |
271 | @pytest.mark.parametrize("predicate", ["intersects", "contains", "within"]) | |
271 | @pytest.mark.parametrize( | |
272 | "predicate", | |
273 | [ | |
274 | "contains", | |
275 | "contains_properly", | |
276 | "covered_by", | |
277 | "covers", | |
278 | "crosses", | |
279 | "intersects", | |
280 | "touches", | |
281 | "within", | |
282 | ], | |
283 | ) | |
272 | 284 | @pytest.mark.parametrize( |
273 | 285 | "empty", |
274 | 286 | [ |
391 | 403 | df = sjoin(self.pointdf, self.polydf, how="left") |
392 | 404 | assert df.shape == (21, 8) |
393 | 405 | for i, row in df.iterrows(): |
394 | assert row.geometry.type == "Point" | |
406 | assert row.geometry.geom_type == "Point" | |
395 | 407 | assert "pointattr1" in df.columns |
396 | 408 | assert "BoroCode" in df.columns |
397 | 409 | |
402 | 414 | assert df.shape == (12, 8) |
403 | 415 | assert df.shape == df2.shape |
404 | 416 | for i, row in df.iterrows(): |
405 | assert row.geometry.type == "MultiPolygon" | |
417 | assert row.geometry.geom_type == "MultiPolygon" | |
406 | 418 | for i, row in df2.iterrows(): |
407 | assert row.geometry.type == "MultiPolygon" | |
419 | assert row.geometry.geom_type == "MultiPolygon" | |
408 | 420 | |
409 | 421 | def test_sjoin_inner(self): |
410 | 422 | df = sjoin(self.pointdf, self.polydf, how="inner") |
518 | 530 | def test_sjoin_empty_geometries(self): |
519 | 531 | # https://github.com/geopandas/geopandas/issues/944 |
520 | 532 | empty = GeoDataFrame(geometry=[GeometryCollection()] * 3) |
521 | df = sjoin(self.pointdf.append(empty), self.polydf, how="left") | |
533 | df = sjoin(pd.concat([self.pointdf, empty]), self.polydf, how="left") | |
522 | 534 | assert df.shape == (24, 8) |
523 | df2 = sjoin(self.pointdf, self.polydf.append(empty), how="left") | |
535 | df2 = sjoin(self.pointdf, pd.concat([self.polydf, empty]), how="left") | |
524 | 536 | assert df2.shape == (21, 8) |
525 | 537 | |
526 | 538 | @pytest.mark.parametrize("predicate", ["intersects", "within", "contains"]) |
558 | 570 | cities_with_country = sjoin( |
559 | 571 | self.cities, countries, how="inner", predicate="intersects" |
560 | 572 | ) |
561 | assert cities_with_country.shape == (172, 4) | |
573 | assert cities_with_country.shape == (213, 4) | |
562 | 574 | |
563 | 575 | |
564 | 576 | @pytest.mark.skipif( |
570 | 582 | df2 = geopandas.GeoDataFrame({"geometry": []}) |
571 | 583 | with pytest.raises( |
572 | 584 | NotImplementedError, |
573 | match="Currently, only PyGEOS >= 0.10.0 supports `nearest_all`", | |
585 | match="Currently, only PyGEOS >= 0.10.0 or Shapely >= 2.0 supports", | |
574 | 586 | ): |
575 | 587 | sjoin_nearest(df1, df2) |
576 | 588 |
31 | 31 | # must be the same. If there is more than one element, |
32 | 32 | # they cannot be Multi*, i.e., can't pass in combination of |
33 | 33 | # Point and MultiPoint... or even just MultiPoint |
34 | t = x[0].type | |
35 | if not all(g.type == t for g in x): | |
34 | t = x[0].geom_type | |
35 | if not all(g.geom_type == t for g in x): | |
36 | 36 | raise ValueError("Geometry type must be homogeneous") |
37 | 37 | if len(x) > 1 and t.startswith("Multi"): |
38 | 38 | raise ValueError("Cannot collect {0}. Must have single geometries".format(t)) |