diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..d1eb644 --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +ignore = E203, W503 +# line length is intentionally set to 80 here because black uses Bugbear +# See https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length for more details +max-line-length = 88 \ No newline at end of file diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..9a5dad2 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,30 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions +# Reduced subset of file from https://github.com/actions/starter-workflows/blob/main/ci/python-app.yml + +name: Python application + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Check the black styling + run: | + make style-check + - name: Lint with flake8 + run: | + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + make lint --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85728f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,161 @@ +# Original file is from https://github.com/github/gitignore/blob/main/Python.gitignore +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a06657e --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +# Check if black formatter will work without any rewrites, and produces an exit code +style-check: + black src/ --check + +# This will reformat all python files unders bookdifferent/ into the python black standard +style: + black src/ + +# Checks that the python source files are compliant regarding errors and style conventions +lint: + flake8 src/ \ No newline at end of file diff --git a/arnis.py b/arnis.py index 0b42d64..0a3658f 100644 --- a/arnis.py +++ b/arnis.py @@ -4,774 +4,7 @@ # MIT License # Please see the LICENSE file that should have been included as part of this package. -import os, sys, time, gc, requests, cv2, argparse, anvil -from random import choice, randint -from math import floor -import numpy as np -import matplotlib.path as mplPath -from polylabel import polylabel +from src.main import run -parser = argparse.ArgumentParser(description='Arnis - Generate cities from real life in Minecraft using Python') -parser.add_argument("--city", dest="city", help="Name of the city") -parser.add_argument("--state", dest="state", help="Name of the state") -parser.add_argument("--country", dest="country", help="Name of the country") -parser.add_argument("--path", dest="path", help="Path to the minecraft world") -parser.add_argument("--debug", dest="debug", default=False, action="store_true", help="Enable debug mode") -args = parser.parse_args() -if (args.city is None or args.state is None or args.country is None or args.path is None): - print("Error! Missing arguments") - os._exit(1) - -gc.collect() -np.seterr(all='raise') -np.set_printoptions(threshold=sys.maxsize) - -def floodFill(img, px, py, newColor, currentBuilding, elementType="None"): - startTimeFloodfill = time.time() - currentBuilding = np.delete(currentBuilding, 0, axis=0) - if (len(currentBuilding) <= 2): return img - if not (mplPath.Path(currentBuilding).contains_point((py, px))): - centroid = polylabel([currentBuilding.tolist()], with_distance=True) - px = round(centroid[0][1]) - py = round(centroid[0][0]) - if not (mplPath.Path(currentBuilding).contains_point((py, px))): - if (mplPath.Path(currentBuilding).contains_point((py - 5, px))): - py -= 5 - elif (mplPath.Path(currentBuilding).contains_point((py + 5, px))): - py += 5 - elif (mplPath.Path(currentBuilding).contains_point((py, px - 5))): - px -= 5 - elif (mplPath.Path(currentBuilding).contains_point((py, px + 5))): - px += 5 - else: return img - - if (str(img[px][py][0])[:1] == "5" or str(img[px][py][0])[:1] == "6"): - if (mplPath.Path(currentBuilding).contains_point((py - 1, px))): - py -= 1 - elif (mplPath.Path(currentBuilding).contains_point((py + 1, px))): - py += 1 - elif (mplPath.Path(currentBuilding).contains_point((py, px - 1))): - px -= 1 - elif (mplPath.Path(currentBuilding).contains_point((py, px + 1))): - px += 1 - else: return img - - try: oldColor = img[px][py][0] - except Exception: return img - queue = [(px, py)] - seen = set() - tot_rows = img.shape[0] - tot_cols = img.shape[1] - queueLen = 0 - while queue: - nxt = [] - for x, y in queue: - if (img[x][y] == newColor): - continue - if not (mplPath.Path(currentBuilding).contains_point((y, x))): - return img - img[x][y] = newColor - seen.add((x, y)) - - if x and (x - 1, y) not in seen and (img[x - 1][y] == oldColor or (elementType == "building" and str(img[x - 1][y][0])[:1] == "1")): - nxt.append((x - 1, y)) - if y and (x, y - 1) not in seen and (img[x][y - 1] == oldColor or (elementType == "building" and str(img[x][y - 1][0])[:1] == "1")): - nxt.append((x, y - 1)) - if x < tot_rows - 1 and (x + 1, y) not in seen and (img[x + 1][y] == oldColor or (elementType == "building" and str(img[x + 1][y][0])[:1] == "1")): - nxt.append((x + 1, y)) - if y < tot_cols - 1 and (x, y + 1) not in seen and (img[x][y + 1] == oldColor or (elementType == "building" and str(img[x][y + 1][0])[:1] == "1")): - nxt.append((x, y + 1)) - - if (time.time() - startTimeFloodfill > 7 or (elementType == "tree_row" and time.time() - startTimeFloodfill > 0.3)): # Timeout (known issue, see Github readme) - return img - - queue = nxt - if (len(nxt) > queueLen): - queueLen = len(nxt) - - return img - -def bresenham(x1, y1, x2, y2): #Bresenham Line Algorithm Credit: encukou/bresenham@Github - dx = x2 - x1 - dy = y2 - y1 - - xsign = 1 if dx > 0 else -1 - ysign = 1 if dy > 0 else -1 - - dx = abs(dx) - dy = abs(dy) - - if dx > dy: - xx, xy, yx, yy = xsign, 0, 0, ysign - else: - dx, dy = dy, dx - xx, xy, yx, yy = 0, ysign, xsign, 0 - - D = 2*dy - dx - y = 0 - - for x in range(dx + 1): - yield x1 + x*xx + y*yx, y1 + x*xy + y*yy - if D >= 0: - y += 1 - D -= 2*dx - D += 2*dy - -def getData(city, state, country): - print("Fetching data...") - api_servers = ['https://overpass-api.de/api/interpreter', 'https://lz4.overpass-api.de/api/interpreter', 'https://z.overpass-api.de/api/interpreter', 'https://maps.mail.ru/osm/tools/overpass/api/interpreter', 'https://overpass.openstreetmap.ru/api/interpreter', 'https://overpass.kumi.systems/api/interpreter'] - url = choice(api_servers) - query1 = f""" - [out:json]; - area[name=""" + '"' + city.replace(" ", "-") + '"' + """]->.city; - area[name=""" + '"' + state.replace(" ", "-") + '"' + """]->.state; - area[name=""" + '"' + country.replace(" ", "-") + '"' + """]->.country; - way(area.country)(area.state)(area.city)[!power][!place][!ferry]; - (._;>;); - out; - """ - - print("Chosen server: " + url) - try: - data = requests.get(url, params={'data': query1}).json() - - if (len(data['elements']) == 0): - print("Error! No data available") - os._exit(1) - except Exception as e: - if "The server is probably too busy to handle your request." in str(e): - print("Error! OSM server overloaded") - elif "Dispatcher_Client::request_read_and_idx::rate_limited" in str(e): - print("Error! IP rate limited") - else: - print("Error! " + str(e)) - os._exit(1) - - if (args.debug): - with open('arnis-debug-raw_data.json', 'w', encoding="utf-8") as f: - f.write(str(data)) - return data - -def processData(data): - print("Parsing data...") - resDownScaler = 100 - processingStartTime = time.time() - - greatestElementX = 0 - greatestElementY = 0 - for element in data['elements']: - if (element['type'] == 'node'): - element['lat'] = int(str(element['lat']).replace('.', '')) - element['lon'] = int(str(element['lon']).replace('.', '')) - - if (element['lat'] > greatestElementX): - greatestElementX = element['lat'] - if (element['lon'] > greatestElementY): - greatestElementY = element['lon'] - - for element in data['elements']: - if (element['type'] == 'node'): - if (len(str(element['lat'])) != len(str(greatestElementX))): - for i in range(0, len(str(greatestElementX)) - len(str(element['lat']))): - element['lat'] *= 10 - - if (len(str(element['lon'])) != len(str(greatestElementY))): - for i in range(0, len(str(greatestElementY)) - len(str(element['lon']))): - element['lon'] *= 10 - - lowestElementX = greatestElementX - lowestElementY = greatestElementY - for element in data['elements']: - if (element['type'] == 'node'): - if (element['lat'] < lowestElementX): - lowestElementX = element['lat'] - if (element['lon'] < lowestElementY): - lowestElementY = element['lon'] - - nodesDict = {} - for element in data['elements']: - if (element['type'] == 'node'): - element['lat'] -= lowestElementX - element['lon'] -= lowestElementY - nodesDict[element['id']] = [element['lat'], element['lon']] - - - img = np.zeros((round((greatestElementY - lowestElementY) / resDownScaler) + 5, round((greatestElementX - lowestElementX) / resDownScaler) + 5, 1), np.uint8) - img.fill(0) - imgLanduse = img.copy() - origImgSize = img.shape[0] * img.shape[1] - - orig_posDeterminationCoordX = 0 - orig_posDeterminationCoordY = 0 - map_posDeterminationCoordX = 0 - map_posDeterminationCoordY = 0 - nodeIndexList = [] - for i, element in enumerate(data['elements']): - if (element['type'] == 'way'): - for j, node in enumerate(element['nodes']): - element['nodes'][j] = nodesDict[node] - - if ("tags" in element and "building" in element["tags"] and orig_posDeterminationCoordX == 0): - orig_posDeterminationCoordX = element['nodes'][0][0] - orig_posDeterminationCoordY = element['nodes'][0][1] - map_posDeterminationCoordX = round(element['nodes'][0][0] / resDownScaler) - map_posDeterminationCoordY = round(element['nodes'][0][1] / resDownScaler) - elif (element['type'] == 'node'): - nodeIndexList.append(i) - - for i in reversed(nodeIndexList): - del data['elements'][i] - - if (args.debug): - print("Biggest element X: " + str(greatestElementX)) - print("Biggest element Y: " + str(greatestElementY)) - print("Lowest element X: " + str(lowestElementX)) - print("Lowest element Y: " + str(lowestElementY)) - print("Original position determination reference coordinates: " + str(orig_posDeterminationCoordX) + ", " + str(orig_posDeterminationCoordY)) - print("Map position determination reference coordinates: " + str(map_posDeterminationCoordX) + ", " + str(map_posDeterminationCoordY)) - with open('arnis-debug-processed_data.json', 'w', encoding="utf-8") as f: - f.write(str(data)) - print("Processing data...") - - maxBuilding = (0, 0) - minBuilding = (greatestElementX, greatestElementY) - ElementIncr = 0 - ElementsLen = len(data['elements']) - lastProgressPercentage = 0 - for element in reversed(data['elements']): - progressPercentage = round(100 * (ElementIncr + 1) / ElementsLen) - if (progressPercentage % 10 == 0 and progressPercentage != lastProgressPercentage): - print("Element " + str(ElementIncr + 1) + "/" + str(ElementsLen) + " (" + str(progressPercentage) + "%)") - lastProgressPercentage = progressPercentage - - if (element['type'] == 'way' and "tags" in element): - if ("building" in element['tags']): - previousElement = (0, 0) - cornerAddup = (0, 0, 0) - currentBuilding = np.array([[0, 0]]) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - buildingHeight = 1 - - if (cordX > maxBuilding[0]): - maxBuilding = (cordX, maxBuilding[1]) - if (cordY > maxBuilding[1]): - maxBuilding = (maxBuilding[0], cordY) - - if (cordX < minBuilding[0]): - minBuilding = (cordX, minBuilding[1]) - if (cordY < minBuilding[1]): - minBuilding = (minBuilding[0], cordY) - - if (previousElement != (0, 0)): - if ("height" in element['tags']): - if (len(element['tags']['height']) >= 3): - buildingHeight = 9 - elif (len(element['tags']['height']) == 1): - buildingHeight = 2 - elif (element['tags']['height'][:1] == "1"): - buildingHeight = 3 - elif (element['tags']['height'][:1] == "2"): - buildingHeight = 6 - else: - buildingHeight = 9 - - if ("building:levels" in element['tags'] and int(element['tags']['building:levels']) <= 8 and int(element['tags']['building:levels']) >= 1): - buildingHeight = str(int(element['tags']['building:levels']) - 1) - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - if not (str(img[i[1]][i[0]][0])[:1] == "6" and img[i[1]][i[0]][0] > int("6" + str(buildingHeight))): - img[i[1]][i[0]] = int("6" + str(buildingHeight)) - - currentBuilding = np.append(currentBuilding, [[cordX, cordY]], axis=0) - if not (str(img[cordY][cordX][0])[:1] == "5" and img[cordY][cordX][0] > int("5" + str(buildingHeight))): - img[cordY][cordX] = int("5" + str(buildingHeight)) - - if not (str(img[previousElement[1]][previousElement[0]][0])[:1] == "5" and img[previousElement[1]][previousElement[0]][0] > int("5" + str(buildingHeight))): - img[previousElement[1]][previousElement[0]] = int("5" + str(buildingHeight)) - - cornerAddup = (cornerAddup[0] + cordX, cornerAddup[1] + cordY, cornerAddup[2] + 1) - previousElement = (cordX, cordY) - - if (cornerAddup != (0, 0, 0)): - img = floodFill(img, round(cornerAddup[1] / cornerAddup[2]), round(cornerAddup[0] / cornerAddup[2]), int("7" + str(buildingHeight)), currentBuilding, elementType="building") - - - elif ("highway" in element['tags']): - previousElement = (0, 0) - for coordinate in element['nodes']: - cordX = round(map_posDeterminationCoordX * coordinate[0] / orig_posDeterminationCoordX) - cordY = round(map_posDeterminationCoordY * coordinate[1] / orig_posDeterminationCoordY) - highwayType = 10 - if (previousElement != (0, 0) and element['tags']['highway'] != "corridor" and previousElement != (0, 0) and element['tags']['highway'] != "steps" and element['tags']['highway'] != "bridge"): - blockRange = 2 - highwayType = 10 - - if (element['tags']['highway'] == "path" or element['tags']['highway'] == "footway"): - blockRange = 1 - highwayType = 11 - elif (element['tags']['highway'] == "motorway"): - blockRange = 4 - elif (element['tags']['highway'] == "track"): - blockRange = 1 - highwayType = 12 - elif ("lanes" in element['tags'] and element['tags']['lanes'] != "1" and element['tags']['lanes'] != "2"): - blockRange = 4 - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - for x in range(i[0] - blockRange, i[0] + blockRange + 1): - for y in range(i[1] - blockRange, i[1] + blockRange + 1): - if (img[y][x] == 0): img[y][x] = highwayType - previousElement = (cordX, cordY) - - - elif ("landuse" in element['tags']): - previousElement = (0, 0) - cornerAddup = (0, 0, 0) - currentLanduse = np.array([[0, 0]]) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - landuseType = 39 - if (previousElement != (0, 0) and element['tags']['landuse'] != "industrial" and element['tags']['landuse'] != "residential"): - if (element['tags']['landuse'] == "greenfield" or element['tags']['landuse'] == "meadow" or element['tags']['landuse'] == "grass"): - landuseType = 30 - elif (element['tags']['landuse'] == "farmland"): - landuseType = 31 - elif (element['tags']['landuse'] == "forest"): - landuseType = 32 - elif (element['tags']['landuse'] == "cemetery"): - landuseType = 33 - elif (element['tags']['landuse'] == "beach"): - landuseType = 34 - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - if (imgLanduse[i[1]][i[0]] == 0): imgLanduse[i[1]][i[0]] = landuseType - - currentLanduse = np.append(currentLanduse, [[cordX, cordY]], axis=0) - cornerAddup = (cornerAddup[0] + cordX, cornerAddup[1] + cordY, cornerAddup[2] + 1) - previousElement = (cordX, cordY) - - if (cornerAddup != (0, 0, 0)): - imgLanduse = floodFill(imgLanduse, round(cornerAddup[1] / cornerAddup[2]), round(cornerAddup[0] / cornerAddup[2]), landuseType, currentLanduse) - - - elif ("natural" in element['tags']): - previousElement = (0, 0) - cornerAddup = (0, 0, 0) - currentNatural = np.array([[0, 0]]) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - naturalType = 39 - if (previousElement != (0, 0)): - if (element['tags']['natural'] == "scrub" or element['tags']['natural'] == "grassland"): - naturalType = 30 - elif (element['tags']['natural'] == "beach" or element['tags']['natural'] == "sand"): - naturalType = 34 - elif (element['tags']['natural'] == "wood" or element['tags']['natural'] == "tree_row"): - naturalType = 32 - elif (element['tags']['natural'] == "wetland"): - naturalType = 35 - elif (element['tags']['natural'] == "water"): - naturalType = 38 - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - if (imgLanduse[i[1]][i[0]] == 0): imgLanduse[i[1]][i[0]] = naturalType - - currentNatural = np.append(currentNatural, [[cordX, cordY]], axis=0) - cornerAddup = (cornerAddup[0] + cordX, cornerAddup[1] + cordY, cornerAddup[2] + 1) - previousElement = (cordX, cordY) - - if (cornerAddup != (0, 0, 0)): - if (naturalType != 32): - imgLanduse = floodFill(imgLanduse, round(cornerAddup[1] / cornerAddup[2]), round(cornerAddup[0] / cornerAddup[2]), naturalType, currentNatural) - else: - imgLanduse = floodFill(imgLanduse, round(cornerAddup[1] / cornerAddup[2]), round(cornerAddup[0] / cornerAddup[2]), naturalType, currentNatural, elementType="tree_row") - - - elif ("leisure" in element['tags']): - previousElement = (0, 0) - cornerAddup = (0, 0, 0) - currentLeisure = np.array([[0, 0]]) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - leisureType = 39 - if (previousElement != (0, 0) and element['tags']['leisure'] != "marina"): - if (element['tags']['leisure'] == "park" or element['tags']['leisure'] == "playground" or element['tags']['leisure'] == "garden"): - leisureType = 30 - elif (element['tags']['leisure'] == "pitch"): - leisureType = 36 - elif (element['tags']['leisure'] == "swimming_pool"): - leisureType = 37 - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - if (imgLanduse[i[1]][i[0]] == 0): imgLanduse[i[1]][i[0]] = leisureType - - currentLeisure = np.append(currentLeisure, [[cordX, cordY]], axis=0) - cornerAddup = (cornerAddup[0] + cordX, cornerAddup[1] + cordY, cornerAddup[2] + 1) - previousElement = (cordX, cordY) - - if (cornerAddup != (0, 0, 0)): - imgLanduse = floodFill(imgLanduse, round(cornerAddup[1] / cornerAddup[2]), round(cornerAddup[0] / cornerAddup[2]), leisureType, currentLeisure) - - - elif ("waterway" in element['tags']): - previousElement = (0, 0) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - - if (previousElement != (0, 0) and not ("layer" in element['tags'] and (element['tags']['layer'] == "-1" or element['tags']['layer'] == "-2" or element['tags']['layer'] != "-3"))): - waterwayWidth = 4 - if ("width" in element['tags']): - try: waterwayWidth = int(element['tags']['width']) - except Exception as e: waterwayWidth = int(float(element['tags']['width'])) - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - for x in range(round(i[0] - waterwayWidth / 2), round(i[0] + waterwayWidth + 1 / 2)): - for y in range(round(i[1] - waterwayWidth / 2), round(i[1] + waterwayWidth + 1 / 2)): - if (img[y][x] != 13): img[y][x] = 38 - previousElement = (cordX, cordY) - - - elif ("amenity" in element['tags']): - previousElement = (0, 0) - cornerAddup = (0, 0, 0) - currentAmenity = np.array([[0, 0]]) - amenityType = 20 - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - if (previousElement != (0, 0) and (element['tags']['amenity'] == "parking" or element['tags']['amenity'] == "fountain")): - if (element['tags']['amenity'] == "parking"): - amenityType = 20 - elif (element['tags']['amenity'] == "fountain"): - amenityType = 21 - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - if (imgLanduse[i[1]][i[0]] == 0): imgLanduse[i[1]][i[0]] = amenityType - - currentAmenity = np.append(currentAmenity, [[cordX, cordY]], axis=0) - cornerAddup = (cornerAddup[0] + cordX, cornerAddup[1] + cordY, cornerAddup[2] + 1) - previousElement = (cordX, cordY) - - if (amenityType == 21): amenityType = 37 - if (cornerAddup != (0, 0, 0)): - imgLanduse = floodFill(imgLanduse, round(cornerAddup[1] / cornerAddup[2]), round(cornerAddup[0] / cornerAddup[2]), amenityType, currentAmenity) - - - elif ("bridge" in element['tags']): - previousElement = (0, 0) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - - if (previousElement != (0, 0)): - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - img[i[1]][i[0]] = 13 - previousElement = (cordX, cordY) - - - elif ("railway" in element['tags']): - previousElement = (0, 0) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - - if (previousElement != (0, 0) and element['tags']['railway'] != "proposed"): - for i in bresenham(cordX - 2, cordY - 2, previousElement[0] - 2, previousElement[1] - 2): - img[i[1]][i[0]] = 14 - for i in bresenham(cordX + 1, cordY + 1, previousElement[0] + 1, previousElement[1] + 1): - img[i[1]][i[0]] = 14 - previousElement = (cordX, cordY) - - elif ("barrier" in element['tags']): - previousElement = (0, 0) - for coordinate in element['nodes']: - cordX = round(coordinate[0] / resDownScaler) - cordY = round(coordinate[1] / resDownScaler) - - if (previousElement != (0, 0)): - wallHeight = 1 - if ("height" in element['tags']): - wallHeight = round(int(float(element['tags']['height']))) - if (wallHeight > 3): wallHeight = 2 - - for i in bresenham(cordX, cordY, previousElement[0], previousElement[1]): - if (str(img[i[1]][i[0]][0])[:1] != 5 and str(img[i[1]][i[0]][0])[:1] != 6 and str(img[i[1]][i[0]][0])[:1] != 7): img[i[1]][i[0]] = int("2" + str((wallHeight + 1))) - previousElement = (cordX, cordY) - - ElementIncr += 1 - - - print("Optimizing data...") - - minBuilding = (minBuilding[0] - 50, minBuilding[1] - 50) - maxBuilding = (maxBuilding[0] + 50, maxBuilding[1] + 50) - img = img[minBuilding[1]:maxBuilding[1], minBuilding[0]:maxBuilding[0]] - imgLanduse = imgLanduse[minBuilding[1]:maxBuilding[1], minBuilding[0]:maxBuilding[0]] - print(str(100 - round(100 * (img.shape[0] * img.shape[1]) / origImgSize)) + "% size reduction") - for x in range(0, img.shape[0]): - for y in range(0, img.shape[1]): - if (imgLanduse[x][y] != 0 and img[x][y] == 0): - img[x][y] = imgLanduse[x][y] - - print("Processing finished in " + str(round(time.time() - processingStartTime, 2)) + " seconds (" + str(round((time.time() - processingStartTime) / 60, 2)) + " minutes)") - if (args.debug): cv2.imwrite('arnis-debug-map.png', img) - return np.flip(img, axis=1) - - -processStartTime = time.time() -air = anvil.Block('minecraft', 'air') -stone = anvil.Block('minecraft', 'stone') -grass_block = anvil.Block('minecraft', 'grass_block') -dirt = anvil.Block('minecraft', 'dirt') -sand = anvil.Block('minecraft', 'sand') -podzol = anvil.Block.from_numeric_id(3, 2) -grass = anvil.Block.from_numeric_id(175, 2) -farmland = anvil.Block('minecraft', 'farmland') -water = anvil.Block('minecraft', 'water') -wheat = anvil.Block('minecraft', 'wheat') -carrots = anvil.Block('minecraft', 'carrots') -potatoes = anvil.Block('minecraft', 'potatoes') -cobblestone = anvil.Block('minecraft', 'cobblestone') -iron_block = anvil.Block('minecraft', 'iron_block') -log = anvil.Block.from_numeric_id(17) -leaves = anvil.Block.from_numeric_id(18) -white_stained_glass = anvil.Block('minecraft', 'white_stained_glass') -dark_oak_door_lower = anvil.Block('minecraft', 'dark_oak_door', properties={'half': 'lower'}) -dark_oak_door_upper = anvil.Block('minecraft', 'dark_oak_door', properties={'half': 'upper'}) -cobblestone_wall = anvil.Block('minecraft', 'cobblestone_wall') -stone_brick_slab = anvil.Block.from_numeric_id(44, 5) -red_flower = anvil.Block.from_numeric_id(38) -white_concrete = anvil.Block('minecraft', 'white_concrete') -black_concrete = anvil.Block('minecraft', 'black_concrete') -gray_concrete = anvil.Block('minecraft', 'gray_concrete') -light_gray_concrete = anvil.Block('minecraft', 'light_gray_concrete') -green_stained_hardened_clay = anvil.Block.from_numeric_id(159, 5) -dirt = anvil.Block('minecraft', 'dirt') -glowstone = anvil.Block('minecraft', 'glowstone') -sponge = anvil.Block('minecraft', 'sponge') - -regions = {} -for x in range(0, 3): - for z in range(0, 3): - regions['r.' + str(x) + '.' + str(z)] = anvil.EmptyRegion(0, 0) - - -def setBlock(block, x, y, z): - flooredX = floor(x / 512) - flooredZ = floor(z / 512) - identifier = 'r.' + str(flooredX) + '.' + str(flooredZ) - if (identifier not in regions): - regions[identifier] = anvil.EmptyRegion(0, 0) - regions[identifier].set_block(block, x - flooredX * 512, y, z - flooredZ * 512) - -def fillBlocks(block, x1, y1, z1, x2, y2, z2): - for x in range(x1, x2 + 1): - for y in range(y1, y2 + 1): - for z in range(z1, z2 + 1): - if not (x == x2 + 1 or y == y2 + 1 or z == z2 + 1): - setBlock(block, x, y, z) - -mcWorldPath = args.path -if (mcWorldPath[-1] == '/'): mcWorldPath = mcWorldPath[:-1] -def saveRegion(region='all'): - if (region == 'all'): - for key in regions: - regions[key].save(mcWorldPath + '/region/' + key + '.mca') - print("Saved " + key) - else: - regions[region].save(mcWorldPath + '/region/' + region + '.mca') - print("Saved " + region) - -rawdata = getData(args.city, args.state, args.country) -imgarray = processData(rawdata) - - -print("Generating minecraft world...") - -x = 0 -z = 0 -doorIncrement = 0 -ElementIncr = 0 -ElementsLen = len(imgarray) -lastProgressPercentage = 0 -for i in imgarray: - progressPercentage = round(100 * (ElementIncr + 1) / ElementsLen) - if (progressPercentage % 10 == 0 and progressPercentage != lastProgressPercentage): - print("Pixel " + str(ElementIncr + 1) + "/" + str(ElementsLen) + " (" + str(progressPercentage) + "%)") - lastProgressPercentage = progressPercentage - - z = 0 - for j in i: - setBlock(dirt, x, 0, z) - if (j == 0): # Ground - setBlock(light_gray_concrete, x, 1, z) - elif (j == 10): # Street - setBlock(black_concrete, x, 1, z) - setBlock(air, x, 2, z) - elif (j == 11): # Footway - setBlock(gray_concrete, x, 1, z) - setBlock(air, x, 2, z) - elif (j == 12): # Natural path - setBlock(cobblestone, x, 1, z) - elif (j == 13): # Bridge - setBlock(light_gray_concrete, x, 2, z) - setBlock(light_gray_concrete, x - 1, 2, z - 1) - setBlock(light_gray_concrete, x + 1, 2, z - 1) - setBlock(light_gray_concrete, x + 1, 2, z + 1) - setBlock(light_gray_concrete, x - 1, 2, z + 1) - elif (j == 14): # Railway - setBlock(iron_block, x, 2, z) - elif (j == 20): # Parking - setBlock(gray_concrete, x, 1, z) - elif (j == 21): # Fountain border - setBlock(light_gray_concrete, x, 2, z) - setBlock(white_concrete, x, 1, z) - elif (j >= 22 and j <= 24): # Fence - if (str(j)[-1] == "2"): - setBlock(cobblestone_wall, x, 2, z) - else: - fillBlocks(cobblestone , x, 2, z, x, int(str(j[0])[-1]), z) - - setBlock(grass_block, x, 1, z) - elif (j == 30): # Meadow - setBlock(grass_block, x, 1, z) - randomChoice = randint(0, 2) - if (randomChoice == 0 or randomChoice == 1): - setBlock(grass, x, 2, z) - elif (j == 31): # Farmland - randomChoice = randint(0, 16) - if (randomChoice == 0): - setBlock(water, x, 1, z) - else: - setBlock(farmland, x, 1, z) - randomChoice = randint(0, 2) - if (randomChoice == 0): - setBlock(wheat, x, 2, z) - elif (randomChoice == 1): - setBlock(carrots, x, 2, z) - else: - setBlock(potatoes, x, 2, z) - elif (j == 32): # Forest - setBlock(grass_block, x, 1, z) - randomChoice = randint(0, 8) - if (randomChoice >= 0 and randomChoice <= 5): - setBlock(grass, x, 2, z) - elif (randomChoice == 6): - fillBlocks(log, x, 2, z, x, 8, z) - fillBlocks(leaves, x - 2, 5, z - 2, x + 2, 6, z + 2) - setBlock(air, x - 2, 6, z - 2) - setBlock(air, x - 2, 6, z + 2) - setBlock(air, x + 2, 6, z - 2) - setBlock(air, x + 2, 6, z + 2) - fillBlocks(leaves, x - 1, 7, z - 1, x + 1, 8, z + 1) - setBlock(air, x - 1, 8, z - 1) - setBlock(air, x - 1, 8, z + 1) - setBlock(air, x + 1, 8, z - 1) - setBlock(air, x + 1, 8, z + 1) - elif (j == 33): # Cemetery - setBlock(podzol, x, 1, z) - randomChoice = randint(0, 100) - if (randomChoice == 0): - setBlock(cobblestone, x - 1, 2, z) - setBlock(stone_brick_slab, x - 1, 3, z) - setBlock(stone_brick_slab, x, 2, z) - setBlock(stone_brick_slab, x + 1, 2, z) - elif (randomChoice == 1): - setBlock(cobblestone, x, 2, z - 1) - setBlock(stone_brick_slab, x, 3, z - 1) - setBlock(stone_brick_slab, x, 2, z) - setBlock(stone_brick_slab, x, 2, z + 1) - elif (randomChoice == 2 or randomChoice == 3): - setBlock(red_flower, x, 2, z) - elif (j == 34): # Beach - setBlock(sand, x, 1, z) - elif (j == 35): # Wetland - randomChoice = randint(0, 2) - if (randomChoice == 0): - setBlock(grass_block, x, 1, z) - else: - setBlock(water, x, 1, z) - elif (j == 36): # Pitch - setBlock(green_stained_hardened_clay, x, 1, z) - elif (j == 37): # Swimming pool - setBlock(water, x, 1, z) - setBlock(white_concrete, x, 0, z) - elif (j == 38): # Water - setBlock(water, x, 1, z) - elif (j == 39): # Raw grass - setBlock(grass_block, x, 1, z) - elif (j >= 50 and j <= 59): # House corner - building_height = 5 - if (j == 51): building_height = 8 - elif (j == 52): building_height = 11 - elif (j == 53): building_height = 14 - elif (j == 54): building_height = 17 - elif (j == 55): building_height = 20 - elif (j == 56): building_height = 23 - elif (j == 57): building_height = 26 - elif (j == 58): building_height = 29 - elif (j == 59): building_height = 32 - - fillBlocks(white_concrete, x, 1, z, x, building_height, z) - elif (j >= 60 and j <= 69): # House wall - building_height = 4 - if (j == 61): building_height = 7 - elif (j == 62): building_height = 10 - elif (j == 63): building_height = 13 - elif (j == 64): building_height = 16 - elif (j == 65): building_height = 19 - elif (j == 66): building_height = 22 - elif (j == 67): building_height = 25 - elif (j == 68): building_height = 28 - elif (j == 69): building_height = 31 - - if (doorIncrement == 25): - fillBlocks(white_stained_glass, x, 4, z, x, building_height, z) - setBlock(white_concrete, x, 1, z) - setBlock(dark_oak_door_lower, x, 2, z) - setBlock(dark_oak_door_upper, x, 3, z) - doorIncrement = 0 - else: - fillBlocks(white_concrete, x, 1, z, x, 2, z) - fillBlocks(white_stained_glass, x, 3, z, x, building_height, z) - doorIncrement += 1 - setBlock(white_concrete, x, building_height + 1, z) - elif (j >= 70 and j <= 79): # House interior - if (j >= 70): - setBlock(white_concrete, x, 5, z) - if (j >= 71): - setBlock(white_concrete, x, 8, z) - if (j >= 72): - setBlock(white_concrete, x, 11, z) - if (j >= 73): - setBlock(white_concrete, x, 14, z) - if (j >= 74): - setBlock(white_concrete, x, 17, z) - if (j >= 75): - setBlock(white_concrete, x, 20, z) - if (j >= 76): - setBlock(white_concrete, x, 23, z) - if (j >= 77): - setBlock(white_concrete, x, 26, z) - if (j >= 78): - setBlock(white_concrete, x, 29, z) - if (j >= 78): - setBlock(white_concrete, x, 32, z) - - setBlock(glowstone, x, 1, z) - - z += 1 - x += 1 - ElementIncr += 1 - - -print("Saving minecraft world...") -saveRegion() -print("Done! Finished in " + str(round(time.time() - processStartTime, 2)) + " seconds (" + str(round((time.time() - processStartTime) / 60, 2)) + " minutes)") -os._exit(0) +if __name__ == "__main__": + run() diff --git a/cat b/cat new file mode 100644 index 0000000..b97a7dc --- /dev/null +++ b/cat @@ -0,0 +1,20 @@ +anvil-parser==0.9.0 +certifi==2022.9.24 +charset-normalizer==2.0.12 +cycler==0.11.0 +fonttools==4.37.4 +frozendict==2.3.4 +idna==3.4 +kiwisolver==1.4.4 +matplotlib==3.5.2 +NBT==1.5.1 +numpy==1.22.3 +opencv-python==4.5.5.64 +packaging==21.3 +Pillow==9.2.0 +pyparsing==3.0.9 +python-dateutil==2.8.2 +python-polylabel==0.6 +requests==2.27.1 +six==1.16.0 +urllib3==1.26.12 diff --git a/requirements.txt b/requirements.txt index 8217109..04c4a80 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,31 @@ -anvil-parser==0.9.0 -matplotlib==3.5.2 -numpy==1.22.3 -opencv-python==4.5.5.64 -python-polylabel==0.6 -requests==2.27.1 -argparse==1.4.0 \ No newline at end of file +anvil-parser==0.9.0 +black==22.8.0 +certifi==2022.9.24 +charset-normalizer==2.0.12 +click==8.1.3 +cycler==0.11.0 +flake8==5.0.4 +fonttools==4.37.4 +frozendict==2.3.4 +idna==3.4 +kiwisolver==1.4.4 +matplotlib==3.5.2 +mccabe==0.7.0 +mypy-extensions==0.4.3 +NBT==1.5.1 +numpy==1.22.3 +opencv-python==4.5.5.64 +packaging==21.3 +pathspec==0.10.1 +Pillow==9.2.0 +platformdirs==2.5.2 +pycodestyle==2.9.1 +pyflakes==2.5.0 +pyparsing==3.0.9 +python-dateutil==2.8.2 +python-polylabel==0.6 +requests==2.27.1 +six==1.16.0 +tomli==2.0.1 +typing_extensions==4.3.0 +urllib3==1.26.12 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/bresenham.py b/src/bresenham.py new file mode 100644 index 0000000..dc35e77 --- /dev/null +++ b/src/bresenham.py @@ -0,0 +1,26 @@ +def bresenham(x1, y1, x2, y2): + """Bresenham Line Algorithm Credit: encukou/bresenham@Github""" + dx = x2 - x1 + dy = y2 - y1 + + xsign = 1 if dx > 0 else -1 + ysign = 1 if dy > 0 else -1 + + dx = abs(dx) + dy = abs(dy) + + if dx > dy: + xx, xy, yx, yy = xsign, 0, 0, ysign + else: + dx, dy = dy, dx + xx, xy, yx, yy = 0, ysign, xsign, 0 + + D = 2 * dy - dx + y = 0 + + for x in range(dx + 1): + yield x1 + x * xx + y * yx, y1 + x * xy + y * yy + if D >= 0: + y += 1 + D -= 2 * dx + D += 2 * dy diff --git a/src/floodFill.py b/src/floodFill.py new file mode 100644 index 0000000..f6575e6 --- /dev/null +++ b/src/floodFill.py @@ -0,0 +1,106 @@ +from time import time +import numpy as np +import matplotlib.path as mplPath +from polylabel import polylabel + + +def floodFill(img, px, py, newColor, currentBuilding, elementType="None"): + startTimeFloodfill = time() + currentBuilding = np.delete(currentBuilding, 0, axis=0) + if len(currentBuilding) <= 2: + return img + if not (mplPath.Path(currentBuilding).contains_point((py, px))): + centroid = polylabel([currentBuilding.tolist()], with_distance=True) + px = round(centroid[0][1]) + py = round(centroid[0][0]) + if not (mplPath.Path(currentBuilding).contains_point((py, px))): + if mplPath.Path(currentBuilding).contains_point((py - 5, px)): + py -= 5 + elif mplPath.Path(currentBuilding).contains_point((py + 5, px)): + py += 5 + elif mplPath.Path(currentBuilding).contains_point((py, px - 5)): + px -= 5 + elif mplPath.Path(currentBuilding).contains_point((py, px + 5)): + px += 5 + else: + return img + + if str(img[px][py][0])[:1] == "5" or str(img[px][py][0])[:1] == "6": + if mplPath.Path(currentBuilding).contains_point((py - 1, px)): + py -= 1 + elif mplPath.Path(currentBuilding).contains_point((py + 1, px)): + py += 1 + elif mplPath.Path(currentBuilding).contains_point((py, px - 1)): + px -= 1 + elif mplPath.Path(currentBuilding).contains_point((py, px + 1)): + px += 1 + else: + return img + + try: + oldColor = img[px][py][0] + except Exception: + return img + queue = [(px, py)] + seen = set() + tot_rows = img.shape[0] + tot_cols = img.shape[1] + queueLen = 0 + while queue: + nxt = [] + for x, y in queue: + if img[x][y] == newColor: + continue + if not (mplPath.Path(currentBuilding).contains_point((y, x))): + return img + img[x][y] = newColor + seen.add((x, y)) + + if ( + x + and (x - 1, y) not in seen + and ( + img[x - 1][y] == oldColor + or (elementType == "building" and str(img[x - 1][y][0])[:1] == "1") + ) + ): + nxt.append((x - 1, y)) + if ( + y + and (x, y - 1) not in seen + and ( + img[x][y - 1] == oldColor + or (elementType == "building" and str(img[x][y - 1][0])[:1] == "1") + ) + ): + nxt.append((x, y - 1)) + if ( + x < tot_rows - 1 + and (x + 1, y) not in seen + and ( + img[x + 1][y] == oldColor + or (elementType == "building" and str(img[x + 1][y][0])[:1] == "1") + ) + ): + nxt.append((x + 1, y)) + if ( + y < tot_cols - 1 + and (x, y + 1) not in seen + and ( + img[x][y + 1] == oldColor + or (elementType == "building" and str(img[x][y + 1][0])[:1] == "1") + ) + ): + nxt.append((x, y + 1)) + + # Timeout (known issue, see Github readme) + if time() - startTimeFloodfill > 7 or ( + elementType == "tree_row" and time() - startTimeFloodfill > 0.3 + ): + return img + + queue = nxt + if len(nxt) > queueLen: + queueLen = len(nxt) + + return img diff --git a/src/getData.py b/src/getData.py new file mode 100644 index 0000000..cb962fc --- /dev/null +++ b/src/getData.py @@ -0,0 +1,60 @@ +import os +import requests +from random import choice + + +def getData(city, state, country, debug): + print("Fetching data...") + api_servers = [ + "https://overpass-api.de/api/interpreter", + "https://lz4.overpass-api.de/api/interpreter", + "https://z.overpass-api.de/api/interpreter", + "https://maps.mail.ru/osm/tools/overpass/api/interpreter", + "https://overpass.openstreetmap.ru/api/interpreter", + "https://overpass.kumi.systems/api/interpreter", + ] + url = choice(api_servers) + query1 = ( + """ + [out:json]; + area[name=""" + + '"' + + city.replace(" ", "-") + + '"' + + """]->.city; + area[name=""" + + '"' + + state.replace(" ", "-") + + '"' + + """]->.state; + area[name=""" + + '"' + + country.replace(" ", "-") + + '"' + + """]->.country; + way(area.country)(area.state)(area.city)[!power][!place][!ferry]; + (._;>;); + out; + """ + ) + + print("Chosen server: " + url) + try: + data = requests.get(url, params={"data": query1}).json() + + if len(data["elements"]) == 0: + print("Error! No data available") + os._exit(1) + except Exception as e: + if "The server is probably too busy to handle your request." in str(e): + print("Error! OSM server overloaded") + elif "Dispatcher_Client::request_read_and_idx::rate_limited" in str(e): + print("Error! IP rate limited") + else: + print("Error! " + str(e)) + os._exit(1) + + if debug: + with open("arnis-debug-raw_data.json", "w", encoding="utf-8") as f: + f.write(str(data)) + return data diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..bf8cb94 --- /dev/null +++ b/src/main.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python + +# Copyright 2022 by louis-e, https://github.com/louis-e/. +# MIT License +# Please see the LICENSE file that should have been included as part of this package. + +import os +import sys +import time +import gc +import argparse +import anvil +from random import randint +from math import floor +import numpy as np + +from .getData import getData +from .processData import processData + +parser = argparse.ArgumentParser( + description="Arnis - Generate cities from real life in Minecraft using Python" +) +parser.add_argument("--city", dest="city", help="Name of the city") +parser.add_argument("--state", dest="state", help="Name of the state") +parser.add_argument("--country", dest="country", help="Name of the country") +parser.add_argument("--path", dest="path", help="Path to the minecraft world") +parser.add_argument( + "--debug", + dest="debug", + default=False, + action="store_true", + help="Enable debug mode", +) +args = parser.parse_args() +if args.city is None or args.state is None or args.country is None or args.path is None: + print("Error! Missing arguments") + os._exit(1) + +gc.collect() +np.seterr(all="raise") +np.set_printoptions(threshold=sys.maxsize) + +processStartTime = time.time() +air = anvil.Block("minecraft", "air") +stone = anvil.Block("minecraft", "stone") +grass_block = anvil.Block("minecraft", "grass_block") +dirt = anvil.Block("minecraft", "dirt") +sand = anvil.Block("minecraft", "sand") +podzol = anvil.Block.from_numeric_id(3, 2) +grass = anvil.Block.from_numeric_id(175, 2) +farmland = anvil.Block("minecraft", "farmland") +water = anvil.Block("minecraft", "water") +wheat = anvil.Block("minecraft", "wheat") +carrots = anvil.Block("minecraft", "carrots") +potatoes = anvil.Block("minecraft", "potatoes") +cobblestone = anvil.Block("minecraft", "cobblestone") +iron_block = anvil.Block("minecraft", "iron_block") +log = anvil.Block.from_numeric_id(17) +leaves = anvil.Block.from_numeric_id(18) +white_stained_glass = anvil.Block("minecraft", "white_stained_glass") +dark_oak_door_lower = anvil.Block( + "minecraft", "dark_oak_door", properties={"half": "lower"} +) +dark_oak_door_upper = anvil.Block( + "minecraft", "dark_oak_door", properties={"half": "upper"} +) +cobblestone_wall = anvil.Block("minecraft", "cobblestone_wall") +stone_brick_slab = anvil.Block.from_numeric_id(44, 5) +red_flower = anvil.Block.from_numeric_id(38) +white_concrete = anvil.Block("minecraft", "white_concrete") +black_concrete = anvil.Block("minecraft", "black_concrete") +gray_concrete = anvil.Block("minecraft", "gray_concrete") +light_gray_concrete = anvil.Block("minecraft", "light_gray_concrete") +green_stained_hardened_clay = anvil.Block.from_numeric_id(159, 5) +dirt = anvil.Block("minecraft", "dirt") +glowstone = anvil.Block("minecraft", "glowstone") +sponge = anvil.Block("minecraft", "sponge") + +regions = {} +for x in range(0, 3): + for z in range(0, 3): + regions["r." + str(x) + "." + str(z)] = anvil.EmptyRegion(0, 0) + + +def setBlock(block, x, y, z): + flooredX = floor(x / 512) + flooredZ = floor(z / 512) + identifier = "r." + str(flooredX) + "." + str(flooredZ) + if identifier not in regions: + regions[identifier] = anvil.EmptyRegion(0, 0) + regions[identifier].set_block(block, x - flooredX * 512, y, z - flooredZ * 512) + + +def fillBlocks(block, x1, y1, z1, x2, y2, z2): + for x in range(x1, x2 + 1): + for y in range(y1, y2 + 1): + for z in range(z1, z2 + 1): + if not (x == x2 + 1 or y == y2 + 1 or z == z2 + 1): + setBlock(block, x, y, z) + + +mcWorldPath = args.path +if mcWorldPath[-1] == "/": + mcWorldPath = mcWorldPath[:-1] + + +def saveRegion(region="all"): + if region == "all": + for key in regions: + regions[key].save(mcWorldPath + "/region/" + key + ".mca") + print("Saved " + key) + else: + regions[region].save(mcWorldPath + "/region/" + region + ".mca") + print("Saved " + region) + + +rawdata = getData(args.city, args.state, args.country, args.debug) +imgarray = processData(rawdata, args) + + +print("Generating minecraft world...") + +x = 0 +z = 0 +doorIncrement = 0 +ElementIncr = 0 +ElementsLen = len(imgarray) +lastProgressPercentage = 0 +for i in imgarray: + progressPercentage = round(100 * (ElementIncr + 1) / ElementsLen) + if progressPercentage % 10 == 0 and progressPercentage != lastProgressPercentage: + print( + "Pixel " + + str(ElementIncr + 1) + + "/" + + str(ElementsLen) + + " (" + + str(progressPercentage) + + "%)" + ) + lastProgressPercentage = progressPercentage + + z = 0 + for j in i: + setBlock(dirt, x, 0, z) + if j == 0: # Ground + setBlock(light_gray_concrete, x, 1, z) + elif j == 10: # Street + setBlock(black_concrete, x, 1, z) + setBlock(air, x, 2, z) + elif j == 11: # Footway + setBlock(gray_concrete, x, 1, z) + setBlock(air, x, 2, z) + elif j == 12: # Natural path + setBlock(cobblestone, x, 1, z) + elif j == 13: # Bridge + setBlock(light_gray_concrete, x, 2, z) + setBlock(light_gray_concrete, x - 1, 2, z - 1) + setBlock(light_gray_concrete, x + 1, 2, z - 1) + setBlock(light_gray_concrete, x + 1, 2, z + 1) + setBlock(light_gray_concrete, x - 1, 2, z + 1) + elif j == 14: # Railway + setBlock(iron_block, x, 2, z) + elif j == 20: # Parking + setBlock(gray_concrete, x, 1, z) + elif j == 21: # Fountain border + setBlock(light_gray_concrete, x, 2, z) + setBlock(white_concrete, x, 1, z) + elif j >= 22 and j <= 24: # Fence + if str(j)[-1] == "2": + setBlock(cobblestone_wall, x, 2, z) + else: + fillBlocks(cobblestone, x, 2, z, x, int(str(j[0])[-1]), z) + + setBlock(grass_block, x, 1, z) + elif j == 30: # Meadow + setBlock(grass_block, x, 1, z) + randomChoice = randint(0, 2) + if randomChoice == 0 or randomChoice == 1: + setBlock(grass, x, 2, z) + elif j == 31: # Farmland + randomChoice = randint(0, 16) + if randomChoice == 0: + setBlock(water, x, 1, z) + else: + setBlock(farmland, x, 1, z) + randomChoice = randint(0, 2) + if randomChoice == 0: + setBlock(wheat, x, 2, z) + elif randomChoice == 1: + setBlock(carrots, x, 2, z) + else: + setBlock(potatoes, x, 2, z) + elif j == 32: # Forest + setBlock(grass_block, x, 1, z) + randomChoice = randint(0, 8) + if randomChoice >= 0 and randomChoice <= 5: + setBlock(grass, x, 2, z) + elif randomChoice == 6: + fillBlocks(log, x, 2, z, x, 8, z) + fillBlocks(leaves, x - 2, 5, z - 2, x + 2, 6, z + 2) + setBlock(air, x - 2, 6, z - 2) + setBlock(air, x - 2, 6, z + 2) + setBlock(air, x + 2, 6, z - 2) + setBlock(air, x + 2, 6, z + 2) + fillBlocks(leaves, x - 1, 7, z - 1, x + 1, 8, z + 1) + setBlock(air, x - 1, 8, z - 1) + setBlock(air, x - 1, 8, z + 1) + setBlock(air, x + 1, 8, z - 1) + setBlock(air, x + 1, 8, z + 1) + elif j == 33: # Cemetery + setBlock(podzol, x, 1, z) + randomChoice = randint(0, 100) + if randomChoice == 0: + setBlock(cobblestone, x - 1, 2, z) + setBlock(stone_brick_slab, x - 1, 3, z) + setBlock(stone_brick_slab, x, 2, z) + setBlock(stone_brick_slab, x + 1, 2, z) + elif randomChoice == 1: + setBlock(cobblestone, x, 2, z - 1) + setBlock(stone_brick_slab, x, 3, z - 1) + setBlock(stone_brick_slab, x, 2, z) + setBlock(stone_brick_slab, x, 2, z + 1) + elif randomChoice == 2 or randomChoice == 3: + setBlock(red_flower, x, 2, z) + elif j == 34: # Beach + setBlock(sand, x, 1, z) + elif j == 35: # Wetland + randomChoice = randint(0, 2) + if randomChoice == 0: + setBlock(grass_block, x, 1, z) + else: + setBlock(water, x, 1, z) + elif j == 36: # Pitch + setBlock(green_stained_hardened_clay, x, 1, z) + elif j == 37: # Swimming pool + setBlock(water, x, 1, z) + setBlock(white_concrete, x, 0, z) + elif j == 38: # Water + setBlock(water, x, 1, z) + elif j == 39: # Raw grass + setBlock(grass_block, x, 1, z) + elif j >= 50 and j <= 59: # House corner + building_height = 5 + if j == 51: + building_height = 8 + elif j == 52: + building_height = 11 + elif j == 53: + building_height = 14 + elif j == 54: + building_height = 17 + elif j == 55: + building_height = 20 + elif j == 56: + building_height = 23 + elif j == 57: + building_height = 26 + elif j == 58: + building_height = 29 + elif j == 59: + building_height = 32 + + fillBlocks(white_concrete, x, 1, z, x, building_height, z) + elif j >= 60 and j <= 69: # House wall + building_height = 4 + if j == 61: + building_height = 7 + elif j == 62: + building_height = 10 + elif j == 63: + building_height = 13 + elif j == 64: + building_height = 16 + elif j == 65: + building_height = 19 + elif j == 66: + building_height = 22 + elif j == 67: + building_height = 25 + elif j == 68: + building_height = 28 + elif j == 69: + building_height = 31 + + if doorIncrement == 25: + fillBlocks(white_stained_glass, x, 4, z, x, building_height, z) + setBlock(white_concrete, x, 1, z) + setBlock(dark_oak_door_lower, x, 2, z) + setBlock(dark_oak_door_upper, x, 3, z) + doorIncrement = 0 + else: + fillBlocks(white_concrete, x, 1, z, x, 2, z) + fillBlocks(white_stained_glass, x, 3, z, x, building_height, z) + doorIncrement += 1 + setBlock(white_concrete, x, building_height + 1, z) + elif j >= 70 and j <= 79: # House interior + if j >= 70: + setBlock(white_concrete, x, 5, z) + if j >= 71: + setBlock(white_concrete, x, 8, z) + if j >= 72: + setBlock(white_concrete, x, 11, z) + if j >= 73: + setBlock(white_concrete, x, 14, z) + if j >= 74: + setBlock(white_concrete, x, 17, z) + if j >= 75: + setBlock(white_concrete, x, 20, z) + if j >= 76: + setBlock(white_concrete, x, 23, z) + if j >= 77: + setBlock(white_concrete, x, 26, z) + if j >= 78: + setBlock(white_concrete, x, 29, z) + if j >= 78: + setBlock(white_concrete, x, 32, z) + + setBlock(glowstone, x, 1, z) + + z += 1 + x += 1 + ElementIncr += 1 + + +print("Saving minecraft world...") +saveRegion() +print( + "Done! Finished in " + + str(round(time.time() - processStartTime, 2)) + + " seconds (" + + str(round((time.time() - processStartTime) / 60, 2)) + + " minutes)" +) +os._exit(0) diff --git a/src/processData.py b/src/processData.py new file mode 100644 index 0000000..af9986a --- /dev/null +++ b/src/processData.py @@ -0,0 +1,616 @@ +from time import time +from cv2 import imwrite +import numpy as np + +from .bresenham import bresenham +from .floodFill import floodFill + + +def processData(data, args): + print("Parsing data...") + resDownScaler = 100 + processingStartTime = time() + + greatestElementX = 0 + greatestElementY = 0 + for element in data["elements"]: + if element["type"] == "node": + element["lat"] = int(str(element["lat"]).replace(".", "")) + element["lon"] = int(str(element["lon"]).replace(".", "")) + + if element["lat"] > greatestElementX: + greatestElementX = element["lat"] + if element["lon"] > greatestElementY: + greatestElementY = element["lon"] + + for element in data["elements"]: + if element["type"] == "node": + if len(str(element["lat"])) != len(str(greatestElementX)): + for i in range( + 0, len(str(greatestElementX)) - len(str(element["lat"])) + ): + element["lat"] *= 10 + + if len(str(element["lon"])) != len(str(greatestElementY)): + for i in range( + 0, len(str(greatestElementY)) - len(str(element["lon"])) + ): + element["lon"] *= 10 + + lowestElementX = greatestElementX + lowestElementY = greatestElementY + for element in data["elements"]: + if element["type"] == "node": + if element["lat"] < lowestElementX: + lowestElementX = element["lat"] + if element["lon"] < lowestElementY: + lowestElementY = element["lon"] + + nodesDict = {} + for element in data["elements"]: + if element["type"] == "node": + element["lat"] -= lowestElementX + element["lon"] -= lowestElementY + nodesDict[element["id"]] = [element["lat"], element["lon"]] + + img = np.zeros( + ( + round((greatestElementY - lowestElementY) / resDownScaler) + 5, + round((greatestElementX - lowestElementX) / resDownScaler) + 5, + 1, + ), + np.uint8, + ) + img.fill(0) + imgLanduse = img.copy() + origImgSize = img.shape[0] * img.shape[1] + + orig_posDeterminationCoordX = 0 + orig_posDeterminationCoordY = 0 + map_posDeterminationCoordX = 0 + map_posDeterminationCoordY = 0 + nodeIndexList = [] + for i, element in enumerate(data["elements"]): + if element["type"] == "way": + for j, node in enumerate(element["nodes"]): + element["nodes"][j] = nodesDict[node] + + if ( + "tags" in element + and "building" in element["tags"] + and orig_posDeterminationCoordX == 0 + ): + orig_posDeterminationCoordX = element["nodes"][0][0] + orig_posDeterminationCoordY = element["nodes"][0][1] + map_posDeterminationCoordX = round( + element["nodes"][0][0] / resDownScaler + ) + map_posDeterminationCoordY = round( + element["nodes"][0][1] / resDownScaler + ) + elif element["type"] == "node": + nodeIndexList.append(i) + + for i in reversed(nodeIndexList): + del data["elements"][i] + + if args.debug: + print("Biggest element X: " + str(greatestElementX)) + print("Biggest element Y: " + str(greatestElementY)) + print("Lowest element X: " + str(lowestElementX)) + print("Lowest element Y: " + str(lowestElementY)) + print( + "Original position determination reference coordinates: " + + str(orig_posDeterminationCoordX) + + ", " + + str(orig_posDeterminationCoordY) + ) + print( + "Map position determination reference coordinates: " + + str(map_posDeterminationCoordX) + + ", " + + str(map_posDeterminationCoordY) + ) + with open("arnis-debug-processed_data.json", "w", encoding="utf-8") as f: + f.write(str(data)) + print("Processing data...") + + maxBuilding = (0, 0) + minBuilding = (greatestElementX, greatestElementY) + ElementIncr = 0 + ElementsLen = len(data["elements"]) + lastProgressPercentage = 0 + for element in reversed(data["elements"]): + progressPercentage = round(100 * (ElementIncr + 1) / ElementsLen) + if ( + progressPercentage % 10 == 0 + and progressPercentage != lastProgressPercentage + ): + print( + "Element " + + str(ElementIncr + 1) + + "/" + + str(ElementsLen) + + " (" + + str(progressPercentage) + + "%)" + ) + lastProgressPercentage = progressPercentage + + if element["type"] == "way" and "tags" in element: + if "building" in element["tags"]: + previousElement = (0, 0) + cornerAddup = (0, 0, 0) + currentBuilding = np.array([[0, 0]]) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + buildingHeight = 1 + + if cordX > maxBuilding[0]: + maxBuilding = (cordX, maxBuilding[1]) + if cordY > maxBuilding[1]: + maxBuilding = (maxBuilding[0], cordY) + + if cordX < minBuilding[0]: + minBuilding = (cordX, minBuilding[1]) + if cordY < minBuilding[1]: + minBuilding = (minBuilding[0], cordY) + + if previousElement != (0, 0): + if "height" in element["tags"]: + if len(element["tags"]["height"]) >= 3: + buildingHeight = 9 + elif len(element["tags"]["height"]) == 1: + buildingHeight = 2 + elif element["tags"]["height"][:1] == "1": + buildingHeight = 3 + elif element["tags"]["height"][:1] == "2": + buildingHeight = 6 + else: + buildingHeight = 9 + + if ( + "building:levels" in element["tags"] + and int(element["tags"]["building:levels"]) <= 8 + and int(element["tags"]["building:levels"]) >= 1 + ): + buildingHeight = str( + int(element["tags"]["building:levels"]) - 1 + ) + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + if not ( + str(img[i[1]][i[0]][0])[:1] == "6" + and img[i[1]][i[0]][0] > int("6" + str(buildingHeight)) + ): + img[i[1]][i[0]] = int("6" + str(buildingHeight)) + + currentBuilding = np.append( + currentBuilding, [[cordX, cordY]], axis=0 + ) + if not ( + str(img[cordY][cordX][0])[:1] == "5" + and img[cordY][cordX][0] > int("5" + str(buildingHeight)) + ): + img[cordY][cordX] = int("5" + str(buildingHeight)) + + if not ( + str(img[previousElement[1]][previousElement[0]][0])[:1] + == "5" + and img[previousElement[1]][previousElement[0]][0] + > int("5" + str(buildingHeight)) + ): + img[previousElement[1]][previousElement[0]] = int( + "5" + str(buildingHeight) + ) + + cornerAddup = ( + cornerAddup[0] + cordX, + cornerAddup[1] + cordY, + cornerAddup[2] + 1, + ) + previousElement = (cordX, cordY) + + if cornerAddup != (0, 0, 0): + img = floodFill( + img, + round(cornerAddup[1] / cornerAddup[2]), + round(cornerAddup[0] / cornerAddup[2]), + int("7" + str(buildingHeight)), + currentBuilding, + elementType="building", + ) + + elif "highway" in element["tags"]: + previousElement = (0, 0) + for coordinate in element["nodes"]: + cordX = round( + map_posDeterminationCoordX + * coordinate[0] + / orig_posDeterminationCoordX + ) + cordY = round( + map_posDeterminationCoordY + * coordinate[1] + / orig_posDeterminationCoordY + ) + highwayType = 10 + if ( + previousElement != (0, 0) + and element["tags"]["highway"] != "corridor" + and previousElement != (0, 0) + and element["tags"]["highway"] != "steps" + and element["tags"]["highway"] != "bridge" + ): + blockRange = 2 + highwayType = 10 + + if ( + element["tags"]["highway"] == "path" + or element["tags"]["highway"] == "footway" + ): + blockRange = 1 + highwayType = 11 + elif element["tags"]["highway"] == "motorway": + blockRange = 4 + elif element["tags"]["highway"] == "track": + blockRange = 1 + highwayType = 12 + elif ( + "lanes" in element["tags"] + and element["tags"]["lanes"] != "1" + and element["tags"]["lanes"] != "2" + ): + blockRange = 4 + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + for x in range(i[0] - blockRange, i[0] + blockRange + 1): + for y in range( + i[1] - blockRange, i[1] + blockRange + 1 + ): + if img[y][x] == 0: + img[y][x] = highwayType + previousElement = (cordX, cordY) + + elif "landuse" in element["tags"]: + previousElement = (0, 0) + cornerAddup = (0, 0, 0) + currentLanduse = np.array([[0, 0]]) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + landuseType = 39 + if ( + previousElement != (0, 0) + and element["tags"]["landuse"] != "industrial" + and element["tags"]["landuse"] != "residential" + ): + if ( + element["tags"]["landuse"] == "greenfield" + or element["tags"]["landuse"] == "meadow" + or element["tags"]["landuse"] == "grass" + ): + landuseType = 30 + elif element["tags"]["landuse"] == "farmland": + landuseType = 31 + elif element["tags"]["landuse"] == "forest": + landuseType = 32 + elif element["tags"]["landuse"] == "cemetery": + landuseType = 33 + elif element["tags"]["landuse"] == "beach": + landuseType = 34 + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + if imgLanduse[i[1]][i[0]] == 0: + imgLanduse[i[1]][i[0]] = landuseType + + currentLanduse = np.append( + currentLanduse, [[cordX, cordY]], axis=0 + ) + cornerAddup = ( + cornerAddup[0] + cordX, + cornerAddup[1] + cordY, + cornerAddup[2] + 1, + ) + previousElement = (cordX, cordY) + + if cornerAddup != (0, 0, 0): + imgLanduse = floodFill( + imgLanduse, + round(cornerAddup[1] / cornerAddup[2]), + round(cornerAddup[0] / cornerAddup[2]), + landuseType, + currentLanduse, + ) + + elif "natural" in element["tags"]: + previousElement = (0, 0) + cornerAddup = (0, 0, 0) + currentNatural = np.array([[0, 0]]) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + naturalType = 39 + if previousElement != (0, 0): + if ( + element["tags"]["natural"] == "scrub" + or element["tags"]["natural"] == "grassland" + ): + naturalType = 30 + elif ( + element["tags"]["natural"] == "beach" + or element["tags"]["natural"] == "sand" + ): + naturalType = 34 + elif ( + element["tags"]["natural"] == "wood" + or element["tags"]["natural"] == "tree_row" + ): + naturalType = 32 + elif element["tags"]["natural"] == "wetland": + naturalType = 35 + elif element["tags"]["natural"] == "water": + naturalType = 38 + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + if imgLanduse[i[1]][i[0]] == 0: + imgLanduse[i[1]][i[0]] = naturalType + + currentNatural = np.append( + currentNatural, [[cordX, cordY]], axis=0 + ) + cornerAddup = ( + cornerAddup[0] + cordX, + cornerAddup[1] + cordY, + cornerAddup[2] + 1, + ) + previousElement = (cordX, cordY) + + if cornerAddup != (0, 0, 0): + if naturalType != 32: + imgLanduse = floodFill( + imgLanduse, + round(cornerAddup[1] / cornerAddup[2]), + round(cornerAddup[0] / cornerAddup[2]), + naturalType, + currentNatural, + ) + else: + imgLanduse = floodFill( + imgLanduse, + round(cornerAddup[1] / cornerAddup[2]), + round(cornerAddup[0] / cornerAddup[2]), + naturalType, + currentNatural, + elementType="tree_row", + ) + + elif "leisure" in element["tags"]: + previousElement = (0, 0) + cornerAddup = (0, 0, 0) + currentLeisure = np.array([[0, 0]]) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + leisureType = 39 + if ( + previousElement != (0, 0) + and element["tags"]["leisure"] != "marina" + ): + if ( + element["tags"]["leisure"] == "park" + or element["tags"]["leisure"] == "playground" + or element["tags"]["leisure"] == "garden" + ): + leisureType = 30 + elif element["tags"]["leisure"] == "pitch": + leisureType = 36 + elif element["tags"]["leisure"] == "swimming_pool": + leisureType = 37 + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + if imgLanduse[i[1]][i[0]] == 0: + imgLanduse[i[1]][i[0]] = leisureType + + currentLeisure = np.append( + currentLeisure, [[cordX, cordY]], axis=0 + ) + cornerAddup = ( + cornerAddup[0] + cordX, + cornerAddup[1] + cordY, + cornerAddup[2] + 1, + ) + previousElement = (cordX, cordY) + + if cornerAddup != (0, 0, 0): + imgLanduse = floodFill( + imgLanduse, + round(cornerAddup[1] / cornerAddup[2]), + round(cornerAddup[0] / cornerAddup[2]), + leisureType, + currentLeisure, + ) + + elif "waterway" in element["tags"]: + previousElement = (0, 0) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + + if previousElement != (0, 0) and not ( + "layer" in element["tags"] + and ( + element["tags"]["layer"] == "-1" + or element["tags"]["layer"] == "-2" + or element["tags"]["layer"] != "-3" + ) + ): + waterwayWidth = 4 + if "width" in element["tags"]: + try: + waterwayWidth = int(element["tags"]["width"]) + except Exception: + waterwayWidth = int(float(element["tags"]["width"])) + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + for x in range( + round(i[0] - waterwayWidth / 2), + round(i[0] + waterwayWidth + 1 / 2), + ): + for y in range( + round(i[1] - waterwayWidth / 2), + round(i[1] + waterwayWidth + 1 / 2), + ): + if img[y][x] != 13: + img[y][x] = 38 + previousElement = (cordX, cordY) + + elif "amenity" in element["tags"]: + previousElement = (0, 0) + cornerAddup = (0, 0, 0) + currentAmenity = np.array([[0, 0]]) + amenityType = 20 + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + if previousElement != (0, 0) and ( + element["tags"]["amenity"] == "parking" + or element["tags"]["amenity"] == "fountain" + ): + if element["tags"]["amenity"] == "parking": + amenityType = 20 + elif element["tags"]["amenity"] == "fountain": + amenityType = 21 + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + if imgLanduse[i[1]][i[0]] == 0: + imgLanduse[i[1]][i[0]] = amenityType + + currentAmenity = np.append( + currentAmenity, [[cordX, cordY]], axis=0 + ) + cornerAddup = ( + cornerAddup[0] + cordX, + cornerAddup[1] + cordY, + cornerAddup[2] + 1, + ) + previousElement = (cordX, cordY) + + if amenityType == 21: + amenityType = 37 + if cornerAddup != (0, 0, 0): + imgLanduse = floodFill( + imgLanduse, + round(cornerAddup[1] / cornerAddup[2]), + round(cornerAddup[0] / cornerAddup[2]), + amenityType, + currentAmenity, + ) + + elif "bridge" in element["tags"]: + previousElement = (0, 0) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + + if previousElement != (0, 0): + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + img[i[1]][i[0]] = 13 + previousElement = (cordX, cordY) + + elif "railway" in element["tags"]: + previousElement = (0, 0) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + + if ( + previousElement != (0, 0) + and element["tags"]["railway"] != "proposed" + ): + for i in bresenham( + cordX - 2, + cordY - 2, + previousElement[0] - 2, + previousElement[1] - 2, + ): + img[i[1]][i[0]] = 14 + for i in bresenham( + cordX + 1, + cordY + 1, + previousElement[0] + 1, + previousElement[1] + 1, + ): + img[i[1]][i[0]] = 14 + previousElement = (cordX, cordY) + + elif "barrier" in element["tags"]: + previousElement = (0, 0) + for coordinate in element["nodes"]: + cordX = round(coordinate[0] / resDownScaler) + cordY = round(coordinate[1] / resDownScaler) + + if previousElement != (0, 0): + wallHeight = 1 + if "height" in element["tags"]: + wallHeight = round(int(float(element["tags"]["height"]))) + if wallHeight > 3: + wallHeight = 2 + + for i in bresenham( + cordX, cordY, previousElement[0], previousElement[1] + ): + if ( + str(img[i[1]][i[0]][0])[:1] != 5 + and str(img[i[1]][i[0]][0])[:1] != 6 + and str(img[i[1]][i[0]][0])[:1] != 7 + ): + img[i[1]][i[0]] = int("2" + str((wallHeight + 1))) + previousElement = (cordX, cordY) + + ElementIncr += 1 + + print("Optimizing data...") + + minBuilding = (minBuilding[0] - 50, minBuilding[1] - 50) + maxBuilding = (maxBuilding[0] + 50, maxBuilding[1] + 50) + img = img[minBuilding[1] : maxBuilding[1], minBuilding[0] : maxBuilding[0]] + imgLanduse = imgLanduse[ + minBuilding[1] : maxBuilding[1], minBuilding[0] : maxBuilding[0] + ] + print( + str(100 - round(100 * (img.shape[0] * img.shape[1]) / origImgSize)) + + "% size reduction" + ) + for x in range(0, img.shape[0]): + for y in range(0, img.shape[1]): + if imgLanduse[x][y] != 0 and img[x][y] == 0: + img[x][y] = imgLanduse[x][y] + + print( + "Processing finished in " + + str(round(time() - processingStartTime, 2)) + + " seconds (" + + str(round((time() - processingStartTime) / 60, 2)) + + " minutes)" + ) + if args.debug: + imwrite("arnis-debug-map.png", img) + return np.flip(img, axis=1)