简体   繁体   中英

Plotting categorical raster data with matplotlib imshow - how to get colors right

I'm trying to plot a raster image 'test_img.tif' with plt.imshow(). It is a landcover image and every unique value is a different landcover class. The problem is that the cmap doesn't seem to set the colors right as I'm creating a custom cmap with ListedColormap. Although the plot legend is getting plotted right, the colors aren't.

(The image is here https://drive.google.com/file/d/1aN95UnYmw52GHW017sbfcf4JDSWHIou4/view?usp=sharing )

I'm using the following code:

## Loading TIF file
import rioxarray as rxr
data_path = 'folder/test_img.tif'
lctif_1km_20530 = rxr.open_rasterio(data_path, masked=True).squeeze()
lctif_1km_20530 = lctif_1km_20530.astype('int')

### Getting unique classes from array and setting color and legend list
import numpy as np

classes_20530 = list(np.unique(lctif_1km_20530).astype('int'))
color_list_20530 = ['#FAF5E4', '#006400', '#45C2A5', '#B8AF4F', '#fff3bf', '#0000FF']
class_names_20530 = ['Out of buffer','Forest Formation','Wetlands','Grassland', 'Mosaic Agriculture and Pasture','River, Lake and Ocean']

# Plot newly classified and masked raster
import matplotlib.pyplot as plt
import earthpy.plot as ep
from matplotlib.colors import ListedColormap

cmap_20530 = ListedColormap(color_list_20530)
f, ax = plt.subplots(figsize=(10,5))
im = ax.imshow(lctif_1km_20530, cmap = cmap_20530)
ax.set(title="Landcover classes")
ep.draw_legend(im, titles = class_names_20530, classes = classes_20530)
ax.set_axis_off()
plt.show()

I've created the image with the expected output in QGIS (Rendering type: Palette/Unique values) to compare:

方法比较

Can anybody help me? Thanks a lot in advance.

I had the same problem and solved it for an 8 land use class visualization. In your example the landcover class Forest is missing, that's why the colour mapping is faulty.

# get np with shape of matplotlib cms, here I use viridis.
cm_solution_map = viridis(np.linspace(0, 1, 256))
#define rgba of you landclasses
cropland_1 = np.array([256 / 256, 246 / 256, 143 / 256, 1])
cropland_2 = np.array([238 / 256, 230 / 256, 133 / 256, 1])
cropland_3 = np.array([256 / 256, 215 / 256, 1 / 256, 1])
cropland_4 = np.array([238 / 256, 201 / 256, 0 / 256, 1])
cropland_5 = np.array([205 / 256, 173 / 256, 1 / 256, 1])
pasture = np.array([162 / 256, 205 / 256, 90 / 256, 1])
forest = np.array([34 / 256, 139 / 256, 34 / 256, 1])
urban = np.array([105 / 256, 105 / 256, 105 / 256, 1])
# identify whether or not the landclasses occour in your map. This can be done more elegant in a loop, but for readability I didn't do it
        if 1. in solution_landuse_map:
        plot_cropland_1 = True
        nr_classes += 1
    else:
        plot_cropland_1 = False

    if 2. in solution_landuse_map:
        plot_cropland_2 = True
        nr_classes += 1
    else:
        plot_cropland_2 = False

    if 3. in solution_landuse_map:
        plot_cropland_3 = True
        nr_classes += 1
    else:
        plot_cropland_3 = False

    if 4. in solution_landuse_map:
        plot_cropland_4 = True
        nr_classes += 1
    else:
        plot_cropland_4 = False

    if 5. in solution_landuse_map:
        plot_cropland_5 = True
        nr_classes += 1
    else:
        plot_cropland_5 = False
    if 6. in solution_landuse_map:
        plot_forest = True
        nr_classes += 1
    else:
        plot_forest = False
    if 7. in solution_landuse_map:
        plot_pasture = True
        nr_classes += 1
    else:
        plot_pasture = False
    if 8. in solution_landuse_map:
        plot_urban = True
        nr_classes += 1
    else:
        plot_urban = False
#now you can update your colormap array. Again, you can do it in a loop and more elegant.
class_counter = 0
    rgb_interval = int(256 / nr_classes)+1
    if plot_cropland_1:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = cropland_1
        class_counter += 1
    if plot_cropland_2:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = cropland_2
        class_counter += 1
    if plot_cropland_3:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = cropland_3
        class_counter += 1
    if plot_cropland_4:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = cropland_4
        class_counter += 1
    if plot_cropland_5:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = cropland_5
        class_counter += 1
    if plot_forest:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = forest
        class_counter += 1
    if plot_pasture:
        cm_solution_map[(rgb_interval * class_counter):(rgb_interval * (class_counter + 1)), :] = pasture
        class_counter += 1
    if plot_urban:
        cm_solution_map[(rgb_interval * class_counter):(256), :] = urban
        class_counter += 1

    cmp_solution_map = ListedColormap(cm_solution_map)
# now, you can use this cmap in ax.imshow as cmap argument

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM