Thursday, April 9, 2026

Comparison of AI Generated Cartographic Webmap

 In this post, I will provide a simple request in plain English to four AI tools (namely: Gemini, ChatGPT, DeepSeek and Grok) to compare the results we get.

Generate html webmap with all the necessary cartographic elements

Note that the request statement isn't detailed, as no specific location and technical functionality were provided. This is delibrate to see how each AI tool thinker and solve the problem.


Gemini


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Professional Web Map Interface</title>
    
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    
    <style>
        body { margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
        #map { height: 100vh; width: 100vw; }

        /* Map Legend Styling */
        .legend {
            padding: 10px;
            background: white;
            line-height: 1.5;
            color: #333;
            border-radius: 5px;
            box-shadow: 0 0 15px rgba(0,0,0,0.2);
        }
        .legend i {
            width: 18px;
            height: 18px;
            float: left;
            margin-right: 8px;
            opacity: 0.7;
        }

        /* Header Overlay */
        .map-header {
            position: absolute;
            top: 10px;
            left: 50px;
            z-index: 1000;
            background: rgba(255, 255, 255, 0.9);
            padding: 5px 15px;
            border-radius: 4px;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>

<div class="map-header">
    <h2>City Infrastructure Overview</h2>
</div>

<div id="map"></div>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>

<script>
    // 1. Initialize the Map
    const map = L.map('map').setView([45.523062, -122.676482], 13);

    // 2. Base Layers (Cartography Element: Reference Frame)
    const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap contributors'
    }).addTo(map);

    const satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
        attribution: 'Tiles &copy; Esri'
    });

    // 3. Data Layers (Thematic Elements)
    const parks = L.layerGroup();
    L.marker([45.528, -122.662]).bindPopup("<b>Holladay Park</b><br>Public Green Space").addTo(parks);
    L.marker([45.515, -122.684]).bindPopup("<b>Portland Art Museum</b><br>Cultural Center").addTo(parks);

    // 4. Layer Control (Interactivity)
    const baseMaps = {
        "Street Map": osm,
        "Satellite": satellite
    };
    const overlayMaps = {
        "Points of Interest": parks
    };
    L.control.layers(baseMaps, overlayMaps, {collapsed: false}).addTo(map);

    // 5. Scale Bar (Cartography Element: Measurement)
    L.control.scale({ imperial: true, metric: true, position: 'bottomleft' }).addTo(map);

    // 6. Custom Legend (Cartography Element: Symbology)
    const legend = L.control({position: 'bottomright'});
    legend.onAdd = function (map) {
        const div = L.DomUtil.create('div', 'legend');
        div.innerHTML += '<h4>Map Legend</h4>';
        div.innerHTML += '<i style="background: #2196F3"></i><span>Commercial</span><br>';
        div.innerHTML += '<i style="background: #4CAF50"></i><span>Parks</span><br>';
        div.innerHTML += '<i style="background: #F44336"></i><span>Critical Infrastructure</span><br>';
        return div;
    };
    legend.addTo(map);

</script>
</body>
</html>



ChatGPT




<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Smart Campus Web Map</title>
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
  <style>
    html, body { margin: 0; padding: 0; height: 100%; font-family: Arial, sans-serif; }
    #map { width: 100%; height: 100vh; }
    .map-title {
      position: absolute;
      top: 10px;
      left: 50%;
      transform: translateX(-50%);
      z-index: 1000;
      background: white;
      padding: 10px 18px;
      border-radius: 10px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.2);
      font-weight: bold;
      font-size: 18px;
    }
    .north-arrow {
      position: absolute;
      top: 70px;
      right: 15px;
      z-index: 1000;
      background: white;
      padding: 8px;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.2);
      text-align: center;
      font-weight: bold;
    }
    .legend {
      position: absolute;
      bottom: 30px;
      right: 15px;
      z-index: 1000;
      background: white;
      padding: 10px;
      border-radius: 10px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.2);
      line-height: 1.6;
      font-size: 14px;
    }
    .legend i {
      width: 14px;
      height: 14px;
      float: left;
      margin-right: 8px;
      opacity: 0.8;
    }
    .coords {
      position: absolute;
      bottom: 10px;
      left: 10px;
      z-index: 1000;
      background: rgba(255,255,255,0.9);
      padding: 6px 10px;
      border-radius: 6px;
      font-size: 12px;
      box-shadow: 0 2px 6px rgba(0,0,0,0.15);
    }
  </style>
</head>
<body>
  <div class="map-title">Smart Campus Web Map</div>
  <div class="north-arrow">N<br>⬆</div>
  <div id="map"></div>
  <div class="coords" id="coords">Lat: 0, Lng: 0</div>
  <div class="legend">
    <strong>Legend</strong><br>
    <i style="background:#1f78b4"></i> Academic Buildings<br>
    <i style="background:#33a02c"></i> Hostels<br>
    <i style="background:#e31a1c"></i> Health Facilities<br>
    <i style="background:#ff7f00"></i> Roads
  </div>

  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
  <script>
    const map = L.map('map').setView([9.0579, 7.4951], 16);

    const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; OpenStreetMap contributors'
    }).addTo(map);

    const satellite = L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', {
      maxZoom: 20,
      subdomains:['mt0','mt1','mt2','mt3']
    });

    const academic = L.layerGroup([
      L.marker([9.0585, 7.4955]).bindPopup('Faculty of Science'),
      L.marker([9.0575, 7.4960]).bindPopup('Engineering Block')
    ]).addTo(map);

    const hostels = L.layerGroup([
      L.marker([9.0569, 7.4945]).bindPopup('Male Hostel'),
      L.marker([9.0564, 7.4964]).bindPopup('Female Hostel')
    ]).addTo(map);

    const health = L.layerGroup([
      L.marker([9.0580, 7.4940]).bindPopup('Campus Clinic')
    ]).addTo(map);

    const roads = L.polyline([
      [9.0560, 7.4940],
      [9.0588, 7.4965]
    ], {color: '#ff7f00'}).addTo(map);

    const baseMaps = {
      'OpenStreetMap': osm,
      'Satellite': satellite
    };

    const overlayMaps = {
      'Academic Buildings': academic,
      'Hostels': hostels,
      'Health Facilities': health,
      'Roads': roads
    };

    L.control.layers(baseMaps, overlayMaps).addTo(map);
    L.control.scale().addTo(map);

    map.on('mousemove', function(e) {
      document.getElementById('coords').innerHTML =
        `Lat: ${e.latlng.lat.toFixed(5)}, Lng: ${e.latlng.lng.toFixed(5)}`;
    });
  </script>
</body>
</html>



DeepSeek


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Professional PLSS Section Locator</title>
    
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    
    <style>
        body { background-color: #f4f7f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
        #map { height: 600px; width: 100%; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); border: 2px solid white; }
        .card { border: none; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
        .form-label { font-weight: 600; font-size: 0.8rem; color: #555; text-transform: uppercase; }
        .btn-search { background-color: #2e7d32; color: white; border: none; }
        .btn-search:hover { background-color: #1b5e20; color: white; }
        .btn-clear { background-color: #78909c; color: white; }
        .copy-btn { padding: 2px 8px; font-size: 0.7rem; cursor: pointer; }
    </style>
</head>
<body>

<div class="container-fluid py-4">
    <div class="row">
        <div class="col-md-4 col-lg-3">
            <div class="card p-4 mb-3">
                <h5 class="text-success mb-3 border-bottom pb-2">PLSS Locator</h5>
                
                <form id="plssForm">
                    <div class="mb-3">
                        <label class="form-label">State</label>
                        <select id="state" class="form-select" onchange="updateMeridians()" required>
                            <option value="">Select State...</option>
                            <option value="AL">Alabama</option><option value="AK">Alaska</option>
                            <option value="AZ">Arizona</option><option value="AR">Arkansas</option>
                            <option value="CA">California</option><option value="CO">Colorado</option>
                            <option value="FL">Florida</option><option value="ID">Idaho</option>
                            <option value="IL">Illinois</option><option value="IN">Indiana</option>
                            <option value="IA">Iowa</option><option value="KS">Kansas</option>
                            <option value="LA">Louisiana</option><option value="MI">Michigan</option>
                            <option value="MN">Minnesota</option><option value="MS">Mississippi</option>
                            <option value="MO">Missouri</option><option value="MT">Montana</option>
                            <option value="NE">Nebraska</option><option value="NV">Nevada</option>
                            <option value="NM">New Mexico</option><option value="ND">North Dakota</option>
                            <option value="OH">Ohio</option><option value="OK">Oklahoma</option>
                            <option value="OR">Oregon</option><option value="SD">South Dakota</option>
                            <option value="UT">Utah</option><option value="WA">Washington</option>
                            <option value="WI">Wisconsin</option><option value="WY">Wyoming</option>
                        </select>
                    </div>

                    <div class="mb-3">
                        <label class="form-label">Principal Meridian</label>
                        <select id="meridian" class="form-select" required>
                            <option value="">Choose State First</option>
                        </select>
                    </div>

                    <div class="row g-2">
                        <div class="col-8 mb-3">
                            <label class="form-label">Township</label>
                            <input type="number" id="twp" class="form-control" placeholder="38" required>
                        </div>
                        <div class="col-4 mb-3">
                            <label class="form-label">Dir</label>
                            <select id="twpDir" class="form-select">
                                <option value="N">N</option><option value="S">S</option>
                            </select>
                        </div>
                    </div>

                    <div class="row g-2">
                        <div class="col-8 mb-3">
                            <label class="form-label">Range</label>
                            <input type="number" id="rng" class="form-control" placeholder="56" required>
                        </div>
                        <div class="col-4 mb-3">
                            <label class="form-label">Dir</label>
                            <select id="rngDir" class="form-select">
                                <option value="E">E</option><option value="W">W</option>
                            </select>
                        </div>
                    </div>

                    <div class="mb-4">
                        <label class="form-label">Section</label>
                        <input type="number" id="sec" class="form-control" placeholder="10" required>
                    </div>

                    <div class="d-grid gap-2">
                        <button type="submit" class="btn btn-search">Find on Map</button>
                        <button type="button" class="btn btn-clear" onclick="clearMap()">Clear Map</button>
                    </div>
                </form>
            </div>
            <div id="statusBox" class="alert d-none"></div>
        </div>

        <div class="col-md-8 col-lg-9">
            <div id="map"></div>
        </div>
    </div>
</div>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>

<script>
    // 1. Data Mapping for all 30 PLSS States
    const meridianData = {
        "AL": [{ code: "10", name: "Huntsville" }, { code: "31", name: "St. Stephens" }],
        "AK": [{ code: "11", name: "Copper River" }, { code: "12", name: "Fairbanks" }, { code: "13", name: "Seward" }, { code: "14", name: "Kateel River" }, { code: "15", name: "Umiat" }],
        "AZ": [{ code: "33", name: "Gila and Salt River" }],
        "AR": [{ code: "05", name: "5th Principal" }],
        "CA": [{ code: "21", name: "Mount Diablo" }, { code: "22", name: "Humboldt" }, { code: "23", name: "San Bernardino" }],
        "CO": [{ code: "06", name: "6th Principal" }, { code: "07", name: "New Mexico" }, { code: "08", name: "Ute" }],
        "FL": [{ code: "26", name: "Tallahassee" }],
        "ID": [{ code: "09", name: "Boise" }],
        "IL": [{ code: "02", name: "2nd Principal" }, { code: "03", name: "3rd Principal" }, { code: "04", name: "4th Principal" }],
        "IN": [{ code: "02", name: "2nd Principal" }],
        "IA": [{ code: "05", name: "5th Principal" }],
        "KS": [{ code: "06", name: "6th Principal" }],
        "LA": [{ code: "30", name: "Louisiana" }, { code: "31", name: "St. Helena" }],
        "MI": [{ code: "19", name: "Michigan" }],
        "MN": [{ code: "04", name: "4th Principal" }, { code: "05", name: "5th Principal" }],
        "MS": [{ code: "28", name: "Chickasaw" }, { code: "29", name: "Choctaw" }, { code: "31", name: "St. Stephens" }, { code: "32", name: "Washington" }],
        "MO": [{ code: "05", name: "5th Principal" }],
        "MT": [{ code: "20", name: "Montana" }],
        "NE": [{ code: "06", name: "6th Principal" }],
        "NV": [{ code: "21", name: "Mount Diablo" }],
        "NM": [{ code: "07", name: "New Mexico" }],
        "ND": [{ code: "05", name: "5th Principal" }],
        "OH": [{ code: "16", name: "1st Principal" }, { code: "17", name: "Ohio River" }, { code: "18", name: "Muskingum" }],
        "OK": [{ code: "24", name: "Indian" }, { code: "25", name: "Cimarron" }],
        "OR": [{ code: "34", name: "Willamette" }],
        "SD": [{ code: "05", name: "5th Principal" }, { code: "06", name: "6th Principal" }, { code: "27", name: "Black Hills" }],
        "UT": [{ code: "26", name: "Salt Lake" }, { code: "37", name: "Uintah" }],
        "WA": [{ code: "34", name: "Willamette" }],
        "WI": [{ code: "04", name: "4th Principal" }],
        "WY": [{ code: "06", name: "6th Principal" }, { code: "38", name: "Wind River" }]
    };

    // 2. Map Setup
    const map = L.map('map').setView([39.8283, -98.5795], 4);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
    let activeMarker;

    // 3. Functions
    function updateMeridians() {
        const stateSelect = document.getElementById('state');
        const meridianSelect = document.getElementById('meridian');
        const selectedState = stateSelect.value;
        
        meridianSelect.innerHTML = '<option value="">Choose Meridian...</option>';
        
        if (meridianData[selectedState]) {
            meridianData[selectedState].forEach(m => {
                const opt = document.createElement('option');
                opt.value = m.code;
                opt.innerText = `${m.code} - ${m.name}`;
                meridianSelect.appendChild(opt);
            });
        }
    }

    function clearMap() {
        if (activeMarker) map.removeLayer(activeMarker);
        document.getElementById('plssForm').reset();
        document.getElementById('meridian').innerHTML = '<option value="">Choose State First</option>';
        document.getElementById('statusBox').classList.add('d-none');
        map.flyTo([39.8283, -98.5795], 4);
    }

    function copyToClipboard(text) {
        navigator.clipboard.writeText(text).then(() => {
            alert("Coordinates copied to clipboard!");
        });
    }

    // 4. Submission
    document.getElementById('plssForm').addEventListener('submit', async (e) => {
        e.preventDefault();
        const status = document.getElementById('statusBox');
        
        const state = document.getElementById('state').value;
        const meridian = document.getElementById('meridian').value;
        const twp = document.getElementById('twp').value;
        const twpDir = document.getElementById('twpDir').value;
        const rng = document.getElementById('rng').value;
        const rngDir = document.getElementById('rngDir').value;
        const sec = document.getElementById('sec').value;

        const legalDesc = `${state} ${meridian} T${twp}${twpDir} R${rng}${rngDir} SEC ${sec}`;
        const encodedDesc = encodeURIComponent(legalDesc).replace(/%20/g, '+');
        
        const url = `https://gis.blm.gov/arcgis/rest/services/Cadastral/BLM_Natl_PLSS_CadNSDI/MapServer/exts/CadastralSpecialServices/FindLD?legaldescription=${encodedDesc}&returnalllevels=false&f=pjson`;

        status.className = "alert alert-info mt-2 d-block";
        status.innerText = "Searching BLM Database...";

        try {
            const response = await fetch(url);
            const data = await response.json();

            if (data.features && data.features.length > 0) {
                const rings = data.features[0].geometry.rings[0];
                const lats = rings.map(p => p[1]);
                const lons = rings.map(p => p[0]);
                const lat = (Math.max(...lats) + Math.min(...lats)) / 2;
                const lon = (Math.max(...lons) + Math.min(...lons)) / 2;
                const coordString = `${lat.toFixed(6)}, ${lon.toFixed(6)}`;

                if (activeMarker) map.removeLayer(activeMarker);
                
                // Add Marker with Copy Button in Popup
                activeMarker = L.marker([lat, lon]).addTo(map)
                    .bindPopup(`
                        <strong>${legalDesc}</strong><br>
                        Coords: ${coordString}<br><br>
                        <button class="btn btn-sm btn-outline-primary" onclick="copyToClipboard('${coordString}')">Copy Coordinates</button>
                    `).openPopup();

                map.flyTo([lat, lon], 14);
                status.className = "alert alert-success mt-2";
                status.innerText = `Success: Found ${legalDesc}`;
            } else {
                status.className = "alert alert-warning mt-2";
                status.innerText = "No results. Check your Meridian or Section number.";
            }
        } catch (err) {
            status.className = "alert alert-danger mt-2";
            status.innerText = "Error connecting to service.";
        }
    });
</script>
</body>
</html>




Grok


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>World Cities Web Map - Complete Cartographic Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>
        body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
        #map { height: 100vh; width: 100%; }
        .title {
            position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
            background: rgba(255,255,255,0.9); padding: 8px 16px; border-radius: 4px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.3); z-index: 1000; font-size: 1.4em; font-weight: bold;
            text-align: center;
        }
        .credits {
            position: absolute; bottom: 10px; left: 10px; background: rgba(255,255,255,0.8);
            padding: 5px 10px; border-radius: 4px; font-size: 0.8em; z-index: 1000;
        }
        .north-arrow {
            position: absolute; bottom: 80px; right: 20px; z-index: 1000;
            background: rgba(255,255,255,0.8); padding: 5px; border-radius: 4px;
            font-size: 1.2em; text-align: center; box-shadow: 0 2px 6px rgba(0,0,0,0.3);
        }
    </style>
</head>
<body>

<div id="map"></div>

<div class="title">World Major Cities Web Map<br><small>Interactive Example with Full Cartographic Elements</small></div>

<div class="north-arrow">↑<br><small>NORTH</small></div>

<div class="credits">
    Data: Sample cities | Basemap: © OpenStreetMap contributors<br>
    Created with Leaflet | Projection: Web Mercator (EPSG:3857)
</div>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-control-scale@0.1.0/leaflet.control.scale.min.js"></script> <!-- Optional, but Leaflet has built-in now -->

<script>
// Initialize the map
var map = L.map('map', {
    center: [20, 0],  // Global view
    zoom: 2,
    zoomControl: true,
    attributionControl: true
});

// Add OpenStreetMap basemap (standard tile layer)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 19,
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// Sample thematic data: Major cities with population-based sizing and categories
var cities = [
    {name: "Tokyo", lat: 35.6762, lng: 139.6503, pop: 37400000, category: "Asia"},
    {name: "Delhi", lat: 28.6139, lng: 77.2090, pop: 32000000, category: "Asia"},
    {name: "Shanghai", lat: 31.2304, lng: 121.4737, pop: 29000000, category: "Asia"},
    {name: "São Paulo", lat: -23.5505, lng: -46.6333, pop: 22000000, category: "Americas"},
    {name: "Mexico City", lat: 19.4326, lng: -99.1332, pop: 22000000, category: "Americas"},
    {name: "New York", lat: 40.7128, lng: -74.0060, pop: 20000000, category: "Americas"},
    {name: "Cairo", lat: 30.0444, lng: 31.2357, pop: 21000000, category: "Africa"},
    {name: "Mumbai", lat: 19.0760, lng: 72.8777, pop: 20000000, category: "Asia"}
];

// Color function by category
function getColor(category) {
    return category === "Asia" ? "#e41a1c" :
           category === "Americas" ? "#377eb8" : "#4daf4a";
}

// Add circle markers (thematic layer)
var cityLayer = L.layerGroup().addTo(map);
cities.forEach(function(city) {
    var radius = Math.sqrt(city.pop) / 300; // Scaled by population
    L.circleMarker([city.lat, city.lng], {
        radius: radius,
        fillColor: getColor(city.category),
        color: "#fff",
        weight: 2,
        opacity: 1,
        fillOpacity: 0.8
    }).bindPopup(`
        <b>${city.name}</b><br>
        Population: ~${city.pop.toLocaleString()}<br>
        Category: ${city.category}<br>
        Coordinates: ${city.lat.toFixed(2)}°N, ${city.lng.toFixed(2)}°E
    `).addTo(cityLayer);
});

// Add scale bar (built-in Leaflet control)
L.control.scale({
    position: 'bottomleft',
    maxWidth: 200,
    metric: true,
    imperial: true
}).addTo(map);

// Custom Legend control
var legend = L.control({position: 'bottomright'});
legend.onAdd = function(map) {
    var div = L.DomUtil.create('div', 'info legend');
    div.style.background = 'rgba(255,255,255,0.9)';
    div.style.padding = '10px';
    div.style.borderRadius = '4px';
    div.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)';
    div.innerHTML = `
        <b>City Categories</b><br>
        <i style="background:#e41a1c; width:12px; height:12px; display:inline-block;"></i> Asia<br>
        <i style="background:#377eb8; width:12px; height:12px; display:inline-block;"></i> Americas<br>
        <i style="background:#4daf4a; width:12px; height:12px; display:inline-block;"></i> Africa<br>
        <small>Circle size ≈ population</small>
    `;
    return div;
};
legend.addTo(map);

// Layer control (for toggling basemap or overlays if extended)
L.control.layers(null, {
    "Major Cities": cityLayer
}, {position: 'topright'}).addTo(map);

</script>
</body>
</html>