From 0d740641df517f3a844736dbb4cc9e800fdf8491 Mon Sep 17 00:00:00 2001 From: Matthias Merdes Date: Mon, 26 Aug 2024 13:16:09 +0200 Subject: [PATCH] cleanup: remove obsolete raster functionality #815 follow-up to https://github.com/GIScience/ohsome-quality-api/pull/813 https://github.com/GIScience/ohsome-quality-api/issues/815 Co-authored-by: Matthias Schaub --- ohsome_quality_api/raster/__init__.py | 0 ohsome_quality_api/raster/client.py | 57 --------- ohsome_quality_api/raster/definitions.py | 69 ----------- ...ILT_LDS2014_GLOBE_R2018A_54009_1K_V2_0.tif | Bin 1351 -> 0 bytes ...S_POP_E2015_GLOBE_R2019A_54009_1K_V1_0.tif | Bin 1951 -> 0 bytes ...MOD_POP2015_GLOBE_R2019A_54009_1K_V2_0.tif | Bin 1051 -> 0 bytes ..._vcmslcfg_c202102150000.average_masked.tif | Bin 5262 -> 0 bytes tests/integrationtests/test_raster.py | 29 ----- tests/unittests/test_raster.py | 112 ------------------ tests/unittests/test_raster_definitions.py | 19 --- 10 files changed, 286 deletions(-) delete mode 100644 ohsome_quality_api/raster/__init__.py delete mode 100644 ohsome_quality_api/raster/client.py delete mode 100644 ohsome_quality_api/raster/definitions.py delete mode 100644 tests/fixtures/raster/GHS_BUILT_LDS2014_GLOBE_R2018A_54009_1K_V2_0.tif delete mode 100644 tests/fixtures/raster/GHS_POP_E2015_GLOBE_R2019A_54009_1K_V1_0.tif delete mode 100644 tests/fixtures/raster/GHS_SMOD_POP2015_GLOBE_R2019A_54009_1K_V2_0.tif delete mode 100644 tests/fixtures/raster/VNL_v2_npp_2020_global_vcmslcfg_c202102150000.average_masked.tif delete mode 100644 tests/integrationtests/test_raster.py delete mode 100644 tests/unittests/test_raster.py delete mode 100644 tests/unittests/test_raster_definitions.py diff --git a/ohsome_quality_api/raster/__init__.py b/ohsome_quality_api/raster/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ohsome_quality_api/raster/client.py b/ohsome_quality_api/raster/client.py deleted file mode 100644 index 7ddf37dce..000000000 --- a/ohsome_quality_api/raster/client.py +++ /dev/null @@ -1,57 +0,0 @@ -"""A client to raster datasets existing as files on disk.""" - -import os -from copy import deepcopy - -import geojson -from pyproj import Transformer -from rasterstats import zonal_stats - -from ohsome_quality_api.config import get_config_value -from ohsome_quality_api.raster.definitions import RasterDataset -from ohsome_quality_api.utils.exceptions import RasterDatasetNotFoundError - - -def get_zonal_stats( - feature: geojson.Feature, - raster: RasterDataset, - *args, - **kwargs, -): - """Wrapper around the function `zonal_stats` of the package `rasterstats`. - - All arguments are passed directly to `zonal_stats`. - - The only difference is that the arguments `vectors` and `raster` of `zonal_stats` - are expected to be a `geojson.Feature` object and a member of the `RasterDataset` - class respectively. - """ - return zonal_stats( - transform(feature, raster), - get_raster_path(raster), - *args, - nodata=raster.nodata, - **kwargs, - ) - - -def transform(feature: geojson.Feature, raster: RasterDataset): - """Convert Feature to RasterDataset CRS. - - GeoJSON Feature CRS/SRID is expected to be EPSG:4326. - """ - if raster.crs == "EPSG:4326": - return feature - transformer = Transformer.from_crs("EPSG:4326", raster.crs, always_xy=True) - return geojson.utils.map_tuples( - lambda coordinates: transformer.transform(coordinates[0], coordinates[1]), - deepcopy(feature), - ) - - -def get_raster_path(raster: RasterDataset) -> str: - """Get the path of the raster file on disk.""" - path = os.path.join(get_config_value("data_dir"), raster.filename) - if not os.path.exists(path): - raise RasterDatasetNotFoundError(raster) - return path diff --git a/ohsome_quality_api/raster/definitions.py b/ohsome_quality_api/raster/definitions.py deleted file mode 100644 index fcb934abd..000000000 --- a/ohsome_quality_api/raster/definitions.py +++ /dev/null @@ -1,69 +0,0 @@ -from dataclasses import dataclass - -from ohsome_quality_api.utils.exceptions import RasterDatasetUndefinedError - - -@dataclass(frozen=True) -class RasterDataset: - """Raster datasets available on disk. - - Args: - name: Name of raster - filename: Filename of raster on disk - crs: An authority string (i.e. `EPSG:4326` or `ESRI:54009`) - """ - - name: str - filename: str - crs: str - nodata: int | None - - -RASTER_DATASETS = ( - RasterDataset( - "GHS_BUILT_R2018A", - "GHS_BUILT_LDS2014_GLOBE_R2018A_54009_1K_V2_0.tif", - "ESRI:54009", - -200, - ), - RasterDataset( - "GHS_POP_R2019A", - "GHS_POP_E2015_GLOBE_R2019A_54009_1K_V1_0.tif", - "ESRI:54009", - -200, - ), - RasterDataset( - "GHS_SMOD_R2019A", - "GHS_SMOD_POP2015_GLOBE_R2019A_54009_1K_V2_0.tif", - "ESRI:54009", - -200, - ), - RasterDataset( - "VNL", - "VNL_v2_npp_2020_global_vcmslcfg_c202102150000.average_masked.tif", - "EPSG:4326", - -999, - ), -) - - -def get_raster_dataset_names() -> list[str]: - return [r.name for r in RASTER_DATASETS] - - -def get_raster_dataset(name: str) -> RasterDataset: - """Get a instance of the `RasterDataset` class by the raster name. - - Args: - name: Name of the raster as defined by `RASTER_DATASETS`. - - Returns - An instance of the `RasterDataset` class with matching name. - - Raises: - RasterDatasetUndefinedError: If no matching `RasterDataset` class is found. - """ - try: - return next(filter(lambda r: r.name == name, RASTER_DATASETS)) - except StopIteration as e: - raise RasterDatasetUndefinedError(name) from e diff --git a/tests/fixtures/raster/GHS_BUILT_LDS2014_GLOBE_R2018A_54009_1K_V2_0.tif b/tests/fixtures/raster/GHS_BUILT_LDS2014_GLOBE_R2018A_54009_1K_V2_0.tif deleted file mode 100644 index b758e750d9f0a931cbfc9719b28dae2abbe8c25e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1351 zcmebD)MDUZU|<^r;rplk&on;FUmsbfQ83o^2R^}c6fU=T$T z7lX1v=7>YpL;%&uAgKw4vYCNu_?mebK=dsjZffCSU<1;hfOvg7GlLG0RsveQp`C{T zXaqwQkiD^;i6H?=I7*;r)H+4)`YtUD_EG+xCRG# zDg?MH1eX+L=A|pxDg*@idpQS3E1{`Z(s6h7cLzxTiFlyqctcAI6D1uN$BQ5Uu`RuFfHz{(jL)aE~bg zjdToh^mPq!4T@HBOUx-wjdx8fE&=)uYBGkjUw%?LB%`{+lSA#1F283UT=R;W1-{wh+d}`H#;5IzRYn-YFy{!l4%Cl z-?f3!KDgw7W3`AD&~65X@HYp+^oAlGhs!pn91CqroU~plJ1Kijayq|kozuCQ*B!sH z9d(#*bIBnpWw*ojM>8ECmq|MPb6#W*aXSOUW3vvR86Jnf#O`p4<(=i^x2V_Y?vY7O z-aA{JqJxc{I=)PGSYtQcF3#J;LB4gj!`{dH!2W~ihlH6`^Dd`yyRVMSoxM&Oj}AL= zRSG7rsq-8gG`2W-pW5m4BC^ow{C6jCc&LX%wrhHpxZ?D$t O#J5(f-cZ&tuc=CJ5gKXluO`Oss`9TX z>sY6?oKt9IY+Y4Ou&g&g&h5`z81g$<&%Iz_O~9+LX7dfq3Y4I6A#Y*OhV28rm^U#6 zco5dJGbVNm_!lu)5&kkKwZe-32Gs7n&x4`DgO3nV7icn=0M0G2Klj5J z{6t}M)b4h5O4eRaXwWw#^;Wrp16$;P-`fi{meMvkyIpo?cZbdDaJxDjUEL0cvul$x z60r^hygnH!^rcjFESnroNLE{iEn*$)_lBUZx)c25vUhiOIUR0Cm)-5|bQ)XzTjidR z*WW)REe$3?Nl(DzlS6V~NP0LnnpUH7ES-Vr%9WRe`^Qt6MBR9gI+jVrMx#D8m5e82 zV>M^gYzkjJNH%~pu|3iJgRykaqU)p!^CA5`Q z+fN`%d-ohf{+npu?8))kBs!1s-ol2mpKR^x{ex(W=f8LzsZ558NbS=ruMpR_oI)x> z@*||UEp`C8OwL_+h@ZNIwE4=frjX2gjr!=D*Ug}< zpS?=w(5H4&Uw$V27W(aJBie;sK)+Xha6-t+%tmYX9Z)vW4ma6 za8>%&rP%%>_0`X>Igb04bcFimo-u!ncIiv5A@#T00?3?eHiEp7DDPfa&y8T+eh{kh zm#$^$o$1rf@8G^%Vua=tE7DNIBowb+h_^0l4vX6951J diff --git a/tests/fixtures/raster/GHS_SMOD_POP2015_GLOBE_R2019A_54009_1K_V2_0.tif b/tests/fixtures/raster/GHS_SMOD_POP2015_GLOBE_R2019A_54009_1K_V2_0.tif deleted file mode 100644 index a50f94776035257061665d987e966f85202d0cb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1051 zcmZ`#O^?!G5S{`eN~3Hd?nO7G>qS!$T3E`#1Ok=qCI!;6t_dMXq}e5@kaU}DVn~c1 zNsR}+dG@gJD*PRPfCn!o{sEln+d^HnZ|0q+^E}UVrkZvSQUI_8z{P<>bRB~W@KwaI z0A~=#15CPP1aEN(-}^HLkd4%D2AKM62O1I0w;~!dz-WbQZ+M7f{)X}TEe}b|zc9Xf zaS$;J=v{a`koE=Qr5A&znBQZ(I*-96tp6Y~d}<$SJ`8hM|4;sW<4&EZ#e>IwCd~Pp z;RXr9tCzTjUyCJ{9{m&geH?txaH*wt88VRc-#_eLNi96`ru~c>e(CLV+mPT%e|PuY zbNAEjbEk$KYF~O!>PIuj9uM`IZJ$|_u{A$xnK`92S4~69b=92dI+N*f?qRNLbPgJ3 zU$~$b#FpA=5rNkb&BJ0Q%l3!+q2@>01X zNpg8l9t^~;q3J4iXgQWOJ)1l`5yV2iFc1&hS`YhL$5{7E#Y(9xOI4{{tX4}hvvv;D zMo;Us`@(uL0bACMx~}$Aqc1!f*{9Y}9i6(kUC{Y2x;=B;lYjCJYw9{9d#GE^WIP#7 z|Cmj;T+0!}B2GJ)!+ohx0A+RYq#=#fY=j}a13QpKh8;r6ih{=~aZx@CJn}3;j`3Mk z)8TzL-BF%7h!1Dlu)N=oeLZA1ZkmVObTfA2jLywCemyZ$(H_ymzRx(eDPw;FxNOc! diff --git a/tests/fixtures/raster/VNL_v2_npp_2020_global_vcmslcfg_c202102150000.average_masked.tif b/tests/fixtures/raster/VNL_v2_npp_2020_global_vcmslcfg_c202102150000.average_masked.tif deleted file mode 100644 index 1bdd7a2fab76196e29472d70634503fdd6f62f65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5262 zcmd54H+~99^au43Ux2L==&4*JsYte9X)_^UZmld7tOq>$lfh?^?g#df)xWo;p=eAQT7$ ziUNU{gg{J$j6_@P1J)2>@eg>Q2upmxoq5vTu#%W$*V)ygfND3t+6UZONBu+2KxKiT zU$-3F4_HE!(`&n&pwpI$w(kx(fwaihiS|i93Bf>-RTI@u^OF;Zi?E#tpYjv$V!vF3 zJJ-gI3zU_V){@{C`@inr{ET)|OW81V5K8u)bY7l41-~SUb*bqg5J-wJ-IPiYbOK#* zkNrbl!tHMKqA-G{K1-#Iufiy3mfIhm`HiuB*M(5x1?hct zg|r$kQhn4ZiY|_)(EVYbuFrOD)w%GPk2$L^oZ{};)B1I!4P zgQvbctcQG#!B4k9Cng`_HZRdf{72Z_*@!{oM?vF-9K`)Q=tinEjC9p-XO0zQlp^7h zei>VK?Z>_WUqETI0)A+CMoGDaRIwwKZ0jQ_P9fqWetGwODIq+b%wx)_^;R3nzpo@0 z<0yJYn`qQ^$B**=4GCQ-j}jzN=+FZWiAtDpPaVDGB5?KGYe?zz<++vp*{kq-qz~MH z!tbo1)})W6n*E^KZv>3*EQPjGHrnR5VV7qkCORHM@IqI7-Kq=S%-3YHp@60;r%~eZ z7}^yUPDT+2=*H$?I$Z5fdyeg;!0=0S++G|jLevmsDu%7uc~sC5OzFxyX|2~?uz_5Q0jH?qzQKFXe!>t8lIQI_hspvua&WK08c}HHY-(^3N&}wm-opnQV|DP3&+16h|p~c zsJSAB0+(}?I6atjYB!VB?G>b=@MRazss~cAx+;#&3$S;R!=iQt4AGKD@Jn$l)Ob&Q zAHJZ8)?%P`{03UPi);TfjKhbFzG9X>()|G4tz-?wzkn} zk+1jjo-o>^hjm+g(Gpbz1APTPIoW|@$AqwMRsy@uKE)~th5Yt?0UtSYf!}##v$9GV z=M-1+vetAS`_hw3N`kmNJDbxl#dE-oAU-3tj$>ZBa#q_WDJi9y_abbs1TXzC2g=erX?hNdcvmooS4ew2QU~68+uYNn7 z@2vaUKJzsVJtK>Pc3pJ$e;s4RTN)xcRPqV?%x&Q@o8GWbtc027eSz88gd(;LKFQVD z7rCOoiEkaL;8l~-c+!vgJU;jl?|yxg>lY;RDWA3MonXl&<;I*CD$n_&GBL(u8PfYJ zVP`KHWMwGg$pB?YuNT7L(L+j@TulYNnrUpRBGRL#L*koM%$`t$$lK8<^|pdnb>Z)7 z>CV49c6XV}7ZX$XMtc=2ML*?(OAEMI<~*C}WOB};bbe4;&6+>UnK}2-H@nuVVRrt= zBfejr#o8??e0H)gkJ9{%zj|K;-MAFIRfxp;r@lycbAWe>7GBv4p?={xDP60i7a13+ zE-Zx<9HZ%=K`a@C9j4sXDHJ;>k#cv-W64Ydm};xw!p>$IX@2fcY3bCp^5rV7*t3;$ zwYIZ&%T(6#8N@!#t!UC1%SOXvIoL$fO#i8t*~JA4W=q-%S^RuB2d=l{w{AVzCh;iD z``aS=dqR`39_%k`;>lAP%(ak$c3vy#=~dD8-B;6noB# z;%h4wa{cH&tngU_yp47sbIS}&d}xi1B_;@1riKlQQrI8aLSK)%M@epl6zHErrzfS; zyw}Ibu`c8z&doEU=uK_eNB;jr!~f0w$Ep%clnh0qZwMyrO~(`UD(pVh3d7nVyttCt zeUv3<#f;;J65}}~S%U*(DsgGf1~|>MM?SlN&f8$Pg$mjSHjw`NyRf|W+wU>LMZh6xQfyoy$IlgXcw6{5 zw)$x*cZ5#o4zFQcT2PHzmp#}&Xg7>=vask-0%`-7A>L6Q8L@Zhkg%KRI;wsDHjLJkem=bmb>aMB|gg+T#W zXs`!QA1p=cYE>*f@Dq(0bDeC*Wzu}Z6O?KdPfwI1$Wc3(8Ww*?W19ESraPge(wP3I zwDkM3AGGHyV}gzbEUi5uSXzsdO`o$2hVa?XixB0u5{}PD;_d)L$muy_{isxA4R6Nz z#y40LE6pZ}`fRpm8s9rHjt9LS$|GIG*?Hexlx@nxfOly)c_|P9PfT#kxRvb6YUuHZ z%XHP{G?nEf)2VZD6m=qnvP#eXDej$bsQZ$gtANH#A?zC!QL$}2zO(hi1@EKiDI0?P zg(lc)Gyt)(3TUxVh1qf)WXUl?hx$U@%NG{IVj*LC4gV5r(#U0J`y_~ zk4MfbJ;*(fL-#XHKS?8djwF`q%HYO+Mu5|Glvhq-JCC(II?R?;9}i=1j}p94J_P@b zrT8M}8MHL6prphGHf55C$||P1-0c5~mhLMv=A*v# z{RMCh5{Ff*Ay&I3z`GDEs~61e(FfV4VHa<)(B~s`P4pipAE(2fqf+MrTEop?Xf265 z>J{`zHUFcU-Ff_#*rc$6(iYw)SGC$t_MB$gLH!SlW6Hr^5U9K2Q@wlNypP&|gE;W= bFlOuS!vu}7aB3GIQSa3!YksQ6Z~6WQWy1a9 diff --git a/tests/integrationtests/test_raster.py b/tests/integrationtests/test_raster.py deleted file mode 100644 index 320cb125c..000000000 --- a/tests/integrationtests/test_raster.py +++ /dev/null @@ -1,29 +0,0 @@ -import ohsome_quality_api.raster.client as raster_client -from ohsome_quality_api.raster.definitions import get_raster_dataset - -from .utils import get_geojson_fixture - - -def test_get_raster_path(mock_env_oqapi_data_dir): - raster = get_raster_dataset("GHS_BUILT_R2018A") - path = raster_client.get_raster_path(raster) - assert isinstance(path, str) - - -def test_get_zonal_stats(mock_env_oqapi_data_dir): - raster = get_raster_dataset("GHS_BUILT_R2018A") - feature = get_geojson_fixture("algeria-touggourt-feature.geojson") - - expected = [ - { - "min": 0.0, - "max": 46.7505989074707, - "mean": 1.6491034092047276, - "count": 234, - } - ] - result = raster_client.get_zonal_stats( - feature, - raster, - ) - assert expected == result diff --git a/tests/unittests/test_raster.py b/tests/unittests/test_raster.py deleted file mode 100644 index d3a80dc3d..000000000 --- a/tests/unittests/test_raster.py +++ /dev/null @@ -1,112 +0,0 @@ -import os -import tempfile -import unittest -from unittest import mock - -import geojson - -import ohsome_quality_api.raster.client as raster_client -from ohsome_quality_api.raster.definitions import get_raster_dataset -from ohsome_quality_api.utils.exceptions import RasterDatasetNotFoundError - - -class TestRaster(unittest.TestCase): - def setUp(self): - self.raster_dataset = get_raster_dataset("GHS_BUILT_R2018A") - path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "fixtures/heidelberg-altstadt-feature.geojson", - ) - with open(path, "r") as f: - self.feature = geojson.load(f) - - def test_get_raster_path(self): - with tempfile.TemporaryDirectory() as tmpdirname: - path = os.path.join( - tmpdirname, - "GHS_BUILT_LDS2014_GLOBE_R2018A_54009_1K_V2_0.tif", - ) - with open(path, "w") as f: - f.write("") - with mock.patch( - "ohsome_quality_api.raster.client.get_config_value", - return_value=tmpdirname, - ): - self.assertEqual( - raster_client.get_raster_path(self.raster_dataset), - path, - ) - - @mock.patch("ohsome_quality_api.raster.client.os") - def test_get_raster_path_error(self, mock_os): - mock_os.path.exists.return_value = False - self.assertRaises( - RasterDatasetNotFoundError, - raster_client.get_raster_path, - self.raster_dataset, - ) - - @mock.patch( - "ohsome_quality_api.raster.client.get_raster_path", - return_value=os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "fixtures", - "GHS_BUILT_R2018A-Heidelberg.tif", - ), - ) - def test_get_zonal_stats(self, *args): - expected = [ - { - "count": 2, - "max": 73.3844985961914, - "mean": 70.12319946289062, - "min": 66.86190032958984, - } - ] - result = raster_client.get_zonal_stats( - self.feature, - self.raster_dataset, - ) - self.assertEqual(expected, result) - - @mock.patch( - "ohsome_quality_api.raster.client.get_raster_path", - return_value=os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "fixtures", - "nodata.tif", - ), - ) - def test_get_zonal_stats_nodata(self, *args): - """Test on a raster file with only pixel values of -222 (nodata)""" - expected = [{"count": 0, "sum": None}] - result = raster_client.get_zonal_stats( - self.feature, self.raster_dataset, stats=["count", "sum"] - ) - self.assertEqual(expected, result) - - def test_transform_different_crs(self): - expected = { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - (664903.2658458926, 5810944.6089190915), - (666571.1021172021, 5810944.6089190915), - (666474.5092985737, 5812131.278240253), - (664806.9147134189, 5812131.278240253), - (664903.2658458926, 5810944.6089190915), - ] - ], - }, - "properties": {}, - } - result = raster_client.transform(self.feature, self.raster_dataset) - self.assertDictEqual(expected, result) - self.assertNotEqual(self.feature, result) - - def test_transform_same_crs(self): - raster_dataset = get_raster_dataset("VNL") - result = raster_client.transform(self.feature, raster_dataset) - self.assertDictEqual(self.feature, result) diff --git a/tests/unittests/test_raster_definitions.py b/tests/unittests/test_raster_definitions.py deleted file mode 100644 index 673a7635b..000000000 --- a/tests/unittests/test_raster_definitions.py +++ /dev/null @@ -1,19 +0,0 @@ -import unittest - -from ohsome_quality_api.raster import definitions -from ohsome_quality_api.utils.exceptions import RasterDatasetUndefinedError - - -class TestDefinitions(unittest.TestCase): - def test_get_raster_dataset_names(self): - names = definitions.get_raster_dataset_names() - self.assertIsInstance(names, list) - self.assertTrue(names) - - def test_get_raster_dataset(self): - raster = definitions.get_raster_dataset("GHS_BUILT_R2018A") - self.assertIsInstance(raster, definitions.RasterDataset) - - def test_get_raster_dataset_undefined(self): - with self.assertRaises(RasterDatasetUndefinedError): - definitions.get_raster_dataset("foo")