[Model.py 02] Implementation of map scaling

Requirement: Enlarge the map proportionally

Analysis: Considering that the relative positional relationships of objects such as rivers and roads need to be preserved during map enlargement, here we choose to merge the coordinate matrices of objects such as rivers and roads into terrain_matrix and perform scaling on this merged matrix. . The magnified matrix updates the magnification matrix of the respective object according to the position of the object marked in the matrix.

Idea: To enlarge the map proportionally, borrow the idea of image enlargement and use nearest neighbor interpolation to fill the gaps created after enlargement.

accomplish:

Supplementary resizing parametersself.zoomScale

Before initializing self.grid in _init_, add the resizing parameter: self.zoomScale. The code for the modification part of the _init_ function is as follows:

 def __init__(self, N=10, K=0, width=50, height=102,
                 civil_info_exchange=True, model_layer=-1, is_fired=False, is_flood=True, count=0):
        self.warning_UI = "" # Warning information
        self.resizeScale=3 # parameter using to control the scale of the map

Add map enlargement and update code in the _init_ function

 # Load the safe passage coordinates into the map
        self.draw_environment(self.pos_exits)
        #Create coordinate space
        self.graph = path_finding.create_graph(self)


        self.combine_matrices()
        # To rescale the terrain map size after combination.
        self.resize_matrices(self.resizeScale)
        self.separate_matrix() # update different object matrix from the resized matrix.

Definition of map magnification function resize_matrices

 def resize_matrices(self, degree):
        """
        Resize the terrain and constituent matrices while maintaining aspect ratio.

        :param degree: New rescaling degree.
        """
        scaling_factor = (degree, degree)

        # Resize the main terrain matrix
        self.terrain_matrix = zoom(self.terrain_matrix, scaling_factor, order=0)
        # order=1 for bilinear interpolation and 0 for nearest-neighbor interpolation.
        # This change will ensure that your matrices' integer values are preserved during the resizing process.

Update the coordinate matrix function of other objects after zooming inseparate_matrix

 def separate_matrix(self):
        """
        Separate the combined terrain matrix into individual feature matrices.

        Each matrix should represent one feature (e.g., river, road) with 1s where the feature exists
        and 0s where it doesn't.
        """
        # Identify all unique features in the terrain matrix, excluding 0 (empty)
        unique_features = np.unique(self.terrain_matrix)

        # Define a mapping from feature codes to the corresponding class attributes
        feature_mapping = {<!-- -->
            1: 'river_matrix',
            2: 'road_matrix',
            3: 'wall_matrix',
            4: 'indoor_matrix',
            5: 'exits_matrix',
            6: 'pillar_matrix',
            7: 'ditch_matrix'
        }

        # Initialize each matrix as an array of zeros
        for matrix_name in feature_mapping.values():
            setattr(self, matrix_name, np.zeros_like(self.terrain_matrix))

        # For each feature, populate the corresponding matrix
        for feature in unique_features:
            if feature == 0:
                continue # Skip the 'empty' feature

            matrix_name = feature_mapping.get(feature)
            if not matrix_name:
                continue # Skip if the feature is not recognized

            #Update the corresponding matrix directly
            feature_matrix = np.where(self.terrain_matrix == feature, 1, 0)
            setattr(self, matrix_name, feature_matrix)

        # At this point, each feature matrix (e.g., self.river_matrix) has been updated directly
        # No need to return anything since we're modifying the class attributes directly

Supplement: Visual script of the enlarged map

Please run it with a separate .py file.

# It's assumed you have executed the following installation command in your local environment:
# !pip install datashader

import datashader as ds
import datashader.transfer_functions as tf
import pandas as pd

# Load and prepare the data
from matplotlib import pyplot as plt

file_path = 'G:\terrain_matrix3.csv'
data = pd.read_csv(file_path)

# Calculate the aspect ratio of the original data
num_rows, num_cols = data.shape # Assuming 'data' is your DataFrame
aspect_ratio = num_cols / num_rows

# Ensure column headers are consistent and represent coordinates or indexing
# If headers are numeric: good; if not, you might want to set headers as a range of numbers representing columns
data.columns = range(len(data.columns))

# Resetting the index to turn it into a column for melting
data = data.reset_index()

# Melting the data (now 'X' should be consistently numeric)
melted_data = data.melt(id_vars=['index'], var_name='X', value_name='Value')
melted_data['Y'] = melted_data['index']
melted_data.drop(columns=['index'], inplace=True)

# Convert 'X' and 'Y' to numeric values, coercing errors (i.e., non-numeric values are set as NaN)
melted_data['X'] = pd.to_numeric(melted_data['X'], errors='coerce')
melted_data['Y'] = pd.to_numeric(melted_data['Y'], errors='coerce')

# Handle or remove any rows with NaN if necessary (created by 'coerce')
melted_data.dropna(subset=['X', 'Y'], inplace=True)

# Define the dimensions for the canvas
plot_width = 800
plot_height = int(plot_width / aspect_ratio) # Maintain the aspect ratio of the original data

# Set up the canvas with the correct aspect ratio
canvas = ds.Canvas(plot_width=plot_width, plot_height=plot_height)

# Aggregating data into a grid
agg = canvas.points(melted_data, 'X', 'Y', ds.mean('Value'))

# Creating an image by coloring the aggregated data
img = tf.shade(agg, cmap=['lightblue', 'darkblue'], how='linear')

# Convert the Datashader image to a format that can be displayed by matplotlib
img_to_plot = tf.shade(agg, cmap=['lightblue', 'darkblue'], how='linear')
img_plt = tf.set_background(img_to_plot, 'white')

# Display the image using matplotlib
plt.imshow(img_plt.to_pil())
plt.axis('off') # Optional: this removes the axes for a cleaner look
plt.title('Presentation of the rescaling map data(3X).')

plt.show()

The following is an example of the output of the visual script. Among them, 2X and 3X respectively represent the set map magnification.