=== versions === python 3.12.0 | numpy 2.5.4 | pandas 3.0.6 | odc-stac 0.5.3 | rasterio 1.5.2 | geopandas 1.2.0 | matplotlib 3.11.2 === Step 1: study area, labels and samples === AOI: Dark Peak (Kinder Scout, Bleaklow, Edale and the Hope Valley) grid: EPSG:27700, 10 m, bounds (400000, 377500, 425000, 400000), 2500 x 2250 pixels 9,000 samples in 90 blocks of 2.5 km; 6,544 train / 2,456 test === Step 2: seasonal Sentinel-2 L2A composites, 2023 === 50 dates with at least 5% of the AOI clear; median clear fraction 31% winter median 3 clear views; none at 3.56% of pixels spring median 5 clear views; none at 0.02% of pixels summer median 7 clear views; none at 0.03% of pixels autumn median 6 clear views; none at 0.00% of pixels === Step 3: spectral indices === 29 feature layers; samples with any missing value: 312 median of each feature by class (training samples only): bog heath brack semin impro broad conif water built ndvi_spring 0.559 0.650 0.481 0.722 0.810 0.674 0.775 0.238 0.438 ndvi_summer 0.686 0.741 0.746 0.793 0.828 0.877 0.822 0.107 0.447 dndvi_spring_summer 0.116 0.082 0.240 0.082 0.019 0.194 0.034 -0.101 0.006 ndmi_spring 0.071 0.061 -0.011 0.242 0.325 0.134 0.345 0.022 0.053 nir_summer 0.311 0.289 0.411 0.463 0.488 0.418 0.261 0.024 0.233 swir_summer 0.236 0.203 0.251 0.248 0.236 0.173 0.112 0.020 0.198 red_summer 0.057 0.043 0.061 0.053 0.046 0.027 0.026 0.019 0.083 red_spring 0.075 0.047 0.102 0.066 0.044 0.044 0.027 0.012 0.074 === Step 4: fit the decision-list thresholds on the training blocks === 1. Water if nir_summer < 0.119 (train F1 0.94, n = 6,544) 2. Built-up and bare if ndvi_summer < 0.546 (train F1 0.68, n = 5,791) 3. Conifer woodland if swir_summer < 0.151 (train F1 0.94, n = 5,142) 4. Broadleaf woodland if red_summer < 0.031 (train F1 0.75, n = 4,251) 5. Bracken if dndvi_spring_summer > 0.213 (train F1 0.65, n = 3,694) 6. Improved grassland if ndvi_spring > 0.756 (train F1 0.69, n = 3,020) 7. Semi-natural grassland if nir_spring > 0.359 (train F1 0.68, n = 2,207) 8. Blanket bog if red_spring > 0.055 (train F1 0.62, n = 1,763) 9. otherwise -> Heath === Step 5: classify the whole study area === rules_km2 living_england_km2 Blanket bog 131.0 90.5 Heath 109.9 151.5 Bracken 64.1 41.6 Semi-natural grassland 63.5 96.1 Improved grassland 101.9 90.0 Broadleaf woodland 35.4 39.7 Conifer woodland 17.3 17.2 Water 6.3 8.4 Built-up and bare 32.9 25.7 unclassified (no clear spring or summer view): 0.05% of pixels === Step 6: accuracy on the held-out test blocks === confusion matrix (rows = Living England reference, columns = rule-based map): bog heath brack semin impro broad conif water built ref bog 170 28 24 5 1 0 0 0 29 ref heath 58 197 13 5 5 27 1 0 20 ref bracken 77 10 172 13 1 2 0 0 10 ref seminat_grass 24 6 9 84 228 13 0 0 6 ref improved_grass 5 13 4 28 210 15 0 0 7 ref broadleaf 10 35 22 5 34 188 22 0 0 ref conifer 3 8 0 2 2 6 99 0 0 ref water 3 3 1 0 0 0 3 115 13 ref built_bare 68 33 0 1 13 3 1 5 238 n_test producers_acc users_acc f1 Blanket bog 257 0.661 0.407 0.504 Heath 326 0.604 0.592 0.598 Bracken 285 0.604 0.702 0.649 Semi-natural grassland 370 0.227 0.587 0.327 Improved grassland 282 0.745 0.425 0.541 Broadleaf woodland 316 0.595 0.740 0.660 Conifer woodland 120 0.825 0.786 0.805 Water 138 0.833 0.958 0.891 Built-up and bare 362 0.657 0.737 0.695 overall accuracy 0.600 (n = 2,456 samples in 27 test blocks) 95% CI treating samples as independent: 0.580 to 0.619 95% CI from a block bootstrap: 0.527 to 0.664 macro F1 0.630; Cohen's kappa 0.547 disagreement 0.400 = quantity 0.157 + allocation 0.243 area-weighted agreement (sum of reference-area share x producer's accuracy): 0.583 wall-to-wall agreement with Living England over all 1,680,440 labelled test-block pixels: 0.483 wall-to-wall agreement over the whole study area (train and test blocks): 0.490 === Step 6b: blocked 5-fold cross-validation on the training blocks === fold OA: 0.693, 0.697, 0.629, 0.720, 0.681; mean 0.684 +/- 0.030 === Step 7: random split versus spatial blocks === median distance from test to nearest training sample: random split 103 m, block split 785 m overall accuracy over 20 repeated 70/30 splits: rules nearest_neighbour_xy mean std mean std split blocks 0.643 0.029 0.412 0.049 random 0.670 0.010 0.806 0.005 saved 6 figures and habitat_rules_2023.tif to output/