13
13
# You should have received a copy of the GNU Affero General Public License
14
14
# along with this program. If not, see https://www.gnu.org/licenses/.
15
15
16
- # Standard libraries
17
- import collections
18
- import csv
19
- import io
20
- import json
21
- import re
22
- import sys
23
- import os
24
- import psycopg
25
- from dataclasses import dataclass
26
-
27
- # Load .env file
28
- from dotenv import load_dotenv
29
- load_dotenv ()
30
-
31
- # Third-party libraries
32
- import numpy as np
33
- import pandas as pd
34
- from scipy .stats import norm
35
-
36
16
# local libraries
37
17
import soil_id .config
38
18
41
21
getSG_descriptions ,
42
22
)
43
23
44
- from .color import (
24
+ from .color import (
45
25
calculate_deltaE2000 ,
46
26
)
47
27
48
28
from .services import get_soilgrids_classification_data , get_soilgrids_property_data
29
+
49
30
from .utils import (
50
31
agg_data_layer ,
51
32
assign_max_distance_scores ,
52
33
calculate_location_score ,
53
34
drop_cokey_horz ,
54
- extract_values ,
55
35
extract_WISE_data ,
56
36
getCF_fromClass ,
57
37
getClay ,
60
40
getTexture ,
61
41
gower_distances ,
62
42
pedon_color ,
63
- sg_get_and_agg ,
64
43
silt_calc ,
65
- max_comp_depth ,
44
+ max_comp_depth ,
45
+ adjust_depth_interval ,
66
46
)
67
47
48
+ # Standard libraries
49
+ import logging
50
+ import collections
51
+ import csv
52
+ import io
53
+ import re
54
+ from dataclasses import dataclass
55
+
56
+ # Third-party libraries
57
+ import numpy as np
58
+ import pandas as pd
59
+ from scipy .stats import norm
60
+
61
+ # Load .env file
62
+ from dotenv import load_dotenv
63
+ load_dotenv ()
64
+
65
+
68
66
@dataclass
69
67
class SoilListOutputData :
70
68
soil_list_json : dict
71
69
rank_data_csv : str
72
70
map_unit_component_data_csv : str
73
71
72
+
74
73
# entry points
75
74
# getSoilLocationBasedGlobal
76
75
# list_soils
@@ -94,9 +93,9 @@ def list_soils_global(lon, lat):
94
93
lon ,
95
94
lat ,
96
95
# Temporarily change file path
97
- file_path = '/mnt/c/LandPKS_API_SoilID-master/global/wise30sec_poly_simp_soil.gpkg' ,
98
- #file_path=config.WISE_PATH,
99
- #layer_name=None,
96
+ file_path = '/mnt/c/LandPKS_API_SoilID-master/global/wise30sec_poly_simp_soil.gpkg' ,
97
+ # file_path=config.WISE_PATH,
98
+ # layer_name=None,
100
99
buffer_dist = 10000 ,
101
100
)
102
101
@@ -106,7 +105,7 @@ def list_soils_global(lon, lat):
106
105
mucompdata_pd ["distance" ] = pd .to_numeric (mucompdata_pd ["distance" ])
107
106
mucompdata_pd ["share" ] = pd .to_numeric (mucompdata_pd ["share" ])
108
107
mucompdata_pd = mucompdata_pd .drop_duplicates ().reset_index (drop = True )
109
-
108
+
110
109
##############################################################################################
111
110
# Individual probability
112
111
# Based on Fan et al 2018 EQ 1, the conditional probability for each component is calculated
@@ -131,7 +130,7 @@ def list_soils_global(lon, lat):
131
130
132
131
mucompdata_pd = pd .merge (mucompdata_pd , cond_prob , on = "cokey" , how = "left" )
133
132
mucompdata_pd = mucompdata_pd .sort_values ("distance_score" , ascending = False )
134
-
133
+
135
134
mucompdata_pd = mucompdata_pd .reset_index (drop = True )
136
135
mucompdata_pd ["distance" ] = mucompdata_pd ["distance" ].round (4 )
137
136
mucompdata_pd ["Index" ] = mucompdata_pd .index
@@ -214,7 +213,7 @@ def list_soils_global(lon, lat):
214
213
# Subset mucompdata_pd by new compname_key and add suffix to name if there are duplicates
215
214
mucompdata_pd = mucompdata_pd .loc [mucompdata_pd ['cokey' ].isin (comp_key )].reset_index (drop = True )
216
215
mucompdata_pd ["compname_grp" ] = mucompdata_pd ["compname" ]
217
-
216
+
218
217
# Sort by 'distance_score' (descending) and 'distance' (ascending), then reset the index
219
218
mucompdata_pd = mucompdata_pd .sort_values (['distance_score' , 'distance' ], ascending = [False , True ]).reset_index (drop = True )
220
219
@@ -255,7 +254,7 @@ def list_soils_global(lon, lat):
255
254
.drop_duplicates (keep = "first" )
256
255
.reset_index (drop = True )
257
256
)
258
-
257
+
259
258
# profile depth
260
259
c_very_bottom = max_comp_depth (profile )
261
260
@@ -334,7 +333,7 @@ def list_soils_global(lon, lat):
334
333
# Concatenate lists to form DataFrames
335
334
c_bottom_depths = pd .concat (c_bottom_depths , axis = 0 )
336
335
clay_texture = pd .concat (clay_texture , axis = 0 )
337
-
336
+
338
337
# Subset mucompdata and muhorzdata DataFrames
339
338
mucompdata_pd = mucompdata_pd [mucompdata_pd ["cokey" ].isin (c_bottom_depths .cokey )]
340
339
muhorzdata_pd = muhorzdata_pd [muhorzdata_pd ["cokey" ].isin (c_bottom_depths .cokey )]
@@ -527,9 +526,9 @@ def convert_to_serializable(obj):
527
526
return obj .tolist ()
528
527
else :
529
528
return obj
530
-
529
+
531
530
output_SoilList_cleaned = convert_to_serializable (output_SoilList )
532
-
531
+
533
532
soil_list_json = {
534
533
"metadata" : {
535
534
"location" : "us" ,
@@ -544,8 +543,8 @@ def convert_to_serializable(obj):
544
543
"ec" : "ds/m" ,
545
544
},
546
545
},
547
- #"AWS_PIW90": aws_PIW90,
548
- #"Soil Data Value": var_imp,
546
+ # "AWS_PIW90": aws_PIW90,
547
+ # "Soil Data Value": var_imp,
549
548
"soilList" : output_SoilList_cleaned ,
550
549
}
551
550
@@ -554,7 +553,6 @@ def convert_to_serializable(obj):
554
553
rank_data_csv = soilIDRank_output_pd .to_csv (index = None , header = True ),
555
554
map_unit_component_data_csv = mucompdata_cond_prob .to_csv (index = None , header = True ),
556
555
)
557
-
558
556
559
557
560
558
##############################################################################################
@@ -686,12 +684,11 @@ def rank_soils_global(
686
684
p_sandpct_intpl = adjust_depth_interval (p_sandpct_intpl )
687
685
p_claypct_intpl = adjust_depth_interval (p_claypct_intpl )
688
686
p_cfg_intpl = adjust_depth_interval (p_cfg_intpl )
689
- p_lab_intpl = adjust_depth_interval (p_lab_intpl )
690
687
691
688
# Construct final dataframe with adjusted data
692
689
p_compname = pd .Series ("sample_pedon" , index = np .arange (len (p_sandpct_intpl )))
693
690
p_hz_data = pd .concat (
694
- [p_compname , p_sandpct_intpl , p_claypct_intpl , p_cfg_intpl , p_lab_intpl ], axis = 1
691
+ [p_compname , p_sandpct_intpl , p_claypct_intpl , p_cfg_intpl ], axis = 1
695
692
)
696
693
p_hz_data .columns = [
697
694
"compname" ,
@@ -720,22 +717,20 @@ def rank_soils_global(
720
717
p_cfg_intpl = []
721
718
722
719
# Initialize lab interpolation data with NaNs
723
- p_lab_intpl = pd .DataFrame (np .nan , index = np .arange (1 ), columns = np .arange (3 ))
724
720
cr_df = pd .Series ([np .nan ])
725
-
721
+
726
722
# Set default bottom depth data
727
723
if bedrock is not None :
728
724
p_bottom_depth = pd .DataFrame ([- 999 , "sample_pedon" , bedrock ]).T
729
725
else :
730
726
p_bottom_depth = pd .DataFrame ([- 999 , "sample_pedon" , 0 ]).T
731
727
p_bottom_depth .columns = ["cokey" , "compname" , "bottom_depth" ]
732
728
733
-
734
729
# --------------------------------------------------------------------------------------------------------------------------------------
735
730
# Load in component data from soilIDList
736
731
soilIDRank_output_pd = pd .read_csv (io .StringIO (list_output_data .rank_data_csv ))
737
732
mucompdata_pd = pd .read_csv (io .StringIO (list_output_data .map_unit_component_data_csv ))
738
-
733
+
739
734
# Create soil depth DataFrame and subset component depths based on max user depth
740
735
# if no bedrock specified
741
736
c_bottom_depths = mucompdata_pd [["compname" , "c_very_bottom" ]].rename (
@@ -1139,7 +1134,7 @@ def rank_soils_global(
1139
1134
Score_Data_Loc = (D_final_loc [["Score_Data" , "distance_score" ]].sum (axis = 1 )) / (
1140
1135
D_final_loc .weight + location_weight
1141
1136
)
1142
-
1137
+
1143
1138
D_final_loc ["Score_Data_Loc" ] = Score_Data_Loc
1144
1139
1145
1140
# Rule-based final score adjustment
@@ -1251,7 +1246,7 @@ def rank_soils_global(
1251
1246
].fillna (
1252
1247
0.0
1253
1248
)
1254
-
1249
+
1255
1250
# Construct the output format
1256
1251
Rank = [
1257
1252
{
@@ -1288,15 +1283,17 @@ def rank_soils_global(
1288
1283
##################################################################################################
1289
1284
# getSoilGridsGlobal #
1290
1285
##################################################################################################
1286
+
1287
+
1291
1288
def sg_list (lon , lat ):
1292
1289
"""
1293
1290
Query the SoilGrids API (via get_soilgrids_property_data) and post-process
1294
1291
the returned JSON into a structured dictionary that includes:
1295
-
1292
+
1296
1293
1. Soil horizons data (sand, clay, cfvo, pH, cec, silt, texture) at multiple depths.
1297
1294
2. Classification probabilities and descriptions (WRB taxonomy).
1298
1295
3. Summarized or aggregated soil variables, with depth tracking.
1299
-
1296
+
1300
1297
Args:
1301
1298
lon (float): Longitude in decimal degrees (WGS84).
1302
1299
lat (float): Latitude in decimal degrees (WGS84).
@@ -1354,7 +1351,7 @@ def sg_list(lon, lat):
1354
1351
df_top_depth = pd .DataFrame (top_depths , columns = ["top_depth" ])
1355
1352
df_bottom_depth = pd .DataFrame (bottom_depths , columns = ["bottom_depth" ])
1356
1353
df_values = pd .DataFrame (values , columns = ["value" ])
1357
-
1354
+
1358
1355
# The code assumes each property repeats over the same set of depths.
1359
1356
n_depths = len (df_top_depth )
1360
1357
if len (names ) % n_depths != 0 :
@@ -1374,9 +1371,9 @@ def sg_list(lon, lat):
1374
1371
# 6. Pivot the data into a wide form with each property as a column.
1375
1372
try :
1376
1373
sg_data_w = sg_data .pivot_table (
1377
- index = "bottom_depth" ,
1378
- columns = "prop" ,
1379
- values = "value" ,
1374
+ index = "bottom_depth" ,
1375
+ columns = "prop" ,
1376
+ values = "value" ,
1380
1377
aggfunc = "first" # Change the aggregator if needed
1381
1378
)
1382
1379
except Exception as e :
@@ -1461,7 +1458,7 @@ def sg_list(lon, lat):
1461
1458
sand_pd = sg_data_w ["sand" ]
1462
1459
clay_pd = sg_data_w ["clay" ]
1463
1460
rfv_pd = sg_data_w ["cfvo" ]
1464
- pH_pd = sg_data_w ["phh2o" ]
1461
+ pH_pd = sg_data_w ["phh2o" ]
1465
1462
cec_pd = sg_data_w ["cec" ]
1466
1463
# 13. Additional texture calculations from aggregated values
1467
1464
texture_pd = pd .DataFrame ({
@@ -1536,4 +1533,4 @@ def sg_list(lon, lat):
1536
1533
return {
1537
1534
"metadata" : metadata ,
1538
1535
"soilGrids" : SoilGrids
1539
- }
1536
+ }
0 commit comments