Skip to main content

Footprint Facility Library

Project description

Footprint display facility

This set of functions includes a set of facilities to manipulate the footprints to be displayed into leaflet, OpenLayer, MapBox and Cesium.

It manages:

  • Split polygon to multi-polygon when crossing anti-meridian
  • Manage polar area including polar point inside the polygon boundary.
  • Manage long footprints with overlays
  • Manage thin footprints collapsing polygon in a single lineString

Preliminary information regrading dependencies

We recommend the usage of a virtual environment to embed dependencies. The dependencies are automatically installed during this module installation:

  • pyproj
  • numpy
  • shapely
  • geojson

Details can be found in requirements.txt file.

Library usage

The shapely library provides a set of feature that could be a solution to handle the footprints identified in this study. Unfortunately the library is also concerned by the display issue. For example the intersections operation in shapely fails when footprint contains antimeridian. This later library is mainly used to handle geometries types and formats conversions.
Numpy is used to manipulate the list of coordinates to fix the issues caused by lat/lon limits of the geometry.

Installation

pip install footprint-facility

The module is retrieved from pypi.org service.

Available methods

The rework_to_polygon_geometry(geometry: Geometry) -> Geometry method reworks the geometry parameter to manage polar and antimeridian singularities. This process implements the Polar inclusive algorithm. The objective of this algorithm is to add the North/South Pole into the list of coordinates of the polygon at the antimeridian cross.

When the geometry contains the pole the single polygon geometry including the pole in its border point list is properly interpreted by displays systems. When the geometry does not contain the pole, the geometry is split among the antimeridian line.

from shapely import wkt
from footprint_facility import rework_to_polygon_geometry

wkt_geometry='POLYGON((175.812988 -70.711365,-175.61055 -68.469902,-172.383392 -69.75074,178.803558 -72.136864,175.812988 -70.711365))'

geometry = wkt.loads(wkt_geometry)
reworked = rework_to_polygon_geometry(geometry)

print(reworked)

prints

MULTIPOLYGON (((-180 -69.61708714060345, -175.61055 -68.469902, -172.383392 -69.75074, -180 -71.81292858935237, -180 -69.61708714060345)), 
              ((175.812988 -70.711365, 180 -69.61708714060345, 180 -71.81292858935237, 178.803558 -72.136864, 175.812988 -70.711365)))

This sample footprint is extracted from product S1A_EW_SL1__1_DH_20240118T082036_20240118T082109_052158_064DFF_6639 not anymore accessible in data hub, but derived products are available such as S1A_EW_OCN__2SDH_20240118T082040_20240118T082108_052158_064DFF_66CB.SAFE. This data is accessible in the catalog following this odata link

View the output in Leaflet:
Leaflet
View the output in MapBox (https://geojson.io):
MapBox display
View the output in OpenLayer:
OpenLayer Display
View the output in Cesium
Cesium display

The same method also manage footprint passing over pole area, for example product called S3A_SL_1_RBT____20240101T234743_20240101T235043_20240103T092302_0179_107_230_1440_PS1_O_NT_004 avaialble in catalog following this odata link

img_8.png

from shapely import wkt
from footprint_facility import rework_to_polygon_geometry

wkt_geometry='POLYGON((148.66 83.7852,152.864 83.7881,157.085 83.7477,161.246 83.6889,165.27 83.594,169.107 83.4668,172.868 83.3119,176.409 83.1294,179.768 82.9261,-177.09 82.6993,-174.112 82.4387,-171.356 82.1771,-168.826 81.8915,-166.413 81.5862,-164.187 81.2727,-162.124 80.9494,-160.207 80.6132,-158.378 80.271,-156.704 79.911,-155.155 79.5456,-153.715 79.1749,-152.364 78.797,-151.091 78.4225,-149.916 78.0337,-148.797 77.6345,-147.746 77.2423,-146.768 76.845,-145.838 76.4404,-144.979 76.034,-144.147 75.6258,-143.843 75.4683,-133.508 76.4059,-121.888 76.8854,-109.902 76.8263,-98.5116 76.2384,-98.4639 76.2441,-98.2252 76.4066,-97.6125 76.8338,-96.9351 77.2695,-96.2223 77.7014,-95.4704 78.1301,-94.67 78.554,-93.7717 78.9733,-92.7923 79.4003,-91.7963 79.8163,-90.6757 80.2289,-89.4578 80.6379,-88.1285 81.0437,-86.6584 81.4456,-85.117 81.8441,-83.3788 82.2296,-81.4475 82.6096,-79.3161 82.9811,-76.958 83.3328,-74.2774 83.6856,-71.3901 84.0125,-68.1774 84.3328,-64.59 84.6283,-60.5631 84.9038,-55.9807 85.139,-51.1282 85.3472,-45.8032 85.5231,-40.1262 85.6577,-34.1967 85.7497,-28.0603 85.8022,-21.787 85.7844,-21.7786 85.793,-14.5003 88.3719,133.773 88.9159,146.77 86.3469,148.66 83.7852))'
geometry = wkt.loads(wkt_geometry)
reworked = rework_to_polygon_geometry(geometry)

print(reworked)

returns

POLYGON ((148.66 83.7852, 152.864 83.7881, 157.085 83.7477, 161.246 83.6889, 165.27 83.594, 169.107 83.4668, 172.868 83.3119, 176.409 83.1294, 179.768 82.9261, 180 82.90935346912795, 180 90, -180 90, -180 82.90935346912795, -177.09 82.6993, -174.112 82.4387, -171.356 82.1771, -168.826 81.8915, -166.413 81.5862, -164.187 81.2727, -162.124 80.9494, -160.207 80.6132, -158.378 80.271, -156.704 79.911, -155.155 79.5456, -153.715 79.1749, -152.364 78.797, -151.091 78.4225, -149.916 78.0337, -148.797 77.6345, -147.746 77.2423, -146.768 76.845, -145.838 76.4404, -144.979 76.034, -144.147 75.6258, -143.843 75.4683, -133.508 76.4059, -121.888 76.8854, -109.902 76.8263, -98.5116 76.2384, -98.4639 76.2441, -98.2252 76.4066, -97.6125 76.8338, -96.9351 77.2695, -96.2223 77.7014, -95.4704 78.1301, -94.67 78.554, -93.7717 78.9733, -92.7923 79.4003, -91.7963 79.8163, -90.6757 80.2289, -89.4578 80.6379, -88.1285 81.0437, -86.6584 81.4456, -85.117 81.8441, -83.3788 82.2296, -81.4475 82.6096, -79.3161 82.9811, -76.958 83.3328, -74.2774 83.6856, -71.3901 84.0125, -68.1774 84.3328, -64.59 84.6283, -60.5631 84.9038, -55.9807 85.139, -51.1282 85.3472, -45.8032 85.5231, -40.1262 85.6577, -34.1967 85.7497, -28.0603 85.8022, -21.787 85.7844, -21.7786 85.793, -14.5003 88.3719, 133.773 88.9159, 146.77 86.3469, 148.66 83.7852))

View the output in Leaflet:
Leaflet display
View the output in MapBox (https://geojson.io)
MapBox 2D display MapBox 3D display
View the output in OpenLayer:
OpenLayer Display
View the output int Cesium
Cesium display

A more complex footprint that includes overlapping can be retrieved from Sentinel-3 SLSTR products with name S3A_SL_2_WST____20240224T211727_20240224T225826_20240226T033733_6059_109_228______MAR_O_NT_003.SEN3 also available in catalog following this odata link
img_10.png

from shapely import wkt
from footprint_facility import rework_to_polygon_geometry

wkt_geometry='POLYGON ((75.4813 -72.4831, 75.4641 -72.9385, 75.4825 -73.3926, 75.4667 -73.8533, 75.4859 -74.3095, 75.4892 -74.7668, 75.4863 -75.2255, 75.4766 -75.688, 75.5071 -76.138, 75.4855 -76.5976, 75.4994 -77.0502, 75.4912 -77.5039, 75.4804 -77.9674, 75.5255 -78.4229, 75.506 -78.8781, 75.5216 -79.339, 75.5372 -79.7913, 75.5373 -80.2465, 75.5329 -80.7048, 75.5299 -81.1598, 75.5171 -81.623, 75.5639 -82.078, 75.5458 -82.5362, 75.5075 -82.9892, 75.5671 -83.4508, 75.594 -83.9015, 75.5601 -84.3631, 75.5772 -84.8172, 75.5759 -85.2729, 75.6882 -85.7284, 75.6155 -85.8996, 99.0441 -85.5436, 123.827 -83.9119, 136.984 -81.6753, 144.524 -79.2029, 149.351 -76.6243, 152.729 -73.9903, 155.254 -71.3237, 157.243 -68.6363, 158.872 -65.9344, 160.25 -63.2219, 161.447 -60.5012, 162.509 -57.7739, 163.467 -55.0411, 164.345 -52.3034, 165.16 -49.5614, 165.925 -46.8156, 166.65 -44.0663, 167.342 -41.3137, 168.008 -38.558, 168.652 -35.7995, 169.279 -33.0384, 169.893 -30.2748, 170.495 -27.5089, 171.09 -24.741, 171.679 -21.9712, 172.265 -19.1997, 172.849 -16.4268, 173.433 -13.6528, 174.019 -10.8778, 174.61 -8.10221, 175.205 -5.32624, 175.808 -2.55022, 176.42 0.225521, 177.043 3.00064, 177.679 5.77478, 178.33 8.54753, 178.999 11.3185, 179.688 14.0872, -179.6 16.8532, -178.861 19.6159, -178.092 22.3748, -177.288 25.1292, -176.444 27.8785, -175.554 30.6218, -174.611 33.3581, -173.607 36.0865, -172.531 38.8056, -171.373 41.5139, -170.116 44.2097, -168.743 46.8906, -167.231 49.5539, -165.552 52.1959, -163.669 54.8122, -161.536 57.3968, -159.092 59.9417, -156.259 62.4363, -152.931 64.8659, -148.97 67.21, -144.196 69.4397, -138.378 71.514, -131.251 73.3747, -122.56 74.9431, -112.195 76.1196, -100.397 76.7995, -87.8992 76.9054, -75.7446 76.4234, -64.8174 75.4121, -55.5302 73.9711, -47.8722 72.2041, -41.6199 70.1983, -36.5031 68.0184, -32.2759 65.7113, -28.7407 63.3095, -25.7211 60.8146, -23.1527 58.2859, -20.9197 55.7142, -18.956 53.1084, -17.2107 50.4749, -15.6443 47.8187, -14.2259 45.1438, -12.931 42.4532, -11.7401 39.7492, -10.6373 37.0339, -9.60971 34.3089, -8.64659 31.5754, -7.73911 28.8348, -6.87981 26.0878, -6.06243 23.3354, -5.28156 20.5783, -4.53257 17.8171, -3.81149 15.0525, -3.11477 12.285, -2.43933 9.51511, -1.78241 6.74326, -1.14151 3.9699, -0.51437 1.19541, 0.101093 -1.57983, 0.706828 -4.35545, 1.30468 -7.13115, 1.89642 -9.90659, 2.48378 -12.6815, 3.0685 -15.4556, 3.65232 -18.2286, 4.23706 -21.0002, 4.82464 -23.7704, 5.41711 -26.5387, 6.01674 -29.305, 6.62603 -32.0692, 7.24788 -34.831, 7.88562 -37.5902, 8.54317 -40.3466, 9.22526 -43.1001, 9.93764 -45.8504, 10.6874 -48.5973, 11.4837 -51.3405, 12.3381 -54.0796, 13.266 -56.8141, 14.2879 -59.5433, 15.4325 -62.2664, 16.7404 -64.9819, 18.2714 -67.6878, 20.1175 -70.3808, 22.4275 -73.0557, 25.456 -75.7029, 29.6757 -78.3048, 36.0471 -80.8235, 46.7204 -83.1675, 66.5853 -85.0811, 100.702 -85.909, 100.825 -85.9, 100.8 -85.7265, 100.836 -85.2741, 100.835 -84.8184, 100.817 -84.3644, 100.753 -83.9016, 100.799 -83.4425, 100.759 -82.9905, 100.797 -82.5372, 100.743 -82.0785, 100.767 -81.6235, 100.78 -81.16, 100.732 -80.7044, 100.74 -80.2457, 100.733 -79.7918, 100.719 -79.34, 100.758 -78.8787, 100.726 -78.4261, 100.732 -77.9704, 100.741 -77.5078, 100.718 -77.0519, 100.734 -76.5887, 100.728 -76.1405, 100.756 -75.6831, 100.71 -75.2283, 100.717 -74.7686, 100.712 -74.3119, 100.75 -73.8494, 100.706 -73.4005, 100.75 -72.9462, 100.719 -72.4834, 100.689 -72.4834, 91.3873 -72.2634, 82.4869 -71.616, 74.3385 -70.5836, 67.1017 -69.2238, 60.7981 -67.5957, 55.3608 -65.7531, 50.6834 -63.7411, 46.6526 -61.5953, 43.1618 -59.3436, 40.1192 -57.0075, 37.4483 -54.6035, 35.086 -52.1444, 32.9811 -49.64, 31.0922 -47.0982, 29.3854 -44.5249, 27.8333 -41.9253, 26.4133 -39.3032, 25.1067 -36.6619, 23.8983 -34.0041, 22.7752 -31.332, 21.7265 -28.6474, 20.7433 -25.952, 19.8177 -23.2472, 18.9432 -20.5341, 18.1142 -17.8139, 17.3258 -15.0873, 16.5739 -12.3553, 15.8547 -9.61856, 15.165 -6.8778, 14.5022 -4.13359, 13.8636 -1.38652, 13.2472 1.36292, 12.6511 4.11426, 12.0734 6.86705, 11.5129 9.62091, 10.968 12.3755, 10.4375 15.1304, 9.92058 17.8853, 9.41609 20.64, 8.92326 23.3942, 8.44135 26.1477, 7.96971 28.9002, 7.5078 31.6517, 7.05516 34.4018, 6.61142 37.1506, 6.17631 39.8979, 5.74967 42.6435, 5.33147 45.3876, 4.92181 48.1299, 4.521 50.8705, 4.12959 53.6094, 3.74844 56.3466, 3.37891 59.0822, 3.02299 61.8161, 2.68373 64.5486, 2.36316 67.3032, 2.07425 70.0328, 1.82645 72.7612, 1.64156 75.4883, 1.56185 78.2142, 1.68051 80.9387, 2.25149 83.6613, 4.29171 86.3786, 20.3724 89.0493, 166.781 88.1169, 172.888 85.413, 174.157 82.693, 174.516 79.9695, 174.547 77.2444, 174.423 74.518, 174.213 71.7903, 173.948 69.0614, 173.648 66.3312, 173.322 63.5996, 172.977 60.8664, 172.616 58.1317, 172.242 55.3954, 171.857 52.6574, 171.462 49.9177, 171.058 47.1763, 170.646 44.4331, 170.224 41.6883, 169.795 38.9418, 169.357 36.1937, 168.91 33.4442, 168.454 30.6933, 167.989 27.9411, 167.514 25.1879, 167.028 22.4339, 166.531 19.6791, 166.022 16.924, 165.501 14.1687, 164.965 11.4136, 164.415 8.65892, 163.849 5.90507, 163.265 3.15242, 162.661 0.401369, 162.038 -2.34762, 161.391 -5.09407, 160.719 -7.83745, 160.019 -10.5772, 159.289 -13.3126, 158.524 -16.0431, 157.722 -18.7679, 156.878 -21.486, 155.986 -24.1966, 155.041 -26.8985, 154.035 -29.5905, 152.962 -32.2711, 151.81 -34.9387, 150.568 -37.5912, 149.224 -40.2263, 147.759 -42.841, 146.156 -45.4321, 144.388 -47.995, 142.428 -50.5246, 140.237 -53.0142, 137.773 -55.4553, 134.979 -57.8371, 131.788 -60.1457, 128.119 -62.3629, 123.873 -64.4652, 118.942 -66.4222, 113.21 -68.1949, 106.583 -69.7353, 99.0172 -70.9868, 90.5777 -71.8897, 81.482 -72.3907, 75.4813 -72.4831))'
geometry = wkt.loads(wkt_geometry)
reworked = rework_to_polygon_geometry(geometry)

print(reworked)

returns

POLYGON ((75.58244101140099 -85.29943099459616, 66.5853 -85.0811, 46.7204 -83.1675, 36.0471 -80.8235, 29.6757 -78.3048, 25.456 -75.7029, 22.4275 -73.0557, 20.1175 -70.3808, 18.2714 -67.6878, 16.7404 -64.9819, 15.4325 -62.2664, 14.2879 -59.5433, 13.266 -56.8141, 12.3381 -54.0796, 11.4837 -51.3405, 10.6874 -48.5973, 9.93764 -45.8504, 9.22526 -43.1001, 8.54317 -40.3466, 7.88562 -37.5902, 7.24788 -34.831, 6.62603 -32.0692, 6.01674 -29.305, 5.41711 -26.5387, 4.82464 -23.7704, 4.23706 -21.0002, 3.65232 -18.2286, 3.0685 -15.4556, 2.48378 -12.6815, 1.89642 -9.90659, 1.30468 -7.13115, 0.706828 -4.35545, 0.101093 -1.57983, -0.51437 1.19541, -1.14151 3.9699, -1.78241 6.74326, -2.43933 9.51511, -3.11477 12.285, -3.81149 15.0525, -4.53257 17.8171, -5.28156 20.5783, -6.06243 23.3354, -6.87981 26.0878, -7.73911 28.8348, -8.64659 31.5754, -9.60971 34.3089, -10.6373 37.0339, -11.7401 39.7492, -12.931 42.4532, -14.2259 45.1438, -15.6443 47.8187, -17.2107 50.4749, -18.956 53.1084, -20.9197 55.7142, -23.1527 58.2859, -25.7211 60.8146, -28.7407 63.3095, -32.2759 65.7113, -36.5031 68.0184, -41.6199 70.1983, -47.8722 72.2041, -55.5302 73.9711, -64.8174 75.4121, -75.7446 76.4234, -87.8992 76.9054, -100.397 76.7995, -112.195 76.1196, -122.56 74.9431, -131.251 73.3747, -138.378 71.514, -144.196 69.4397, -148.97 67.21, -152.931 64.8659, -156.259 62.4363, -159.092 59.9417, -161.536 57.3968, -163.669 54.8122, -165.552 52.1959, -167.231 49.5539, -168.743 46.8906, -170.116 44.2097, -171.373 41.5139, -172.531 38.8056, -173.607 36.0865, -174.611 33.3581, -175.554 30.6218, -176.444 27.8785, -177.288 25.1292, -178.092 22.3748, -178.861 19.6159, -179.6 16.8532, -180 15.299267415730355, -180 90, 180 90, 180 15.299267415730355, 179.688 14.0872, 178.999 11.3185, 178.33 8.54753, 177.679 5.77478, 177.043 3.00064, 176.42 0.225521, 175.808 -2.55022, 175.205 -5.32624, 174.61 -8.10221, 174.019 -10.8778, 173.433 -13.6528, 172.849 -16.4268, 172.265 -19.1997, 171.679 -21.9712, 171.09 -24.741, 170.495 -27.5089, 169.893 -30.2748, 169.279 -33.0384, 168.652 -35.7995, 168.008 -38.558, 167.342 -41.3137, 166.65 -44.0663, 165.925 -46.8156, 165.16 -49.5614, 164.345 -52.3034, 163.467 -55.0411, 162.509 -57.7739, 161.447 -60.5012, 160.25 -63.2219, 158.872 -65.9344, 157.243 -68.6363, 155.254 -71.3237, 152.729 -73.9903, 149.351 -76.6243, 144.524 -79.2029, 136.984 -81.6753, 123.827 -83.9119, 100.82387906202558 -85.42641978721186, 100.8 -85.7265, 100.825 -85.9, 100.702 -85.909, 90.8040265674998 -85.66880876800023, 75.6155 -85.8996, 75.6882 -85.7284, 75.58244101140099 -85.29943099459616), (88.11476108761217 -72.02536107007776, 90.5777 -71.8897, 99.0172 -70.9868, 106.583 -69.7353, 113.21 -68.1949, 118.942 -66.4222, 123.873 -64.4652, 128.119 -62.3629, 131.788 -60.1457, 134.979 -57.8371, 137.773 -55.4553, 140.237 -53.0142, 142.428 -50.5246, 144.388 -47.995, 146.156 -45.4321, 147.759 -42.841, 149.224 -40.2263, 150.568 -37.5912, 151.81 -34.9387, 152.962 -32.2711, 154.035 -29.5905, 155.041 -26.8985, 155.986 -24.1966, 156.878 -21.486, 157.722 -18.7679, 158.524 -16.0431, 159.289 -13.3126, 160.019 -10.5772, 160.719 -7.83745, 161.391 -5.09407, 162.038 -2.34762, 162.661 0.401369, 163.265 3.15242, 163.849 5.90507, 164.415 8.65892, 164.965 11.4136, 165.501 14.1687, 166.022 16.924, 166.531 19.6791, 167.028 22.4339, 167.514 25.1879, 167.989 27.9411, 168.454 30.6933, 168.91 33.4442, 169.357 36.1937, 169.795 38.9418, 170.224 41.6883, 170.646 44.4331, 171.058 47.1763, 171.462 49.9177, 171.857 52.6574, 172.242 55.3954, 172.616 58.1317, 172.977 60.8664, 173.322 63.5996, 173.648 66.3312, 173.948 69.0614, 174.213 71.7903, 174.423 74.518, 174.547 77.2444, 174.516 79.9695, 174.157 82.693, 172.888 85.413, 166.781 88.1169, 20.3724 89.0493, 4.29171 86.3786, 2.25149 83.6613, 1.68051 80.9387, 1.56185 78.2142, 1.64156 75.4883, 1.82645 72.7612, 2.07425 70.0328, 2.36316 67.3032, 2.68373 64.5486, 3.02299 61.8161, 3.37891 59.0822, 3.74844 56.3466, 4.12959 53.6094, 4.521 50.8705, 4.92181 48.1299, 5.33147 45.3876, 5.74967 42.6435, 6.17631 39.8979, 6.61142 37.1506, 7.05516 34.4018, 7.5078 31.6517, 7.96971 28.9002, 8.44135 26.1477, 8.92326 23.3942, 9.41609 20.64, 9.92058 17.8853, 10.4375 15.1304, 10.968 12.3755, 11.5129 9.62091, 12.0734 6.86705, 12.6511 4.11426, 13.2472 1.36292, 13.8636 -1.38652, 14.5022 -4.13359, 15.165 -6.8778, 15.8547 -9.61856, 16.5739 -12.3553, 17.3258 -15.0873, 18.1142 -17.8139, 18.9432 -20.5341, 19.8177 -23.2472, 20.7433 -25.952, 21.7265 -28.6474, 22.7752 -31.332, 23.8983 -34.0041, 25.1067 -36.6619, 26.4133 -39.3032, 27.8333 -41.9253, 29.3854 -44.5249, 31.0922 -47.0982, 32.9811 -49.64, 35.086 -52.1444, 37.4483 -54.6035, 40.1192 -57.0075, 43.1618 -59.3436, 46.6526 -61.5953, 50.6834 -63.7411, 55.3608 -65.7531, 60.7981 -67.5957, 67.1017 -69.2238, 74.3385 -70.5836, 82.4869 -71.616, 88.11476108761217 -72.02536107007776))

View the output in Leaflet:
Leaflet display

View the output in MapBox (https://geojson.io):
MapBox 2D display MapBox 3D display

View the output in OpenLayer: OpenLayer Display

View the output in Cesium
Cesium display

The rework_to_linestring_geometry(geometry: Geometry) -> Geometry elaborates more or less complex linestring geometry from thin polygon and manage the antimeridian cross. For example, the product named S3A_SR_2_LAN_LI_20240302T235923_20240303T001845_20240304T182116_1161_109_330______PS1_O_ST_005 is available in catalog and can be accessed by following this odata link
img_11.png

from shapely import wkt
from footprint_facility import rework_to_linestring_geometry

wkt_geometry='POLYGON ((-44.6919 59.6012, -45.9328 61.3301, -47.303 63.0483, -48.8281 64.7538, -50.5403 66.4439, -52.4808 68.1153, -54.7026 69.7634, -57.2749 71.3821, -60.2887 72.9632, -63.8633 74.4954, -68.1552 75.9627, -73.3645 77.3427, -79.7337 78.6037, -87.5189 79.7021, -96.9041 80.5808, -107.835 81.1742, -119.831 81.4228, -131.996 81.2971, -143.357 80.8124, -153.278 80.0208, -161.581 78.9883, -168.392 77.7757, -173.958 76.431, -178.529 74.9898, 177.678 73.477, 174.493 71.9106, 171.786 70.3033, 169.456 68.6641, 167.429 66.9998, 165.646 65.3154, 164.063 63.6146, 162.645 61.9002, 161.363 60.1746, 160.197 58.4395, 159.129 56.6962, 158.144 54.946, 157.23 53.1896, 156.378 51.4279, 155.579 49.6615, 155.582 49.661, 156.38 51.4274, 157.233 53.1891, 158.146 54.9454, 159.132 56.6957, 160.201 58.4389, 161.367 60.174, 162.648 61.8996, 164.067 63.6139, 165.65 65.3147, 167.433 66.9991, 169.461 68.6633, 171.79 70.3024, 174.498 71.9097, 177.683 73.4761, -178.524 74.9887, -173.952 76.4299, -168.386 77.7744, -161.575 78.9869, -153.273 80.0193, -143.353 80.8107, -131.994 81.2953, -119.831 81.421, -107.837 81.1725, -96.9086 80.5792, -87.5244 79.7006, -79.7396 78.6023, -73.3705 77.3414, -68.1609 75.9616, -63.8688 74.4944, -60.2939 72.9623, -57.2799 71.3812, -54.7072 69.7626, -52.4851 68.1145, -50.5444 66.4432, -48.832 64.7531, -47.3067 63.0476, -45.9363 61.3295, -44.6952 59.6006, -44.6919 59.6012))'
geometry = wkt.loads(wkt_geometry)
reworked = rework_to_linestring_geometry(geometry)

print(reworked)

results the following multilinestring geometry:

MULTILINESTRING ((-180 74.40789473684211, -178.5 75, -174 76.4, -168.4 77.8, -161.6 79, -153.3 80, -143.4 80.8, -132 81.3, -119.8 81.4, -107.8 81.2, -96.9 80.6, -87.5 79.7, -79.7 78.6, -73.4 77.3, -68.2 76, -63.9 74.5, -60.3 73, -57.3 71.4, -54.7 69.8, -52.5 68.1, -50.5 66.4, -48.8 64.8, -47.3 63, -45.9 61.3, -44.7 59.6), (155.6 49.7, 156.4 51.4, 157.2 53.2, 158.1 54.9, 159.1 56.7, 160.2 58.4, 161.4 60.2, 162.6 61.9, 164.1 63.6, 165.6 65.3, 167.4 67, 169.5 68.7, 171.8 70.3, 174.5 71.9, 177.7 73.5, 180 74.40789473684211))

View the output in Leaflet:
Leaflet display

View the output in MapBox (https://geojson.io):
MapBox 2D display MapBox 3D display

View the output in OpenLayer:
OpenLayer Display

View the output in Cesium
Cesium display

The check_cross_antimeridian(geometry: Geometry) -> bool method checks if the footprint pass over the antimeridian. The recognition of the antimeridian line is based on array list single comparison and can be considered fast.

from shapely import wkt
from footprint_facility import check_cross_antimeridian

wkt_geometry='POLYGON((175.812988 -70.711365,-175.61055 -68.469902,-172.383392 -69.75074,178.803558 -72.136864,175.812988 -70.711365))'

geometry = wkt.loads(wkt_geometry)
print(check_cross_antimeridian(geometry))

returns

True

Export facilities

The to_geojson(geometry: Geometry) -> str returns a geojson formatted string of the given geometry.

from shapely import wkt
from footprint_facility import to_geojson, rework_to_polygon_geometry

wkt_geometry='POLYGON((175.812988 -70.711365,-175.61055 -68.469902,-172.383392 -69.75074,178.803558 -72.136864,175.812988 -70.711365))'

geometry = wkt.loads(wkt_geometry)
print(to_geojson(rework_to_polygon_geometry(geometry)))

returns

{
  "features": [
    {
      "geometry": {
        "coordinates": [
          [
            [
              [
                -180,
                -69.617087
              ],
              [
                -175.61055,
                -68.469902
              ],
              [
                -172.383392,
                -69.75074
              ],
              [
                -180,
                -71.812929
              ],
              [
                -180,
                -69.617087
              ]
            ]
          ],
          [
            [
              [
                175.812988,
                -70.711365
              ],
              [
                180,
                -69.617087
              ],
              [
                180,
                -71.812929
              ],
              [
                178.803558,
                -72.136864
              ],
              [
                175.812988,
                -70.711365
              ]
            ]
          ]
        ],
        "type": "MultiPolygon"
      },
      "id": "15943947-67bd-4515-87c5-454fdc23db2e",
      "properties": {},
      "type": "Feature"
    }
  ],
  "type": "FeatureCollection"
}

The to_wkt(geometry: Geometry) -> str returns a WKT formatted string of the given geometry.

from shapely import wkt
from footprint_facility import to_wkt, rework_to_polygon_geometry

wkt_geometry='POLYGON((175.812988 -70.711365,-175.61055 -68.469902,-172.383392 -69.75074,178.803558 -72.136864,175.812988 -70.711365))'

geometry = wkt.loads(wkt_geometry)
print(to_wkt(rework_to_polygon_geometry(geometry)))

returns

MULTIPOLYGON (
   ((-180 -69.61708714060345, -175.61055 -68.469902, -172.383392 -69.75074, -180 -71.81292858935237, -180 -69.61708714060345)),
   ((175.812988 -70.711365, 180 -69.61708714060345, 180 -71.81292858935237, 178.803558 -72.136864, 175.812988 -70.711365)))

Footprint simplification function

Products such as "S3 Synergy" contains up to 290 points whereas they footprints are single rectangles. Others huge along track acquisition also contains greate number of coordinates. The footprint containing many points could be simplify improve indexing and display services.
This facility module decorates 2 identified algorithms to simplify these footprints:

  • Douglas–Peucker algorithm that is implemented into the shapely geometry library wrapped here as footprint_facility.simplify.
  • Convex hull algorithm can be used to reduce de number of points assembly footprint with many polygons. The algorithm is also already implemented in the shapely library.

The Douglas-Peucker algorithm

The Douglas-Peucker algorithm is an algorithm that decimates a curve composed of line segments to a similar curve with fewer points. It was one of the earliest successful algorithms developed for cartographic generalization.
The purpose of the algorithm is, given a curve composed of line segments, to find a similar curve with fewer points. The algorithm defines 'dissimilar' based on the maximum distance between the original curve and the simplified curve. The simplified curve consists of a subset of the points that defined the original curve.
The shapely library used in the footprint_facility module implements this algorithm of simplification. The definition of the distance to be used for the simplification shall be carefully defined to minimize the surface change. Code simplification snippet

from footprint_facility import rework_to_polygon_geometry, to_geojson, simplify
 
geometry = rework_to_polygon_geometry(origin_geometry)
geojson = to_geojson(simplify(geometry, tolerance=0.4))
 
print(geojson)

img.png We observe the algorithm is quite efficient and reduced the larger the number of points between tolerance set between 0.005 and 0.5:

img_1.png The variation of the surface regarding reduction of the number of point is not significant.

More Complex footprints

When the footprint crosses antimeridian or pass other the poles, the polygon is not anymore properly supported by the algortihm. it is recommended to systematically manage rework of polygon before performing the simplification.

Example of Footprints crossing antimeridian and pole. The Douglas-Peucker algorithm properly manages this use case when antimeridian case has been previously handled. The pole area (polar inclusion solution) is also well supported:

img_2.png

The Use case of single footprint crossing antimeridian managed as multi-polygon are also well supported: img_3.png

Sentinel-3 Synergy Data sets identified in the footprint category can be simplified: img_4.png Here, the 295 points are well aligned and a distance 0 properly simplifies the polygon.

The Convex Hull algorithm

The convex hull is the smallest convex polygon that encloses all points of a geometry. The Sentinel-1 wave-mode images are organized in a list of polygons, and the previously analyzed "Douglas-Peucker" optimization cannot be considered efficient enough to simplify them. The following scheme shows the convex hull (in purple) simplifying the wave-mode images of the set of polygons (in yellow) into a single enclosed polygon.

img_5.png The curvature of the Earth is visible using an algorithm that considers coordinates in a Euclidean reference frame. The algorithm will be improved to support spherical coordinates, but the 2D convex hull used here reduces the number of points by 90%. The convex hull created an envelope that contains all the points of the wave mode without fitting the entire huge envelope (green shape): This algorithm remains a good solution for reducing the number of points to optimize indexing systems.

When using the convex hull algorithm with a footprint that crosses the antimeridian, it must be managed before applying the implementation. In the following scheme, the antimeridian is materialized in red and the footprint hull is spread on each side of the red line:

img_6.png

Code snippett to manage the footprint

import shapely
from footprint_facility import rework_to_polygon_geometry, to_geojson
 
geometry = rework_to_polygon_geometry(origin_geometry)
 
# split eastern/wester part of the polygon
west_geometry = geometry.intersection(shapely.box(-180, -90, 0, 90))
east_geometry = geometry.intersection(shapely.box(0, -90, 180, 90))
 
convex_hull = shapely.MultiPolygon([east_geometry.convex_hull, west_geometry.convex_hull])
print (to_geojson(convex_hull))

Footprint simplification conclusion

Reducing the number of points in the EO data footprint can have a positive effect by optimizing the display and faster indexing systems. However, it may introduce inaccuracies in these footprints. For example, using the convex hull algorithm on a Sentinel-1 wave mode can reduce the number of points to 90%, the counterpart is the footprint will cover a larger area than the data.
The use of the Douglas-Peucker algorithm is also very efficient and according to the tolerance parameter, the simplified result footprint surface may not shift more than 1%. For example, we demonstrate that S3 SYNERGY products can be completely simplified with 0 tolerance without any surface modification. Other products, in particular long footprints with many points along tracks, can also be simplified by controlling the deviations with the tolerance.

Performance management

The methods are completed with embedded performances metrics capabilities. To activate them, use the following check_time method:

import footprint_facility 
footprint_facility.check_time(enable=True,
                              incremental=False,
                              summary_time=True)

enable parameter to False fully deactivate the time management.
incremental parameter to True displays metrics during the execution.
summary_time parameter to True stores metrics for future display with footprint_facility.show_summary()

Sample Output:

check_cross_antimeridian:	    0.69 μs/point
rework_to_polygon_geometry:	    6.94 μs/point
simplify:	                    1.00 μs/point
rework_to_linestring_geometry:      2.99 μs/point

Known issues

Footprint support

Very long footprint crossing both the North and the South poles, crossing the antimeridian 3 times (Use-case Number 10) fails to be rendered.

Polar detection

Detection of the polar point inside the footprint is based on projection transformation within polar stereoscopic projection. Such a transformation cannot be performed on raw footprint that shall manage antimeridian discontinuity.

East/West swaths larger that 180

When eastern distance between consecutive coordinates is longer than 180 and coordinates longitude sign is reverted (e.g. longitudes sequentially -175 then +175), the algorithm uses the smallest area and consider the polygon crossing the antimeridian. To fix this issue, the algorithm shall be upgraded to take into account the clockwise orientation of the coordinates.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

footprint_facility-1.1.tar.gz (59.1 kB view hashes)

Uploaded Source

Built Distribution

footprint_facility-1.1-py3-none-any.whl (24.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page