Skip to content

Commit 740fc13

Browse files
authored
Merge pull request #231 from Vizzuality/data/agri_ghana_uganda
data: add Ghana and Rwanda-Uganda agriculture layer configs
2 parents ed56441 + ba31318 commit 740fc13

4 files changed

Lines changed: 358 additions & 0 deletions

File tree

data-processing/src/animated_config.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,18 @@ layers:
476476
2: "#006724"
477477
3: "#f2d33a"
478478
4: "#e96a1f"
479+
480+
# Rwanda-Uganda - Agriculture
481+
uganda_dry_matter_productivity:
482+
input_folder: "../data/raw/Agriculture/Rwanda-Uganda/QGIS_layers/3_Dry-Matter-Productivity/Uganda-kassnada-Mityana-districts/"
483+
output_folder: "../data/processed/Agriculture/Rwanda-Uganda/APNGs/DryMatterProductivity/"
484+
min_z: 8
485+
max_z: 12
486+
vmin: 0
487+
vmax: 40
488+
date_format: "YYYYMMDD"
489+
colormap:
490+
type: binned
491+
intervals: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40]
492+
colors: ["#96714e", "#f4b982", "#d6e2b3", "#b5d178", "#4a8131", "#264f24", "#0f330d", "#041903", "#a258bf", "#622573"]
493+
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
"""Convert OGC SLD raster ColorMap files to QGIS QML format.
2+
3+
Usage:
4+
python sld_to_qml.py input.sld # writes input.qml next to input.sld
5+
python sld_to_qml.py input.sld output.qml # explicit output path
6+
python sld_to_qml.py *.sld # batch convert all SLDs in a folder
7+
"""
8+
9+
import argparse
10+
import xml.etree.ElementTree as ET
11+
from pathlib import Path
12+
13+
14+
def parse_sld(sld_path):
15+
"""Extract color map entries from an SLD file."""
16+
tree = ET.parse(sld_path)
17+
root = tree.getroot()
18+
entries = []
19+
for entry in root.iter("{http://www.opengis.net/sld}ColorMapEntry"):
20+
color = entry.get("color")
21+
quantity = entry.get("quantity")
22+
label = entry.get("label", "")
23+
entries.append((float(quantity), color, label))
24+
return entries
25+
26+
27+
def hex_to_rgba_str(hex_color):
28+
"""Convert #RRGGBB to QGIS 'R,G,B,255,rgb:r,g,b,1' format."""
29+
h = hex_color.lstrip("#")
30+
r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
31+
rf, gf, bf = r / 255.0, g / 255.0, b / 255.0
32+
return f"{r},{g},{b},255,rgb:{rf},{gf},{bf},1"
33+
34+
35+
def build_gradient_stops(entries):
36+
"""Build QGIS gradient stops string from color entries."""
37+
if len(entries) < 3:
38+
return ""
39+
min_val = entries[0][0]
40+
max_val = entries[-1][0]
41+
val_range = max_val - min_val
42+
if val_range == 0:
43+
return ""
44+
stops = []
45+
for val, color, _label in entries[1:-1]:
46+
pos = (val - min_val) / val_range
47+
rgba = hex_to_rgba_str(color)
48+
stops.append(f"{pos:.6f};{rgba};rgb;ccw")
49+
return ":".join(stops)
50+
51+
52+
def generate_qml(entries, output_path):
53+
"""Generate a QGIS QML file from SLD color map entries."""
54+
min_val = entries[0][0]
55+
max_val = entries[-1][0]
56+
color1_rgba = hex_to_rgba_str(entries[0][1])
57+
color2_rgba = hex_to_rgba_str(entries[-1][1])
58+
stops_str = build_gradient_stops(entries)
59+
60+
item_lines = []
61+
for val, color, label in entries:
62+
item_lines.append(
63+
f' <item color="{color}" value="{val}" alpha="255" label="{label}"/>'
64+
)
65+
items_xml = "\n".join(item_lines)
66+
67+
stops_option = ""
68+
if stops_str:
69+
stops_option = f'\n <Option name="stops" value="{stops_str}" type="QString"/>'
70+
71+
qml = f'''<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
72+
<qgis version="3.40.5-Bratislava" hasScaleBasedVisibilityFlag="0" styleCategories="AllStyleCategories" autoRefreshMode="Disabled" autoRefreshTime="0" maxScale="0" minScale="1e+08">
73+
<flags>
74+
<Identifiable>1</Identifiable>
75+
<Removable>1</Removable>
76+
<Searchable>1</Searchable>
77+
<Private>0</Private>
78+
</flags>
79+
<temporal mode="0" enabled="0" fetchMode="0" bandNumber="1">
80+
<fixedRange>
81+
<start></start>
82+
<end></end>
83+
</fixedRange>
84+
</temporal>
85+
<elevation mode="RepresentsElevationSurface" enabled="0" zscale="1" band="1" symbology="Line" zoffset="0">
86+
<data-defined-properties>
87+
<Option type="Map">
88+
<Option type="QString" value="" name="name"/>
89+
<Option name="properties"/>
90+
<Option type="QString" value="collection" name="type"/>
91+
</Option>
92+
</data-defined-properties>
93+
<profileLineSymbol>
94+
<symbol alpha="1" force_rhr="0" type="line" frame_rate="10" clip_to_extent="1" is_animated="0" name="">
95+
<data_defined_properties>
96+
<Option type="Map">
97+
<Option type="QString" value="" name="name"/>
98+
<Option name="properties"/>
99+
<Option type="QString" value="collection" name="type"/>
100+
</Option>
101+
</data_defined_properties>
102+
<layer class="SimpleLine" pass="0" id="{{{{auto}}}}" enabled="1" locked="0">
103+
<Option type="Map">
104+
<Option type="QString" value="0" name="align_dash_pattern"/>
105+
<Option type="QString" value="square" name="capstyle"/>
106+
<Option type="QString" value="5;2" name="customdash"/>
107+
<Option type="QString" value="3x:0,0,0,0,0,0" name="customdash_map_unit_scale"/>
108+
<Option type="QString" value="MM" name="customdash_unit"/>
109+
<Option type="QString" value="0" name="dash_pattern_offset"/>
110+
<Option type="QString" value="3x:0,0,0,0,0,0" name="dash_pattern_offset_map_unit_scale"/>
111+
<Option type="QString" value="MM" name="dash_pattern_offset_unit"/>
112+
<Option type="QString" value="0" name="draw_inside_polygon"/>
113+
<Option type="QString" value="bevel" name="joinstyle"/>
114+
<Option type="QString" value="141,90,153,255,rgb:0.55294117647058827,0.35294117647058826,0.59999999999999998,1" name="line_color"/>
115+
<Option type="QString" value="solid" name="line_style"/>
116+
<Option type="QString" value="0.6" name="line_width"/>
117+
<Option type="QString" value="MM" name="line_width_unit"/>
118+
<Option type="QString" value="0" name="offset"/>
119+
<Option type="QString" value="3x:0,0,0,0,0,0" name="offset_map_unit_scale"/>
120+
<Option type="QString" value="MM" name="offset_unit"/>
121+
<Option type="QString" value="0" name="ring_filter"/>
122+
<Option type="QString" value="0" name="trim_distance_end"/>
123+
<Option type="QString" value="3x:0,0,0,0,0,0" name="trim_distance_end_map_unit_scale"/>
124+
<Option type="QString" value="MM" name="trim_distance_end_unit"/>
125+
<Option type="QString" value="0" name="trim_distance_start"/>
126+
<Option type="QString" value="3x:0,0,0,0,0,0" name="trim_distance_start_map_unit_scale"/>
127+
<Option type="QString" value="MM" name="trim_distance_start_unit"/>
128+
<Option type="QString" value="0" name="tweak_dash_pattern_on_corners"/>
129+
<Option type="QString" value="0" name="use_custom_dash"/>
130+
<Option type="QString" value="3x:0,0,0,0,0,0" name="width_map_unit_scale"/>
131+
</Option>
132+
<data_defined_properties>
133+
<Option type="Map">
134+
<Option type="QString" value="" name="name"/>
135+
<Option name="properties"/>
136+
<Option type="QString" value="collection" name="type"/>
137+
</Option>
138+
</data_defined_properties>
139+
</layer>
140+
</symbol>
141+
</profileLineSymbol>
142+
<profileFillSymbol>
143+
<symbol alpha="1" force_rhr="0" type="fill" frame_rate="10" clip_to_extent="1" is_animated="0" name="">
144+
<data_defined_properties>
145+
<Option type="Map">
146+
<Option type="QString" value="" name="name"/>
147+
<Option name="properties"/>
148+
<Option type="QString" value="collection" name="type"/>
149+
</Option>
150+
</data_defined_properties>
151+
<layer class="SimpleFill" pass="0" id="{{{{auto}}}}" enabled="1" locked="0">
152+
<Option type="Map">
153+
<Option type="QString" value="3x:0,0,0,0,0,0" name="border_width_map_unit_scale"/>
154+
<Option type="QString" value="141,90,153,255,rgb:0.55294117647058827,0.35294117647058826,0.59999999999999998,1" name="color"/>
155+
<Option type="QString" value="bevel" name="joinstyle"/>
156+
<Option type="QString" value="0,0" name="offset"/>
157+
<Option type="QString" value="3x:0,0,0,0,0,0" name="offset_map_unit_scale"/>
158+
<Option type="QString" value="MM" name="offset_unit"/>
159+
<Option type="QString" value="35,35,35,255,rgb:0.13725490196078433,0.13725490196078433,0.13725490196078433,1" name="outline_color"/>
160+
<Option type="QString" value="no" name="outline_style"/>
161+
<Option type="QString" value="0.26" name="outline_width"/>
162+
<Option type="QString" value="MM" name="outline_width_unit"/>
163+
<Option type="QString" value="solid" name="style"/>
164+
</Option>
165+
<data_defined_properties>
166+
<Option type="Map">
167+
<Option type="QString" value="" name="name"/>
168+
<Option name="properties"/>
169+
<Option type="QString" value="collection" name="type"/>
170+
</Option>
171+
</data_defined_properties>
172+
</layer>
173+
</symbol>
174+
</profileFillSymbol>
175+
</elevation>
176+
<customproperties>
177+
<Option type="Map">
178+
<Option type="bool" value="false" name="WMSBackgroundLayer"/>
179+
<Option type="bool" value="false" name="WMSPublishDataSourceUrl"/>
180+
<Option type="int" value="0" name="embeddedWidgets/count"/>
181+
<Option type="QString" value="Value" name="identify/format"/>
182+
</Option>
183+
</customproperties>
184+
<mapTip enabled="1"></mapTip>
185+
<pipe-data-defined-properties>
186+
<Option type="Map">
187+
<Option type="QString" value="" name="name"/>
188+
<Option name="properties"/>
189+
<Option type="QString" value="collection" name="type"/>
190+
</Option>
191+
</pipe-data-defined-properties>
192+
<pipe>
193+
<provider>
194+
<resampling zoomedOutResamplingMethod="nearestNeighbour" maxOversampling="2" zoomedInResamplingMethod="nearestNeighbour" enabled="false"/>
195+
</provider>
196+
<rasterrenderer classificationMin="{min_val}" classificationMax="{max_val}" nodataColor="" type="singlebandpseudocolor" band="1" alphaBand="-1" opacity="1">
197+
<rasterTransparency/>
198+
<minMaxOrigin>
199+
<limits>None</limits>
200+
<extent>WholeRaster</extent>
201+
<statAccuracy>Estimated</statAccuracy>
202+
<cumulativeCutLower>0.02</cumulativeCutLower>
203+
<cumulativeCutUpper>0.98</cumulativeCutUpper>
204+
<stdDevFactor>2</stdDevFactor>
205+
</minMaxOrigin>
206+
<rastershader>
207+
<colorrampshader clip="0" colorRampType="INTERPOLATED" minimumValue="{min_val}" maximumValue="{max_val}" classificationMode="1" labelPrecision="4">
208+
<colorramp type="gradient" name="[source]">
209+
<Option type="Map">
210+
<Option type="QString" value="{color1_rgba}" name="color1"/>
211+
<Option type="QString" value="{color2_rgba}" name="color2"/>
212+
<Option type="QString" value="ccw" name="direction"/>
213+
<Option type="QString" value="0" name="discrete"/>
214+
<Option type="QString" value="gradient" name="rampType"/>
215+
<Option type="QString" value="rgb" name="spec"/>{stops_option}
216+
</Option>
217+
</colorramp>
218+
{items_xml}
219+
<rampLegendSettings suffix="" direction="0" prefix="" maximumLabel="" useContinuousLegend="1" minimumLabel="" orientation="2">
220+
<numericFormat id="basic">
221+
<Option type="Map">
222+
<Option name="decimal_separator" type="invalid"/>
223+
<Option name="decimals" value="6" type="int"/>
224+
<Option name="rounding_type" value="0" type="int"/>
225+
<Option name="show_plus" value="false" type="bool"/>
226+
<Option name="show_thousand_separator" value="true" type="bool"/>
227+
<Option name="show_trailing_zeros" value="false" type="bool"/>
228+
<Option name="thousand_separator" type="invalid"/>
229+
</Option>
230+
</numericFormat>
231+
</rampLegendSettings>
232+
</colorrampshader>
233+
</rastershader>
234+
</rasterrenderer>
235+
<brightnesscontrast gamma="1" contrast="0" brightness="0"/>
236+
<huesaturation colorizeGreen="128" colorizeStrength="100" invertColors="0" saturation="0" colorizeOn="0" colorizeBlue="128" grayscaleMode="0" colorizeRed="255"/>
237+
<rasterresampler maxOversampling="2"/>
238+
<resamplingStage>resamplingFilter</resamplingStage>
239+
</pipe>
240+
<blendMode>0</blendMode>
241+
</qgis>
242+
'''
243+
Path(output_path).write_text(qml)
244+
print(f"Created: {output_path} ({len(entries)} color stops)")
245+
246+
247+
def main():
248+
parser = argparse.ArgumentParser(
249+
description="Convert OGC SLD raster ColorMap files to QGIS QML format."
250+
)
251+
parser.add_argument("sld_files", nargs="+", type=Path, help="SLD file(s) to convert")
252+
parser.add_argument(
253+
"-o",
254+
"--output",
255+
type=Path,
256+
default=None,
257+
help="Output QML path (only valid with a single input file)",
258+
)
259+
args = parser.parse_args()
260+
261+
if args.output and len(args.sld_files) > 1:
262+
parser.error("-o/--output can only be used with a single input file")
263+
264+
for sld_path in args.sld_files:
265+
if not sld_path.exists():
266+
print(f"SKIP (not found): {sld_path}")
267+
continue
268+
qml_path = args.output if args.output else sld_path.with_suffix(".qml")
269+
entries = parse_sld(sld_path)
270+
if not entries:
271+
print(f"SKIP (no ColorMapEntry found): {sld_path}")
272+
continue
273+
generate_qml(entries, qml_path)
274+
275+
276+
if __name__ == "__main__":
277+
main()

data-processing/src/raster_config.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,4 +786,62 @@ layers:
786786
style_file: "SOC_change.qml"
787787
output_file: "../data/processed/Agriculture/India/Rasters/India_SOC_Change.tif"
788788
layer_name: "India_SOC_Change"
789+
max_zoom: 14
790+
791+
# Ghana - Agriculture
792+
ghana_crop_type:
793+
base_path: "../data/raw/Agriculture/Ghana/Ghana/CropType"
794+
input_file: "openEO_Tono IRRIGABLE AREA_2023_WRLDCOV_CROP_CLIP_postproc_DEFthr_clip.tif"
795+
style_file: "Crop_Type_Style.qml"
796+
output_file: "../data/processed/Agriculture/Ghana/Rasters/Ghana_CropType.tif"
797+
layer_name: "Ghana_CropType"
798+
max_zoom: 14
799+
800+
ghana_precipitation:
801+
base_path: "../data/raw/Agriculture/Ghana/Ghana/Country_Met"
802+
input_file: "Precipitation_Country_1981-2010.tif"
803+
style_file: "Precipitation_Country_1981-2010.qml"
804+
output_file: "../data/processed/Agriculture/Ghana/Rasters/Ghana_Precipitation.tif"
805+
layer_name: "Ghana_Precipitation"
806+
max_zoom: 9
807+
808+
ghana_temperature:
809+
base_path: "../data/raw/Agriculture/Ghana/Ghana/Country_Met"
810+
input_file: "Temperature_Country_1981-2010.tif"
811+
style_file: "Temperature_Country_1981-2010.qml"
812+
output_file: "../data/processed/Agriculture/Ghana/Rasters/Ghana_Temperature.tif"
813+
layer_name: "Ghana_Temperature"
814+
max_zoom: 9
815+
816+
ghana_wind_speed:
817+
base_path: "../data/raw/Agriculture/Ghana/Ghana/Country_Met"
818+
input_file: "WindSpeed_Country_1981-2010.tif"
819+
style_file: "WindSpeed_Country_1981-2010.qml"
820+
output_file: "../data/processed/Agriculture/Ghana/Rasters/Ghana_WindSpeed.tif"
821+
layer_name: "Ghana_WindSpeed"
822+
max_zoom: 9
823+
824+
ghana_solar_radiation:
825+
base_path: "../data/raw/Agriculture/Ghana/Ghana/Country_Met"
826+
input_file: "SolarRadiation_Country_1981-2010.tif"
827+
style_file: "SolarRadiation_Country_1981-2010.qml"
828+
output_file: "../data/processed/Agriculture/Ghana/Rasters/Ghana_SolarRadiation.tif"
829+
layer_name: "Ghana_SolarRadiation"
830+
max_zoom: 9
831+
832+
ghana_soil_moisture:
833+
base_path: "../data/raw/Agriculture/Ghana/Ghana/Country_Met"
834+
input_file: "SoilMoisture_Country_1981-2010.tif"
835+
style_file: "SoilMoisture_Country_1981-2010.qml"
836+
output_file: "../data/processed/Agriculture/Ghana/Rasters/Ghana_SoilMoisture.tif"
837+
layer_name: "Ghana_SoilMoisture"
838+
max_zoom: 9
839+
840+
# Rwanda-Uganda - Agriculture
841+
uganda_yield_estimation:
842+
base_path: "../data/raw/Agriculture/Rwanda-Uganda/QGIS_layers/4_Yield-estimation"
843+
input_file: "Uganda-kassnada-Mityana-districts/GDA-AGRI_UC8-Uganda_Kassanda-district_maize_Yield-Estimation_20230301-20230830_10m.tif"
844+
style_file: "yield_style.qml"
845+
output_file: "../data/processed/Agriculture/Rwanda-Uganda/Rasters/Uganda_YieldEstimation.tif"
846+
layer_name: "Uganda_YieldEstimation"
789847
max_zoom: 14

data-processing/src/vector_config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,3 +453,11 @@ layers:
453453
min_zoom: 3
454454
max_zoom: 9
455455

456+
# Rwanda-Uganda - Agriculture
457+
rwanda_maize_crop_map:
458+
input_file: "../data/raw/Agriculture/Rwanda-Uganda/QGIS_layers/2_Crop-mapping/Rwanda-AOI3/GDA-AGRI_UC8-Rwanda_AOI3_maize-map_20230301-20230831_total.shp"
459+
output_file: "../data/processed/Agriculture/Rwanda-Uganda/Vectors/Rwanda_MaizeCropMap.mbtiles"
460+
layer_name: "Rwanda_MaizeCropMap"
461+
min_zoom: 6
462+
max_zoom: 14
463+

0 commit comments

Comments
 (0)