Skip to content

Instantly share code, notes, and snippets.

@JoeBlakeB
Last active October 20, 2025 23:42
Show Gist options
  • Select an option

  • Save JoeBlakeB/61e4c859f073319926944b638c0961bc to your computer and use it in GitHub Desktop.

Select an option

Save JoeBlakeB/61e4c859f073319926944b638c0961bc to your computer and use it in GitHub Desktop.
Download maps from mapgenie.io
#!/usr/bin/env python
#
# MapGenieDownloader.py - v2.1
#
# Copyright (C) 2022 Joe Baker (JoeBlakeB)
# This program is free software under the GPLv3 license.
#
# Download a high resolution maps from mapgenie.io
# May not work on all games, only tested with tarkov and forza
#
# Requires: python3 and pillow
# Optional: ImageMagick
#
# Usage:
# ./map.py {game} {map} {scale (8-16)}
#
# Examples:
# ./map.py tarkov woods 10
# ./map.py forza-horizon-5 mexico/summer-roads 12
#
import datetime
import os
from PIL import Image
import requests
import subprocess
import sys
import time
if len(sys.argv) < 4:
print("./map.py {game} {map} {scale (8-16)}")
game, map, scale = sys.argv[-3:]
if "/" in map:
mapUrl = map
map = map.replace("/", "-")
else:
mapUrl = map + "/default"
size = 2**(int(scale)-8)
offset = (2 ** (int(scale)-1)) - size
url = "https://tiles.mapgenie.io/games/"+game+"/"+mapUrl+"-v{0}/"+scale+"/{1}/{2}.{3}"
# Get latest map version and filetype
print(f"Finding Map...", end="\r")
retries = 32
a = 0
version = False
while True:
for b in ["jpg", "png"]:
l = offset + int(size/2)
r = requests.head(url.format(a, l, l, b))
if r.status_code == 200:
version = a
fileType = b
retries = 8
break
if retries <= 0:
break
else:
retries -= 1
a += 1
if not version:
print("Could not find map.")
exit()
url = url.format(version, "{0}", "{1}", fileType)
print(f"Found map: v{version} - {fileType}")
# Download image files
os.makedirs(f"{game}-{map}", exist_ok=True)
start = time.time()
for x in range(offset, offset + size):
for y in range(offset, offset + size):
running = time.time() - start
count = ((x - offset) * size) + (y - offset) + 1
total = (size * size)
eta = running/(count/total) - running
if os.path.isfile(f"{game}-{map}/{x}-{y}.png"):
print("Checking: " + str(count) + "/" + str(total) + " -",
datetime.timedelta(seconds=int(running)), "-",
datetime.timedelta(seconds=int(eta)), "remaining",
end="\r")
continue
retries = 0
print("Downloading: " + str(count) + "/" + str(total) + " -",
datetime.timedelta(seconds=int(running)), "-",
datetime.timedelta(seconds=int(eta)), "remaining",
end="\r")
image = url.format(x, y)
r = requests.get(image)
if r.status_code != 200:
sys.stdout.write("\033[K")
print(f"Could not download {x}-{y}.png, status {r.status_code}")
print("Sleeping: " + str(count) + "/" + str(total) + " -",
datetime.timedelta(seconds=int(running)), "-",
datetime.timedelta(seconds=int(eta)), "remaining",
end="\r")
if not r.status_code in [403, 404]:
time.sleep(10)
continue
file = open(f"{game}-{map}/{x}-{y}.png", "wb")
file.write(r.content)
file.close()
sys.stdout.write("\033[K")
print(f"Downloading: {total}/{total} -", datetime.timedelta(seconds=int(running)))
# Combine them into a single image
imageSize = 256
output = Image.new({"png":"RGBA", "jpg":"RGB"}[fileType], (size*imageSize, size*imageSize))
for x in range(0, size):
print(f"Combining: {int(((x/size)*100))}%", end="\r")
for y in range(0, size):
try:
image = Image.open(f"{game}-{map}/{offset+x}-{offset+y}.png")
output.paste(image, (x*imageSize, y*imageSize))
except:
if os.path.isfile(f"{game}-{map}/{offset+x}-{offset+y}.png"):
print(f"Error: Could not add \n{offset+y}-{offset+x}.png")
print("Combining: 100% - Saving...", end="\r")
# Save image, trim output if imagemagick is installed
try:
imagemagick = "ImageMagick" in str(subprocess.check_output(["convert"]))
except:
imagemagick = False
uncroppedFile = f"{game}-{map}/uncropped-{scale}.{fileType}"
outputFile = f"{game}-{map}-{scale}.{fileType}"
if imagemagick:
output.save(uncroppedFile)
subprocess.check_output(["convert", "-trim", uncroppedFile, outputFile])
else:
output.save(outputFile)
sys.stdout.write("\033[K")
print("Combining: 100% - Saved")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment