016] How to Automate Parcel Image Exports in Global Mapper Using Python

Batch export high-resolution parcel images with satellite backgrounds using Global Mapper's Python SDK.

This tutorial provides a complete Python script designed to automate the repetitive task of zooming into individual parcels and capturing images. By leveraging Global Mapper's scripting capabilities, users can process hundreds of vector features into standardized image outputs in seconds, significantly improving report generation efficiency.

Why I Created This Script

Today, I’m finally sharing a script I wrote some time ago and recently refined. I ask for your understanding; it has been difficult to concentrate on writing lately.

The reason I created this script was because I saw a close junior of mine manually zooming in and out of hundreds of parcels in CAD, capturing each one for a report. I figured there had to be a better way than manually capturing hundreds of parcels one by one.

Main map view showing a satellite image background with bright green parcel vector outlines
img 1 : Main map view showing a satellite image background with bright green parcel vector outlines

What This Workflow Does

As shown in the image above, you can divide the cadastral map by parcel into separate layers and then perform the following operations. Experienced Global Mapper users will probably find this setup straightforward.

In this workflow, each parcel is stored as an individual vector layer. The script iterates through layers rather than individual features, which keeps the export logic simple and fast.

I’ll cover the layer separation workflow in a future tutorial.

I originally planned to introduce integration with external languages after the lectures had progressed a bit more, For now, I simply want to demonstrate what can be automated using the Global Mapper Python SDK.

Required Workspace Setup

Raster Layer Requirements

The satellite image layer (raster layer) at the bottom must be at the very top of the layer list...

Parcel Layer Structure

Each parcel must be separated into its own layer...

Script Execution Setup

  1. Go to the File menu -> Open Script Editor
  2. Project folders and paths must all be in English.
  3. You can change the image save path on line 35.
  4. The image aspect ratio can be adjusted on line 9.
  5. The horizontal pixel width is set on line 70.
  6. Click the Run Script button to execute.

Global Mapper Python Script

The script below automatically performs the following workflow for every parcel layer:

  • Detects the parcel extent
  • Calculates a zoom area with margins
  • Maintains a fixed 16:9 aspect ratio
  • Exports a JPG image automatically
#/usr/bin/env python 
# coding=utf8 
import globalmapper as gm 
import os 

# --- User Configuration Area ---
# Margin factor to show some area around the parcel (1.5 = 50% margin)
zoom_out_factor = 1.5   
# Target aspect ratio for the output images (Standard Widescreen)
aspect_ratio = 16/9     

# Fetch all currently loaded layers in the workspace
layers_are_loaded = gm.GetLoadedLayerList() 

if not layers_are_loaded: 
    print("No layers are currently open, so nothing was exported.") 
else: 
    # Extract the layer pointers and count
    arr_ptr, arr_size = layers_are_loaded 
    all_layers = gm.GM_LayerHandle_array_frompointer(arr_ptr) 
    raster_layers = [] 
    vector_layers = [] 
    
    # Categorize layers into Raster (Background) and Vector (Parcels)
    for layer in gm.carray_to_list(all_layers, arr_size): 
        layer_info = gm.GetLayerInfo(layer) 
        if layer_info.mHasVectorData:
            vector_layers.append(layer) 
        elif layer_info.mHasRasterData: 
            raster_layers.append(layer) 
            
    # Verify that both background and target parcels exist
    if len(vector_layers) == 0:
        print("No vector layers were found, so nothing was exported.") 
    elif len(raster_layers) == 0:
        print("No raster layers were found to use as background.") 
    else: 
        # Define the output folder path (Must exist or be created)
        output_directory = os.path.expanduser("F:\\2024-08-26_export\\result\\") 
        print("Exporting files to {}".format(output_directory)) 
        
        # Enable the first raster layer as the static background
        gm.SetLayerEnabled(raster_layers[0], True) 
        
        # Iterate through each vector layer (each parcel)
        for layer in vector_layers: 
            layer_info = gm.GetLayerInfo(layer) 
            try: 
                # Attempt to fix encoding issues for non-English descriptions
                if layer_info.mCodePage == 949: # Korean CP949
                    layer_name = layer_info.mDescription.encode('raw_unicode_escape').decode('cp949') 
                elif layer_info.mCodePage == 0: # Default/Latin
                    layer_name = layer_info.mDescription.encode("latin1").decode("cp1252") 
                else: 
                    layer_name = layer_info.mDescription 
            except UnicodeDecodeError: 
                layer_name = layer_info.mDescription 
            
            # Sanitize the filename for file system compatibility
            name_wo_ext = layer_name[:layer_name.index(".")] if "." in layer_name else layer_name 
            name_wo_ext = name_wo_ext.replace(" ", "_") 
            name_wo_ext = "".join(c for c in name_wo_ext if c.isalnum() or c in "_-") 
            
            # Calculate the geographic bounding box of the current parcel
            width_meters = layer_info.mGlobalRect.mMaxX - layer_info.mGlobalRect.mMinX 
            height_meters = layer_info.mGlobalRect.mMaxY - layer_info.mGlobalRect.mMinY 
            dim_ratio = width_meters / height_meters 
            
            # Find the geometric center of the parcel
            center_x = (layer_info.mGlobalRect.mMaxX + layer_info.mGlobalRect.mMinX) / 2 
            center_y = (layer_info.mGlobalRect.mMaxY + layer_info.mGlobalRect.mMinY) / 2 
            
            # Adjust the capture rectangle to fit the 16:9 aspect ratio
            if dim_ratio > aspect_ratio:
                new_width = width_meters * zoom_out_factor
                new_height = new_width / aspect_ratio
            else:
                new_height = height_meters * zoom_out_factor
                new_width = new_height * aspect_ratio
                
            # Define the new rectangle coordinates centered on the parcel
            new_min_x = center_x - new_width / 2 
            new_max_x = center_x + new_width / 2 
            new_min_y = center_y - new_height / 2 
            new_max_y = center_y + new_height / 2 
            
            export_rect = gm.GM_Rectangle_t(new_min_x, new_min_y, new_max_x, new_max_y) 
            
            # Temporarily enable the parcel layer for capturing
            gm.SetLayerEnabled(layer, True) 
            output_filename = os.path.join(output_directory, "{}.jpg".format(name_wo_ext)) 
            
            # Set fixed resolution (HD width, height calculated by ratio)
            HD_WIDTH = 800 
            HD_HEIGHT = int(HD_WIDTH / aspect_ratio) 
            
            # Export the defined area as a JPG image
            error_code = gm.ExportRaster(output_filename, gm.GM_Export_JPG, 0x0, export_rect, HD_WIDTH, HD_HEIGHT, gm.GM_ExportFlags_AddAlpha) 
            
            if error_code == gm.GM_Error_None: 
                print("Exported {} to {}".format(layer_name, output_filename)) 
            else: 
                print("Error exporting {}: {}".format(layer_name, gm.strerror(error_code))) 
                
            # Disable the parcel layer so it doesn't overlap the next export
            gm.SetLayerEnabled(layer, False) 

        # Turn off the raster background after finishing all exports
        gm.SetLayerEnabled(raster_layers[0], False)

How the Script Works

Layer Detection

The script automatically separates raster and vector layers using the Global Mapper API.

Automatic Zoom and Aspect Ratio

Each parcel is centered automatically, and the export extent is adjusted to maintain a consistent 16:9 image ratio.

Automated Batch Export

The script loops through every parcel layer and exports individual JPG images with consistent framing.

Important Notes About Encoding

I tinkered with lines 47–54 while trying to handle character conversion, but eventually left it as is. It works fine for English, so you can use it without issues. I was going to fix it but felt a bit too lazy.

Performance Test

I tested it with 500 parcels, and the export finished in less than 20 seconds. It’s incredibly satisfying.

Export Results

Windows Explorer view showing a grid of exported parcel images with satellite backgrounds
img 2 : Windows Explorer view showing a grid of exported parcel images with satellite backgrounds

As a side note, when using Python scripts in Global Mapper, you should handle everything in English. The path where the workspace is saved, the workspace name itself, and even the comments should ideally be in English for best compatibility.

That's all for today...

Continue Learning
Explore complete tutorial collections for CityEngine and Global Mapper.

Comments

Popular posts from this blog

029] How to Split Polygon Areas with Intersecting Lines in Global Mapper

[008] How to Understand CGA Rule Structure in CityEngine (Beginner Guide)

046] How to Create Parallel Offset Lines in Global Mapper