Skip to content

Table of Contents

URLs from Julia Mapping: A Practical Guide

Do It Yourself Bullseye Maps

Bullseye maps are useful for orientation at the regional level. These are easy to assemble through the facilities of the Open Maps Project using the Leaflet API. Most of what this function does is to output HTML code with Javascript, which does the work. The HTML output is panable and zoomable. To create your own, just specify the city to center and the coordinates.

using Printf

"""
    dms_to_decimal(coords::AbstractString) -> String

Converts DMS (Degrees, Minutes, Seconds) or decimal format to decimal degrees.
Handles various symbols: °, ′, ', ″, '', ".
"""
function dms_to_decimal(coord::AbstractString)
    # 1. Clean up and check if it's already decimal (e.g., "41.8, -87.6")
    # This regex is now more flexible with spaces and signs
    clean_coord = replace(coord, " " => "")
    if occursin(r"^-?\d+(\.\d+)?,-?\d+(\.\d+)?$", clean_coord)
        parts = split(clean_coord, ",")
        return "$(parts[1]), $(parts[2])"
    end

    # 2. Handle DMS format
    parts = split(coord, ",")
    if length(parts) != 2
        throw(ArgumentError("Expected 'Lat, Lon' format. Received: $coord"))
    end
    
    lat_dms = strip(parts[1])
    lon_dms = strip(parts[2])

    function to_decimal(dms::AbstractString)
        # Normalize symbols
        dms = replace(dms, "''" => "″", "\"" => "″", "'" => "′")
        
        # Match components: Degrees, Minutes, Seconds, and Direction
        # Added 'i' for case-insensitive NSEW
        m = match(r"(\d+)\s*°?\s*(\d+)\s*′?\s*(\d+(?:\.\d+)?)\s*″?\s*([NSEW])"i, dms)
        
        if m === nothing
            throw(ArgumentError("Invalid DMS format: $dms"))
        end

        deg = parse(Float64, m.captures[1])
        min = parse(Float64, m.captures[2])
        sec = parse(Float64, m.captures[3])
        dir = uppercase(m.captures[4])

        decimal = deg + (min / 60) + (sec / 3600)
        if dir == "S" || dir == "W"
            decimal *= -1
        end
        return decimal
    end

    return "$(to_decimal(lat_dms)), $(to_decimal(lon_dms))"
end

"""
    create_bullseye_map(capital_name, capital_coords; file_path, bands, color_scheme)

Creates a Leaflet HTML map with auto-calculating zoom and distance bands.
"""
function create_bullseye_map(capital_name::String, capital_coords::String,
    file_path::String="bullseye.html",
    bands::String="50, 100, 200",
    color_scheme::Int=4)

    # Color Palettes
    pal = (
        "'Red', 'Green', 'Yellow', 'Blue', 'Purple'",
        "'#E74C3C', '#2ECC71', '#3498DB', '#F1C40F', '#9B59B6'",
        "'#FF4136', '#2ECC40', '#0074D9', '#FFDC00', '#B10DC9'",
        "'#D32F2F', '#388E3C', '#1976D2', '#FBC02D', '#7B1FA2'",
        "'#FF5733', '#C70039', '#900C3F', '#581845', '#FFC300'"
    )

    # 1. Coordinate Conversion
    centerpoint = dms_to_decimal(capital_coords)
    band_colors = pal[color_scheme]
    
    # 2. Auto-Zoom Calculation
    # Formula: 12 - log2(distance). Clamp between 1 (world) and 18 (street).
    band_vals = parse.(Float64, split(bands, ","))
    max_dist = maximum(band_vals)
    auto_zoom = clamp(floor(Int, 12 - log2(max_dist)), 1, 18)

    # 3. HTML Template
    bullseye_html = """
    <!DOCTYPE html>
    <html>
    <head>
      <title>Bullseye Map - $capital_name</title>
      <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
      <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
      <style>
        body, html { margin: 0; padding: 0; width: 100%; height: 100%; }
        #map { width: 100%; height: 100vh; }
        .legend {
            padding: 10px; background: white; border-radius: 5px;
            box-shadow: 0 0 15px rgba(0,0,0,0.2); line-height: 1.5;
            font-family: sans-serif;
        }
        .legend i { width: 18px; height: 18px; float: left; margin-right: 8px; opacity: 0.7; }
      </style>
    </head>
    <body>
    <div id="map"></div>
    <script>
    var map = L.map('map').setView([$centerpoint], $auto_zoom);

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

    L.marker([$centerpoint]).addTo(map)
        .bindPopup('<b>$capital_name</b><br>Centerpoint')
        .openPopup();

    var colors = [$band_colors];
    var radii = [$bands]; 

    radii.forEach(function(radius, index) {
        L.circle([$centerpoint], {
            radius: radius * 1609.34, // Convert Miles to Meters
            color: colors[index % colors.length],
            weight: 2,
            fillColor: colors[index % colors.length],
            fillOpacity: 0.1,
            interactive: false
        }).addTo(map);
    });

    var legend = L.control({position: 'bottomleft'});
    legend.onAdd = function (map) {
        var div = L.DomUtil.create('div', 'legend');
        div.innerHTML = '<strong>Distance (Miles)</strong><br>';
        radii.forEach(function(radius, i) {
            div.innerHTML += '<i style="background:' + colors[i % colors.length] + '"></i> ' + radius + ' mi<br>';
        });
        return div;
    };
    legend.addTo(map);

    window.addEventListener('resize', function() { map.invalidateSize(); });
    </script>
    </body>
    </html>
    """

    open(file_path, "w") do f
        write(f, bullseye_html)
    end

    println("Map generated successfully: \$file_path (Zoom Level: \$auto_zoom)")
    return file_path
end

# Execution
create_bullseye_map("Chicago", "41° 52' 54.5952'' N, 87° 37' 23.4372'' W")

Latest

Excel to Julia: The Rosetta Stone

For when your spreadsheet starts to crawl, but you still need to get the job done. 1. The Basics: Data as a Thing In Excel, the data and the logic live in the same cell. In Julia, we keep them separate for speed and sanity. * Workbook/Sheet ≅ DataFrame * Column ≅:Symbol

Members Public

Using the forum at Julialang.org

The Julia language organization maintains an open, free forum where you can post questions to the helpful users under the New to Julia category. Especially if your question involves advanced scientific capabilities of the language, such as tensors, it will be invaluable. On the other hand … If you are an

Members Public

Age pyramids

The age pyramid is a useful tool in demographic analysis. It visualizes the relative proportion of population by sex and age cohort. The shape of the curve can indicate an excess of deaths over births and give an idea of the sufficiency of the working age population to support the

Members Public