Last active
February 25, 2023 07:17
-
-
Save FelixWolf/4ecf188c18fee7c3dbe7ba82926ba95b to your computer and use it in GitHub Desktop.
This script will take a while to run because it has to download stuff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
#Mainland(Minus Zindra): 897 982 1130 1190 | |
#Corsica: 1082 1101 1100 1158 | |
#Gaeta 1: 1112 1140 1130 1155 | |
#Gaeta 5: 1081 1160 1100 1190 | |
#Nautilus: 1050 1108 1080 1137 | |
#Satori: 1005 1099 1047 1134 | |
#Heterocera: 1013 991 1036 1014 | |
#Sansara: 974 982 1012 1038 | |
#Bellisseria: 954 1024 977 1045 | |
#Jeogeot: 897 1004 939 1036 | |
#Sharp: 988 1159 1002 1179 | |
#Blake Sea: 1036 1136 1061 1157 | |
#Mainland(Zindra + Horizons): 1179 1797 1210 1821 | |
#Zindra: 1179 1797 1200 1821 | |
#Horizons: 1201 1804 1210 1813 | |
#Premium: 500 1000 926 1215 | |
#Premium East: 907 1192 926 1215 | |
#Premium South 1: 811 1013 828 1028 | |
#Premium South 2: 750 1000 768 1022 | |
#Premium South 3: 500 1000 518 1022 | |
import argparse | |
from PIL import Image | |
import tempfile | |
import os | |
import threading | |
import urllib.request | |
import urllib.error | |
import struct | |
import time | |
import math | |
cachedir = os.path.join(tempfile.gettempdir(), "slmapcache") | |
mappath = "https://s3.amazonaws.com/map.secondlife.com/map-1-{x}-{y}-objects.jpg" | |
starttime = round(time.time()) | |
yesterday = starttime - (60 * 60 * 24) | |
BOTTOM = 0 | |
LEFT = 1 | |
TOP = 2 | |
RIGHT = 3 | |
def threadedDownload(path, saveFunction, *args, **kwargs): | |
def thread(): | |
try: | |
with urllib.request.urlopen(path) as req: | |
saveFunction(req, *args, **kwargs) | |
except urllib.error.HTTPError as req: | |
saveFunction(req, *args, **kwargs) | |
x = threading.Thread(target=thread) | |
x.start() | |
return x | |
def printProgressBar(value, length=40, title = " ", vmin=0.0, vmax=1.0): | |
""" | |
Text progress bar | |
Parameters | |
---------- | |
value : float | |
Current value to be displayed as progress | |
vmin : float | |
Minimum value | |
vmax : float | |
Maximum value | |
length: int | |
Bar length (in character) | |
title: string | |
Text to be prepend to the bar | |
""" | |
# Block progression is 1/8 | |
blocks = ["", "▏","▎","▍","▌","▋","▊","▉","█"] | |
vmin = vmin or 0.0 | |
vmax = vmax or 1.0 | |
lsep, rsep = "▏", "▕" | |
# Normalize value | |
value = min(max(value, vmin), vmax) | |
value = (value-vmin)/float(vmax-vmin) | |
v = value*length | |
x = math.floor(v) # integer part | |
y = v - x # fractional part | |
base = 0.125 # 0.125 = 1/8 | |
prec = 3 | |
i = int(round(base*math.floor(float(y)/base),prec)/base) | |
bar = "█"*x + blocks[i] | |
n = length-len(bar) | |
bar = lsep + bar + " "*n + rsep | |
print("\r" + title + bar + " %.1f%%" % (value*100), end = "\r") | |
# Print New Line on Complete | |
if value == 100: | |
print() | |
def processProgress(i, t, title=""): | |
if i == t or i > t: | |
printProgressBar(i/t, title=title) | |
elif i % round(t/1000) == 0: | |
printProgressBar(i/t, title=title) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='Process some integers.') | |
parser.add_argument('-b', '--bounds', nargs=4, type=int, default=(897, 982, 1130, 1190), | |
metavar=("bottom", "left", "top", "right"), | |
help='Bounds to render/download') | |
parser.add_argument('-r', '--resolution', type=int, default = 8, | |
help='Resolution of each tile') | |
parser.add_argument('-n', '--nocache', action='store_true', | |
help='Ignore cache') | |
parser.add_argument('-t', '--threads', type=int, default = 32, | |
help='Download threads') | |
parser.add_argument('file', help='Output file') | |
args = parser.parse_args() | |
bounds = args.bounds | |
if not os.path.exists(cachedir): | |
os.makedirs(cachedir) | |
totalTiles = (bounds[TOP] - bounds[BOTTOM] + 1) * (bounds[RIGHT] - bounds[LEFT] + 1) | |
imageSize = ( | |
(bounds[RIGHT] - bounds[LEFT] + 1) * args.resolution, | |
(bounds[TOP] - bounds[BOTTOM] + 1) * args.resolution | |
) | |
print("Need {} tiles".format(totalTiles)) | |
print("Destionation Resolution {}x{}".format(*imageSize)) | |
maxThreads = args.threads | |
threadList = [None]*maxThreads | |
downloaded = 0 | |
for y in range(bounds[BOTTOM], bounds[TOP]+2): | |
for x in range(bounds[LEFT], bounds[RIGHT]+2): | |
file = os.path.join(cachedir, "map-{x}-{y}.jpg".format(x=x, y=y)) | |
if not args.nocache: | |
if os.path.isfile(file): | |
stat = os.stat(file) | |
if stat.st_mtime > yesterday: | |
continue | |
def saveFunction(req, file): | |
with open(file, "wb") as f: | |
if req.code == 200: | |
f.write(req.read()) | |
else: | |
f.write(b"") | |
thread = None | |
while not thread: | |
for i in range(0, maxThreads): | |
if threadList[i] == None or not threadList[i].is_alive(): | |
thread = threadedDownload(mappath.format(x=x, y=y), saveFunction, file) | |
threadList[i] = thread | |
break | |
downloaded += 1 | |
processProgress(downloaded, totalTiles) | |
print("Download complete") | |
im = Image.new("RGB", imageSize, color=0x5f471d) | |
top = bounds[TOP] - bounds[BOTTOM] | |
for y in range(bounds[BOTTOM], bounds[TOP]+1): | |
for x in range(bounds[LEFT], bounds[RIGHT]+1): | |
file = os.path.join(cachedir, "map-{x}-{y}.jpg".format(x=x, y=y)) | |
with open(file, "rb") as f: | |
f.seek(0, 2) | |
if f.tell() == 0: | |
continue | |
f.seek(0, 0) | |
tile = Image.open(f)\ | |
.resize((args.resolution, args.resolution)) | |
im.paste(tile, ( | |
args.resolution * (x - bounds[LEFT]), | |
args.resolution * (top - (y - bounds[BOTTOM])) | |
)) | |
im.save(args.file) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment