How to Generate SVG Country Maps in Python

Learn how to use the GADM API to make SVG maps of all world countries in Python.
  · 6 min read · Updated aug 2022 · General Python Tutorials

Unlock the secrets of your code with our AI-powered Code Explainer. Take a look!

In this tutorial, we will use the GADM country API to generate SVG Files for each country. We will make it, so the country fits perfectly in the SVG tag. For this, we have to analyze the data. We will also use the pycountry module to get all three letter country codes. Let us get right into it!

Installing pycountry:

$ pip install pycountry

Importing the libraries:

# Default Library
import requests
import json
import os

# Download with pip install pycountry
import pycountry

Setup

All the rest of the code is inside a for loop because we will download every country. To do this, we convert the countries dictionary to a list and loop over it. The country object will hold a Country object that has the three-letter code as a property.:

for country in list(pycountry.countries):

Then we define four variables. The first one will hold all coordinates of the current country's border. The second one will hold the coordinates as they are grouped in the API. This is needed because most countries consist of multiple nonconnected parts. The last two hold the three-letter country code and the country name from the country object:

    # All Points from all Groups
    # used to analyze
    allPoints = []

    # Countries that dont consist of one body 
    # will have multiple groups of coordinates
    pointGroups = []

    # Country Code with 3 letters
    countryCode = country.alpha_3
    countryName = country.name

Before we get deeper into the loop, we check if we have already generated an SVG map for this country. We will store the SVG in an output folder, and the file's name will simply be the country's name. So we concatenate a string with this information and give it to the os.path.exists() function. If the file exists, we skip this iteration with the continue statement.

    # Check if the SVG file already Exists and skip if it does
    if os.path.exists(f'output/{countryName}.svg'):
        print(f'{countryName}.svg Already exists ... Skipping to next Country\n')
        continue

    print('Generating Map for: ', countryName)

Requesting Data

Now we request the data. The data is just a .json file residing on a web server. The URL for the swiss data looks like this. For other countries, we have to simply swap the CHE with another country's code:

https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_CHE_0.json

So we insert this country code in this URL and use it in the get() function from requests. After that, we try to decode the returned text. If that does not work, we may have made an invalid request. If that's the case, we skip the iteration.

    # Get the Data
    re = requests.get(f'https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_{countryCode}_0.json')

    # If the string cant be parsed an invalid country was requested
    try:
        data = json.loads(re.text)
    except json.decoder.JSONDecodeError:
        print('Could not decode ... Skipping to next Country\n')
        continue

Organizing Data

Now we need to organize the data for later usage. For this, we loop over the data until we encounter the groups; we append these to the list we defined earlier. Then we also loop through this group and append the points to the allPoints list:

    # Organise the Data 
    # Get the groups and all coordinates
    for i in data['features'][0]['geometry']['coordinates']:
        for group in i:
            pointGroups.append(group)
            for coord in group:
                allPoints.append(coord)

    print(f'\n{len(allPoints)} Points')

Analyzing Data

After that, we analyze the data. We want to find out the lowest and highest points for each axis because we want to make it so the path we later generate fits ideally into the SVG file. So we define variables for each of those four points:

    # Analyse Data
    # Use these Information to calculate 
    # offset, height and width of the Country
    lowestX = 9999999999
    highestX = -9999999999

    lowestY = 9999999999
    highestY = -9999999999

Then we loop over the allPoints list and set each value accordingly. We do this with a ternary operator:

    for x, y in allPoints:
        lowestX = x if x < lowestX else lowestX
        highestX = x if x > highestX else highestX

        lowestY = y if y < lowestY else lowestY
        highestY = y if y > highestY else highestY

We then display some debug information:

    print('lowestX', lowestX)
    print('highestX', highestX)

    print('lowestY', lowestY)
    print('highestY', highestY)

Then we can calculate the height and width of the SVG, or there I say the country with the info we just got.

    svgWidth = (highestX - lowestX)
    svgHeight = (highestY - lowestY)

Displaying the Data

Let us display the country with SVG. We will use the polygon element. Some HTML knowledge would be good for the following part because SVG is just HTML.

So we define a polygon variable that will hold the polygon element strings. Then we loop over the groups and define a coordinateString which will keep the current coordinates. We then also loop over this group and unpack it.

We subtract the lowest of each axis, so the country sticks to the edges. Then we add the pair to the coordinate string. After we have looped over the group, we make a polygon string with the coordinate string inserted at the correct location.

    # Transfrom Points to Polygon Strings
    polygonString = ''
    for group in pointGroups:
        coordinateString = ''
        for x, y in group:
            x  = (x - lowestX)
            y  = (y - lowestY)

            coordinateString = coordinateString + f'{x},{y} '

        polygonString += f'<polygon points="{coordinateString}"></polygon>'

Then we insert all the polygons into an SVG string with the correct settings:

    svgContent = f"""
    <svg width="{svgWidth}" height="{svgHeight}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="transform: scale(1, -1)">
        {polygonString}
    </svg>
    """

Last but not least, we write this string to a file named after the country:

    # make the output folder
    if not os.path.isdir("output"):
        os.mkdir("output")
    # write the svg file
    with open(f'output/{countryName}.svg', 'w') as f:
        f.write(svgContent)
    # new line
    print('\n')

Conclusion

This program will try to make country border SVG files of all world countries and will store them in the output folder.

Note that small countries will look tiny compared to larger ones, make sure to scale them if you wish to use any of them.

Learn also: How to Get Geolocation in Python.

Happy coding ♥

Want to code smarter? Our Python Code Assistant is waiting to help you. Try it now!

View Full Code Explain The Code for Me
Sharing is caring!



Read Also



Comment panel

    Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!