45 Commits
master ... main

Author SHA1 Message Date
2618e434e4 cli/print: print tracks and areas
Some checks failed
Build maps / validate (push) Failing after 19s
Build maps / build-bluemap (push) Successful in 28s
Build maps / build-mapcrafter (push) Successful in 1m20s
2026-01-27 16:52:43 +09:00
8ef19f8051 README: fix invalid command 2026-01-27 16:52:21 +09:00
7cb7e0c0e7 cli/validate: allow exempting certain markers from validation 2026-01-27 16:52:07 +09:00
306d35fb23 cli/validate: validate track point offsets
Some checks failed
Build maps / validate (push) Failing after 22s
Build maps / build-bluemap (push) Successful in 31s
Build maps / build-mapcrafter (push) Successful in 34s
2026-01-27 16:20:15 +09:00
86f68fdd9a cli/validate: wrap errors in types, improve formatting 2026-01-27 16:19:08 +09:00
5cd25d1717 .gitea/workflows: run validator
Some checks failed
Build maps / validate (push) Failing after 21s
Build maps / build-mapcrafter (push) Successful in 33s
Build maps / build-bluemap (push) Successful in 34s
2026-01-27 04:41:53 +09:00
06c99de7eb cli/validate: fail on errors 2026-01-27 04:41:53 +09:00
1fe46eea88 cli: use recognizable main functions 2026-01-27 04:41:52 +09:00
David Kaasen
0892890d4f Maa bruke samme verdensnavn som er i Mapcrafter.
All checks were successful
Build maps / build-mapcrafter (push) Successful in 31s
Build maps / build-bluemap (push) Successful in 31s
Fikset koordinatrekkefoelgen (x, z, y).
2026-01-24 05:51:11 +01:00
David Kaasen
3fae06f53f Fikset (kanskje) json-formatet for Mapcrafter.
All checks were successful
Build maps / build-mapcrafter (push) Successful in 34s
Build maps / build-bluemap (push) Successful in 34s
2026-01-24 05:19:04 +01:00
040294f2e1 overworld/tunnels: add southern part of danio's tunnel
All checks were successful
Build maps / build-bluemap (push) Successful in 33s
Build maps / build-mapcrafter (push) Successful in 1m23s
2026-01-19 00:16:14 +09:00
a74844b877 cli/validator: fix minimum y value
All checks were successful
Build maps / build-mapcrafter (push) Successful in 31s
Build maps / build-bluemap (push) Successful in 1m44s
2026-01-17 03:58:07 +09:00
db319f65e6 Add a bunch of more railways and tunnels 2026-01-17 03:58:07 +09:00
David Kaasen
2112ddd214 Merge branch 'main' of https://git.pvv.ntnu.no/Projects/minecraft-kartverket
All checks were successful
Build maps / build-mapcrafter (push) Successful in 31s
Build maps / build-bluemap (push) Successful in 34s
Flettet inn endringer.
2026-01-16 18:54:28 +01:00
73a818de79 Add a bunch of more railways
All checks were successful
Build maps / build-mapcrafter (push) Successful in 34s
Build maps / build-bluemap (push) Successful in 1m11s
2026-01-17 02:28:42 +09:00
David Kaasen
20babc9d50 Oppdaterte med navn fra isvegg. 2026-01-16 17:44:07 +01:00
3bc8867f06 validate: fix track and duplicate point validation
All checks were successful
Build maps / build-mapcrafter (push) Successful in 32s
Build maps / build-bluemap (push) Successful in 1m30s
2026-01-16 19:49:30 +09:00
83c7740a55 overworld/railways: add some more railways 2026-01-16 19:49:02 +09:00
1ca8495fa2 LICENSE: init
All checks were successful
Build maps / build-bluemap (push) Successful in 30s
Build maps / build-mapcrafter (push) Successful in 41s
2026-01-15 00:05:46 +09:00
b0c855dc43 README: misc cleanup
All checks were successful
Build maps / build-bluemap (push) Successful in 33s
Build maps / build-mapcrafter (push) Successful in 45s
2026-01-14 18:00:29 +09:00
584fd6379c flake.nix: system -> stdenv.hostPlatform.system
All checks were successful
Build maps / build-mapcrafter (push) Successful in 31s
Build maps / build-bluemap (push) Successful in 29s
2026-01-09 06:10:53 +09:00
6fae27b165 bluemap: fix exporter marker sets
All checks were successful
Build maps / build-bluemap (push) Successful in 29s
Build maps / build-mapcrafter (push) Successful in 32s
The earlier version would put the markers outside the marker set
2025-12-17 02:04:43 +09:00
d3bb5c7e8c overworld/area_names: add 'Nordørkenen' 2025-12-17 02:02:18 +09:00
7c86d342e6 README: add links to map export artifacts
All checks were successful
Build maps / build-bluemap (push) Successful in 30s
Build maps / build-mapcrafter (push) Successful in 40s
2025-12-17 01:46:29 +09:00
1af4bbc330 .gitea/workflows: upload map exports as artifacts
All checks were successful
Build maps / build-bluemap (push) Successful in 29s
Build maps / build-mapcrafter (push) Successful in 30s
2025-12-17 01:42:40 +09:00
be638b2ade Implement track export for bluemap, ignore for mapcrafter
All checks were successful
Build maps / build-mapcrafter (push) Successful in 28s
Build maps / build-bluemap (push) Successful in 29s
2025-12-17 01:36:07 +09:00
1b3a7d0e90 Add two railways
Some checks failed
Build maps / build-mapcrafter (push) Failing after 19s
Build maps / build-bluemap (push) Failing after 22s
2025-12-17 01:25:21 +09:00
89e9d8827c Add missing points from mapcrafter
All checks were successful
Build maps / build-bluemap (push) Successful in 30s
Build maps / build-mapcrafter (push) Successful in 1m8s
2025-12-17 00:50:59 +09:00
08507b8d53 README: improve sections on getting started
All checks were successful
Build maps / build-bluemap (push) Successful in 29s
Build maps / build-mapcrafter (push) Successful in 38s
2025-12-08 22:29:38 +09:00
30e1aabde9 Makefile: init
All checks were successful
Build maps / build-mapcrafter (push) Successful in 29s
Build maps / build-bluemap (push) Successful in 31s
2025-12-08 22:20:07 +09:00
1168d37d7e .gitea/workflows: add workflow for building map exports
Some checks failed
Build maps / build-mapcrafter (push) Failing after 28s
Build maps / build-bluemap (push) Successful in 27s
2025-12-08 22:02:39 +09:00
c20520b400 flake.lock: bump, uv.lock: bump 2025-12-08 18:57:39 +09:00
6a6a3b6c68 rewrite-in-python: more nix validation, fix eval warning 2025-11-11 22:30:37 +09:00
3d5f5ea9d8 rewrite-in-python: flatten marker sets 2025-11-11 22:30:37 +09:00
bf012ea24f rewrite-to-python: add test for hocon includes, fix generator bug 2025-11-11 22:30:36 +09:00
0a22ae5bfa rewrite-to-python: fix gitignore 2025-11-11 22:30:36 +09:00
f585d8fe67 rewrite-to-python: flatten structure 2025-11-11 22:30:36 +09:00
587d727220 rewrite to python: redo nix packaging 2025-11-11 22:30:36 +09:00
974bd1e7f8 rewrite to python: remove old map markers 2025-11-11 22:30:36 +09:00
86c98a6d2f rewrite to python: export to bluemap 2025-11-11 22:30:35 +09:00
65c7fe2371 rewrite to python: add section about adding marker sets 2025-11-11 22:30:35 +09:00
025475f554 rewrite to python: fix oopsies reported by validator 2025-11-11 22:30:35 +09:00
aada4fc897 rewrite to python: implement validator 2025-11-11 22:30:35 +09:00
2b0a171663 rewrite in python: export to mapcrafter 2025-11-11 22:30:35 +09:00
12a36f96ce rewrite in python: recreate all points and structure 2025-11-11 22:30:34 +09:00
61 changed files with 2906 additions and 528 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

View File

@@ -0,0 +1,93 @@
name: "Build maps"
on:
pull_request:
push:
jobs:
build-mapcrafter:
runs-on: debian-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Build map
run: uv run mckart export-mapcrafter
# - name: Setup Biome
# uses: biomejs/setup-biome@v2
# - name: Format map
# run: biome format ./mapcrafter/* --write
- name: Transfer results
uses: https://git.pvv.ntnu.no/Projects/rsync-action@v1
with:
source: ./mapcrafter/
target: ${{ gitea.ref_name }}/mapcrafter/
username: gitea-web
ssh-key: ${{ secrets.WEB_SYNC_SSH_KEY }}
host: pages.pvv.ntnu.no
known-hosts: "pages.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2QjfFB+city1SYqltkVqWACfo1j37k+oQQfj13mtgg"
- name: Upload deb package artifact
uses: actions/upload-artifact@v3
with:
name: minecraft-kartverket-mapcrafter-export-${{ gitea.sha }}.zip
path: ./mapcrafter/
if-no-files-found: error
retention-days: 30
compression: 7
build-bluemap:
runs-on: debian-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Build map
run: uv run mckart export-bluemap
- name: Transfer results
uses: https://git.pvv.ntnu.no/Projects/rsync-action@v1
with:
source: ./bluemap/
target: ${{ gitea.ref_name }}/bluemap/
username: gitea-web
ssh-key: ${{ secrets.WEB_SYNC_SSH_KEY }}
host: pages.pvv.ntnu.no
known-hosts: "pages.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2QjfFB+city1SYqltkVqWACfo1j37k+oQQfj13mtgg"
- name: Upload deb package artifact
uses: actions/upload-artifact@v3
with:
name: minecraft-kartverket-bluemap-export-${{ gitea.sha }}.zip
path: ./bluemap/
if-no-files-found: error
retention-days: 30
compression: 7
validate:
runs-on: debian-latest
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Validate map data
run: uv run mckart validate
# build-nix:
# runs-on: debian-latest
# steps:
# - name: Install sudo
# run: apt-get update && apt-get -y install sudo
# - name: Install nix
# uses: https://github.com/cachix/install-nix-action@v31
# - name: Configure nix
# run: echo -e "show-trace = true\nmax-jobs = auto\ntrusted-users = root\nexperimental-features = nix-command flakes\nbuild-users-group =" > /etc/nix/nix.conf

16
.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
# Python
*.pyc
*.pyo
build/
dist/
.venv
__pycache__
# Nix
result
result-*
# Ignore generated files by default
/mapcrafter
/bluemap
/out

28
LICENSE Normal file
View File

@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2026, Programvareverkstedet <projects@pvv.ntnu.no>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

20
Makefile Normal file
View File

@@ -0,0 +1,20 @@
.PHONY: all clean validate
all: bluemap mapcrafter
export PYTHONPATH := $(CURDIR)/src/
OUT := $(CURDIR)/out
validate:
python -m cli.main validate
bluemap: validate
python -m cli.main export-bluemap --output-dir "$(OUT)/bluemap"
mapcrafter: validate
python -m cli.main export-mapcrafter --output-dir "$(OUT)/mapcrafter"
clean:
rm -rf ./bluemap
rm -rf ./mapcrafter

207
README.md Normal file
View File

@@ -0,0 +1,207 @@
# minecraft-kartverket
Map markers for PVV's minecraft server
## What is this?
This project is mainly a collection of markers for PVV's minecraft server.
It is meant to let PVV members contribute map data for PVV's minecraft server, like coordinates for bases, cities, and other points of interest.
These markers are written in python so that we can do things like generating points in a loop, calculating distances, and other types of automation.
The codebase includes a CLI tool that we use to export this data into the formats of several minecraft map implementations.
Grab the latest exports here:
- Bluemap:
- https://pages.pvv.ntnu.no/Projects/minecraft-kartverket/main/bluemap/overworld.hocon
- https://pages.pvv.ntnu.no/Projects/minecraft-kartverket/main/bluemap/nether.hocon
- https://pages.pvv.ntnu.no/Projects/minecraft-kartverket/main/bluemap/the-end.hocon
- Mapcrafter:
- https://pages.pvv.ntnu.no/Projects/minecraft-kartverket/main/mapcrafter/markers.js
## Marker sets
Here is an overview of the different marker sets, and a short description of what goes where:
### Overworld
| Directory | Description |
|----------------|-------------|
| Area Names | Large named areas or biomes |
| Buildings | Important buildings and world wonders |
| Cities | An area that consist of several bases and/or public infrastructure |
| Homes | Single bases consisting of 1-3 people |
| Huts | Small huts, meant for visitors and travelers |
| Infrastructure | Public infrastructure, like bridges, sea ports, tunnels, etc. |
| Mines | Public mines |
| Nature | Similar to `Area Names`, but meant for mostly untouched nature reserves or national parks. |
| Other | Anything that doesn't fit into the other categories |
| Railways | Railways, train tracks for transportation |
| Roads | Roads and paths, for walking |
| Tunnels | Underground passages, usually for transportation |
| Villages | Villages generated by minecraft (player made cities go in `Cities`) |
### Nether
| Directory | Description |
|------------|-------------|
| Ice Tracks | Ice tracks meant for highspeed travel with boats |
| Other | Anything that doesn't fit into the other categories |
| Portals | Nether portals that lead back to the overworld |
| Railways | Railways, train tracks for transportation |
### The End
| Directory | Description |
|------------|-------------|
| Other | Anything that doesn't fit into the other categories |
| Portals | End portals that send you back to the center of the map |
## How to run the python code
> [!NOTE]
> There should be no external dependencies required to run the code.
> Please keep it that way.
You can either use `uv`, `nix` or the bundled `Makefile` with a bare python installation to run the project.
### Using uv
If you have [uv](https://docs.astral.sh/uv/) installed, running the code is as easy as executing
```bash
uv run mckart <args>
```
### Using the Makefile
The makefile will validate the map data, and generate both types of map exports in a directory named `out`
You can invoke it with:
```bash
make
ls out
```
### Using nix
```bash
# Run the cli tool
nix run .# <args>
# Build the exports
nix build .#bluemap-export
nix build .#mapcrafter-export
# Get a python environment with development tooling
nix develop
```
> [!NOTE]
> In the following sections of the readme,
> we will assume that you are using `uv`.
> But the other methods can also be adapted to the instructions.
## How to add a point marker
1. Find the correct marker set file in `src/marker_sets/<world>`.
2. Add the marker to the list in the file. You can look at neighbouring markers for inspiration.
3. Run the cli tool to verify that the marker has been added correctly.
```bash
uv run mckart validate
uv run mckart print
```
4. Open a pull request at https://git.pvv.ntnu.no/Projects/minecraft-kartverket/pulls
## How to add a railway / road
This is very similar to adding a point marker, but the content of the marker is different.
You can follow the previous guide, but for step 3, use this template or look at other markers for inspiration.
```python
from lib_marker import Track
MARKERS = [
...,
Track(
name="Nordbanen",
points = [
(848, 70, 1583),
(920, 70, 1583),
(920, 70, 1200),
...
],
),
...,
]
```
## How to add a new marker set
To add a new marker set, you need to:
1. Create a new file the other marker sets in `src/marker_sets/<world>`.
2. Create a marker set in this file, using this template:
```python
from lib_marker.marker_set import MarkerSet
MARKER_SET = MarkerSet(
name="My Marker Set",
markers=[
],
)
```
3. Add the marker set to the `MARKER_SETS` list in `src/marker_sets/<world>/__init__.py`.
```python
from .<marker_set_name> import MARKER_SET as <marker_set_name>_marker_set
MARKER_SETS = [
...
portals_marker_set,
]
```
See the other marker sets for inspiration.
4. Add the marker set with a description to this README file.
## Making exceptions for the validation tool
Sometimes, you might want to add a marker that doesn't pass validation.
You can do this by setting the `skip_validation` parameter to `True` when creating the marker.
```python
from lib_marker import Point, Track
MARKERS = [
...,
Point(
name="My Invalid Marker",
x=100,
y=200,
z=300,
skip_validation=True,
),
Track(
name="My Invalid Track",
points=[
(0, 64, 0),
(10000, 64, 10000),
],
skip_validation=True,
),
...,
]
```
## See also
- [BlueMap documentation for markers](https://bluemap.bluecolored.de/wiki/customization/Markers.html)
- [Mapcrafter documentation for markers](https://mapcrafter.readthedocs.io/en/latest/markers.html)

27
flake.lock generated Normal file
View File

@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1764947035,
"narHash": "sha256-EYHSjVM4Ox4lvCXUMiKKs2vETUSL5mx+J2FfutM7T9w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a672be65651c80d3f592a89b3945466584a22069",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

123
flake.nix
View File

@@ -1,7 +1,122 @@
{ {
inputs = { }; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
outputs = inputs@{ self, ...}: {
map-markers = import ./map-markers; outputs = { self, nixpkgs }: let
map-lib = import ./map-markers/lib.nix; systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: let
pkgs = nixpkgs.legacyPackages.${system};
in f system pkgs);
inherit (nixpkgs) lib;
in {
apps = forAllSystems (system: pkgs: {
default = {
type = "app";
program = lib.getExe self.packages.${system}.default;
meta.description = "The 'mckart' cli tool with all map data embedded";
};
});
devShells = forAllSystems (_: pkgs: {
default = pkgs.mkShell {
packages = with pkgs; [
uv
ruff
python3
# (python3.withPackages (ps: with ps; []))
biome
];
};
});
overlays.default = final: prev: self.packages.${final.stdenv.hostPlatform.system};
checks = forAllSystems (system: pkgs: {
inherit (self.packages.${system}) bluemap-export mapcrafter-export;
hocon-include = let
format = pkgs.formats.hocon { };
in format.generate "minecraft-kartverket-test-hocon-include" {
_includes = [
(format.lib.mkInclude "${self.packages.${system}.bluemap-export}/overworld.hocon")
(format.lib.mkInclude "${self.packages.${system}.bluemap-export}/nether.hocon")
(format.lib.mkInclude "${self.packages.${system}.bluemap-export}/the-end.hocon")
];
};
});
packages = forAllSystems (system: pkgs: {
default = self.packages.${system}.mckart;
mckart = let
pyproject = builtins.fromTOML (builtins.readFile ./pyproject.toml);
in with pkgs.python3Packages; buildPythonPackage {
pname = pyproject.project.name;
version = pyproject.project.version;
src = lib.cleanSource ./.;
format = "pyproject";
build-system = [ hatchling ];
dependencies = [];
meta.mainProgram = "mckart";
};
bluemap-export = let
# from pkgs/pkgs-lib/formats/hocon/default.nix
hocon-validator =
pkgs.writers.writePython3Bin "hocon-validator"
{
libraries = [ pkgs.python3Packages.pyhocon ];
}
''
from sys import argv
from pyhocon import ConfigFactory
if not len(argv) == 2:
print("USAGE: hocon-validator <file>")
ConfigFactory.parse_file(argv[1])
'';
in pkgs.runCommand "bluemap-export" {
nativeBuildInputs = [
(self.packages.${system}.mckart)
hocon-validator
];
} ''
mckart export-bluemap --output-dir "$out"
for file in "$out"/*.hocon; do
hocon-validator "$file"
done
'';
mapcrafter-export = pkgs.runCommand "mapcrafter-export" {
buildInputs = [
(self.packages.${system}.mckart)
pkgs.biome
];
biomeConf = builtins.toJSON {
formatter = {
indentStyle = "space";
lineWidth = 100;
};
};
passAsFile = [ "biomeConf" ];
} ''
mckart export-mapcrafter --output-dir "$out"
cp "$biomeConfPath" ./biome.json
biome format --config-path=./biome.json "$out"/* --write
'';
});
}; };
} }

0
icons/.gitkeep Normal file
View File

View File

@@ -1,6 +0,0 @@
let
lib = import ./lib.nix;
icons = import ./icons;
in {
vanillaSurvival = import ./vanillaSurvival { inherit lib icons; };
}

View File

@@ -1,11 +0,0 @@
rec {
mkPoi = label: pos: attr: {
type = "poi";
position = {
x = builtins.elemAt pos 0;
y = builtins.elemAt pos 2;
z = builtins.elemAt pos 1;
};
label = label;
} // attr;
}

View File

@@ -1,380 +0,0 @@
var MAPCRAFTER_MARKERS = [
{
"id" : "byer",
"name" : "Byer og steder",
"showDefault" : true,
"markers" : {
"verden" : [
// Sorter markoerene etter lengdegrad.
{
"pos" : [-2204, 447, 64],
"title" : "Vestlandet",
},
{
"pos" : [-1818, 98, 64],
"title" : "Vestsumpland",
},
{
"pos" : [-1760, -2440, 64],
"title" : "Smedby",
},
{
"pos" : [-1654, -654, 64],
"title" : "Snøklippan",
},
{
"pos" : [-1563, -1966, 64],
"title" : "Dypskiferhytta",
},
{
"pos" : [-1189, 1556, 75],
"title" : "England",
},
{
"pos" : [-980, 4090, 64],
"title" : "Hemmelig mesabiom",
},
{
"pos" : [-800, 0, 64],
"title" : "Vestisødet",
},
{
"pos" : [-378, 153, 64],
"title" : "Isjungelkatedralen",
},
{
"pos" : [-370, 2390, 64],
"title" : "Svartskoghavn",
},
{
"pos" : [-334, 473, 116],
"title" : "Kystbasen",
},
{
"pos" : [-305, 535, 64],
"title" : "Langstrand",
},
{
"pos" : [-220, 2890, 64],
"title" : "Summefjord",
},
{
"pos" : [-206, -35, 64],
"title" : "Slimegruva",
},
{
"pos" : [-160, 1480, 64],
"title" : "Turrikkelmyra",
},
{
"pos" : [-128, -1460, 64],
"title" : "Akasienborg",
},
{
"pos" : [-110, 3465, 64],
"title" : "Summevatn",
},
{
"pos" : [-99, 1025, 99],
"title" : "Tegltårnet",
},
{
"pos" : [-54, 234, 64],
"title" : "Huløyeid",
},
{
"pos" : [-47, 146, 64],
"title" : "Trangdalen",
},
{
"pos" : [-22, 67, 76],
"title" : "Tårnodden",
},
{
"pos" : [0, -485, 67],
"title" : "Nordørkenen",
},
{
"pos" : [4, 800, 64],
"title" : "Naturreservat",
},
{
"pos" : [56, -266, 67],
"title" : "Sandsteingruva",
},
{
"pos" : [58, -2860, 63],
"title" : "Tangen",
},
{
"pos" : [77, -348, 69],
"title" : "Smalelvbro",
},
{
"pos" : [100, -3830, 64],
"title" : "Ittoqqortoormiit",
},
{
"pos" : [102, 37, 77],
"title" : "Bjerkepalasset",
},
{
"pos" : [150, 80, 64],
"title" : "Piratbukta",
},
{
"pos" : [177, 177, 64],
"title" : "Heimfjell",
},
{
"pos" : [240, 3070, 64],
"title" : "Hulevatna",
},
{
"pos" : [267, -496, 89],
"title" : "Hodeskallegrotten",
},
{
"pos" : [280, -2800, 64],
"title" : "Jesus Christ Superstore",
},
{
"pos" : [337, -853, 64],
"title" : "NPC-landsby nord",
},
{
"pos" : [360, -460, 64],
"title" : "Vikingelandsbyen",
},
{
"pos" : [473, -3043, 78],
"title" : "Bjørnebukten",
},
{
"pos" : [512, -3320, 64],
"title" : "Leirhavn",
},
{
"pos" : [550, -1480, 64],
"title" : "Grensebuelvbu",
},
{
"pos" : [675, 362, 64],
"title" : "Storvatnet",
},
{
"pos" : [695, 2812, 64],
"title" : "Kløfteby",
},
{
"pos" : [723, -2380, 64],
"title" : "Isbjørnby",
},
{
"pos" : [730, -1900, 64],
"title" : "Lille isbjørnelv",
},
{
"pos" : [800, -3300, 64],
"title" : "Gammelleirnes",
},
{
"pos" : [834, -3089, 65],
"title" : "Biosfæren",
},
{
"pos" : [848, 1583, 70],
"title" : "Nyverdenhytta",
},
{
"pos" : [850, -2130, 64],
"title" : "Store isbjørnelv",
},
{
"pos" : [874, 160, 65],
"title" : "Blindodden",
},
{
"pos" : [910, 3050, 64],
"title" : "Ulveelva",
},
{
"pos" : [960, 460, 64],
"title" : "Tosjødalen",
},
{
"pos" : [1005, 1335, 64],
"title" : "Storhavskanalen",
},
{
"pos" : [1278, -397, 64],
"title" : "NPC-landsby",
},
{
"pos" : [1570, 3285, 64],
"title" : "SuperTheodors fiskehus",
},
{
"pos" : [1810, -1630, 64],
"title" : "Lamafjellet",
},
{
"pos" : [2111, 3777, 64],
"title" : "SuperTheodors strand",
},
{
"pos" : [2300, 250, 64],
"title" : "Andøya",
},
{
"pos" : [2350, -136, 64],
"title" : "Grantrebukta",
},
{
"pos" : [2734, -984, 64],
"title" : "Hestelandsby",
},
{
"pos" : [2950, 270, 64],
"title" : "Bikkjeby",
},
{
"pos" : [3000, 1000, 64],
"title" : "Storhavet i øst",
},
{
"pos" : [3085, 424, 64],
"title" : "Fyret",
},
{
"pos" : [3481, 210, 75],
"title" : "Vinterstranda hotel & resort",
},
{
"pos" : [4468, 1337, 64],
"title" : "Holmgard",
},
{
"pos" : [4630, 270, 64],
"title" : "Der Kölner Dom",
},
{
"pos" : [5470, -270, 64],
"title" : "Storsnøfjella",
},
{
"pos" : [6200, -300, 64],
"title" : "Bondeby",
},
{
"pos" : [6880, -465, 64],
"title" : "Kirkemyr",
},
],
"underverden" : [
// Sorter markoerene etter lengdegrad.
{
"pos" : [-900, 27, 64],
"title" : "Grisebukta",
},
{
"pos" : [-120, 510, 64],
"title" : "Hemmelig mesabiom, ca.",
},
{
"pos" : [120, -420, 64],
"title" : "Biosfæren, ca.",
},
{
"pos" : [520, 220, 64],
"title" : "Holmgard, ca.",
},
],
},
},
{
"id" : "spesial",
"name" : "Spesielle steder",
"showDefault" : true,
"icon" : "marker-icon-green.png",
"iconSize" : [25, 41],
"markers" : {
"verden" : [
{
"pos" : [0, 0, 64],
"title" : "Nullpunkt",
},
],
"underverden" : [
{
"pos" : [0, 0, 64],
"title" : "Nullpunkt",
},
],
},
},
{
//
// Tegner et rutenett som viser regiongrensene, og
// koordinatene for hver region.
//
"id" : "regioner",
"name" : "Regioner",
"showDefault" : false,
"createMarker" : function(ui, groupInfo, markerInfo) {
var rmax = 15;
var objekter = [];
var multilatlngs = [];
for (var rx = -rmax; rx < rmax; rx++) {
var latlngs = [];
var miny = -rmax*512;
var maxy = rmax*512;
var x = rx*512;
// use the ui.mcToLatLng-function to convert Minecraft coords to LatLngs
latlngs.push(ui.mcToLatLng(x, miny, 64));
latlngs.push(ui.mcToLatLng(x, maxy, 64));
multilatlngs.push(latlngs);
}
for (var ry = -rmax; ry < rmax; ry++) {
var latlngs = [];
var minx = -rmax*512;
var maxx = rmax*512;
var y = ry*512;
// use the ui.mcToLatLng-function to convert Minecraft coords to LatLngs
latlngs.push(ui.mcToLatLng(minx, y, 64));
latlngs.push(ui.mcToLatLng(maxx, y, 64));
multilatlngs.push(latlngs);
}
for (var rx = -rmax; rx < rmax; rx++) {
for (var ry = -rmax; ry < rmax; ry++) {
var x = rx*512 + 256;
var y = ry*512 + 256;
var myIcon = L.divIcon({iconSize: L.point(55, 20), html: "<center>r." + String(rx) + "." + String(ry) + "</center>"});
var myMarker = L.marker(ui.mcToLatLng(x, y, 64), {icon: myIcon});
objekter.push(myMarker);
}
}
var linjer = [];
if (L.version.startsWith("0.7.")) {
linjer = L.multiPolyline(multilatlngs, {"color" : markerInfo.color});
} else {
linjer = L.polyline(multilatlngs, {"color" : markerInfo.color});
}
objekter.push(linjer);
return L.layerGroup(objekter);
},
"markers" : {
"verden" : [
{
"color" : "yellow",
},
],
},
},
];

View File

@@ -1,7 +0,0 @@
{ lib, icons, ... }:
{
verden = import ./verden { inherit lib icons; };
underverden = import ./underverden { inherit lib icons; };
# enden = import ./enden { inherit lib icons; };
}

View File

@@ -1,8 +0,0 @@
{ lib, icons }:
{
"Grisebukta" = lib.mkPoi "Grisebukta" [(-900) (27) (64)] { };
"Hemmelig mesabiom, ca." = lib.mkPoi "Hemmelig mesabiom, ca." [(-120) (510) (64)] { };
"Biosfæren, ca." = lib.mkPoi "Biosfæren, ca." [(120) (-420) (64)] { };
"Holmgard, ca." = lib.mkPoi "Holmgard, ca." [(520) (220) (64)] { };
}

View File

@@ -1,6 +0,0 @@
{ lib, icons }:
{
byer = import ./byer.nix { inherit lib icons; };
spesial = import ./spesial.nix { inherit lib icons; };
}

View File

@@ -1,12 +0,0 @@
{ lib, icons }:
{
label = "Spesielle steder";
toggleable = true;
default-hidden = false;
sorting = 100;
markers = {
nullpunkt = lib.mkPoi "Nullpunkt" [0 0 64] { /*icon = icons.green*/ };
};
}

View File

@@ -1,76 +0,0 @@
{ lib, ... }:
{
label = "Byer og steder";
toggleable = true;
default-hidden = false;
sorting = 0;
markers = {
"Vestlandet" = lib.mkPoi "Vestlandet" [(-2204) (447) (64)] { };
"Vestsumpland" = lib.mkPoi "Vestsumpland" [(-1818) (98) (64)] { };
"Smedby" = lib.mkPoi "Smedby" [(-1760) (-2440) (64)] { };
"Snøklippan" = lib.mkPoi "Snøklippan" [(-1654) (-654) (64)] { };
"Dypskiferhytta" = lib.mkPoi "Dypskiferhytta" [(-1563) (-1966) (64)] { };
"England" = lib.mkPoi "England" [(-1189) (1556) (75)] { };
"Hemmelig mesabiom" = lib.mkPoi "Hemmelig mesabiom" [(-980) (4090) (64)] { };
"Vestisødet" = lib.mkPoi "Vestisødet" [(-800) (0) (64)] { };
"Isjungelkatedralen" = lib.mkPoi "Isjungelkatedralen" [(-378) (153) (64)] { };
"Svartskoghavn" = lib.mkPoi "Svartskoghavn" [(-370) (2390) (64)] { };
"Kystbasen" = lib.mkPoi "Kystbasen" [(-334) (473) (116)] { };
"Langstrand" = lib.mkPoi "Langstrand" [(-305) (535) (64)] { };
"Summefjord" = lib.mkPoi "Summefjord" [(-220) (2890) (64)] { };
"Slimegruva" = lib.mkPoi "Slimegruva" [(-206) (-35) (64)] { };
"Turrikkelmyra" = lib.mkPoi "Turrikkelmyra" [(-160) (1480) (64)] { };
"Akasienborg" = lib.mkPoi "Akasienborg" [(-128) (-1460) (64)] { };
"Summevatn" = lib.mkPoi "Summevatn" [(-110) (3465) (64)] { };
"Tegltårnet" = lib.mkPoi "Tegltårnet" [(-99) (1025) (99)] { };
"Huløyeid" = lib.mkPoi "Huløyeid" [(-54) (234) (64)] { };
"Trangdalen" = lib.mkPoi "Trangdalen" [(-47) (146) (64)] { };
"Tårnodden" = lib.mkPoi "Tårnodden" [(-22) (67) (76)] { };
"Nordørkenen" = lib.mkPoi "Nordørkenen" [(0) (-485) (67)] { };
"Naturreservat" = lib.mkPoi "Naturreservat" [(4) (800) (64)] { };
"Sandsteingruva" = lib.mkPoi "Sandsteingruva" [(56) (-266) (67)] { };
"Tangen" = lib.mkPoi "Tangen" [(58) (-2860) (63)] { };
"Smalelvbro" = lib.mkPoi "Smalelvbro" [(77) (-348) (69)] { };
"Ittoqqortoormiit" = lib.mkPoi "Ittoqqortoormiit" [(100) (-3830) (64)] { };
"Bjerkepalasset" = lib.mkPoi "Bjerkepalasset" [(102) (37) (77)] { };
"Piratbukta" = lib.mkPoi "Piratbukta" [(150) (80) (64)] { };
"Heimfjell" = lib.mkPoi "Heimfjell" [(177) (177) (64)] { };
"Hulevatna" = lib.mkPoi "Hulevatna" [(240) (3070) (64)] { };
"Hodeskallegrotten" = lib.mkPoi "Hodeskallegrotten" [(267) (-496) (89)] { };
"Jesus Christ Superstore" = lib.mkPoi "Jesus Christ Superstore" [(280) (-2800) (64)] { };
"NPC-landsby nord" = lib.mkPoi "NPC-landsby nord" [(337) (-853) (64)] { };
"Vikingelandsbyen" = lib.mkPoi "Vikingelandsbyen" [(360) (-460) (64)] { };
"Bjørnebukten" = lib.mkPoi "Bjørnebukten" [(473) (-3043) (78)] { };
"Leirhavn" = lib.mkPoi "Leirhavn" [(512) (-3320) (64)] { };
"Grensebuelvbu" = lib.mkPoi "Grensebuelvbu" [(550) (-1480) (64)] { };
"Storvatnet" = lib.mkPoi "Storvatnet" [(675) (362) (64)] { };
"Kløfteby" = lib.mkPoi "Kløfteby" [(695) (2812) (64)] { };
"Isbjørnby" = lib.mkPoi "Isbjørnby" [(723) (-2380) (64)] { };
"Lille isbjørnelv" = lib.mkPoi "Lille isbjørnelv" [(730) (-1900) (64)] { };
"Gammelleirnes" = lib.mkPoi "Gammelleirnes" [(800) (-3300) (64)] { };
"Biosfæren" = lib.mkPoi "Biosfæren" [(834) (-3089) (65)] { };
"Nyverdenhytta" = lib.mkPoi "Nyverdenhytta" [(848) (1583) (70)] { };
"Store isbjørnelv" = lib.mkPoi "Store isbjørnelv" [(850) (-2130) (64)] { };
"Blindodden" = lib.mkPoi "Blindodden" [(874) (160) (65)] { };
"Ulveelva" = lib.mkPoi "Ulveelva" [(910) (3050) (64)] { };
"Tosjødalen" = lib.mkPoi "Tosjødalen" [(960) (460) (64)] { };
"Storhavskanalen" = lib.mkPoi "Storhavskanalen" [(1005) (1335) (64)] { };
"NPC-landsby" = lib.mkPoi "NPC-landsby" [(1278) (-397) (64)] { };
"SuperTheodors fiskehus" = lib.mkPoi "SuperTheodors fiskehus" [(1570) (3285) (64)] { };
"Lamafjellet" = lib.mkPoi "Lamafjellet" [(1810) (-1630) (64)] { };
"SuperTheodors strand" = lib.mkPoi "SuperTheodors strand" [(2111) (3777) (64)] { };
"Andøya" = lib.mkPoi "Andøya" [(2300) (250) (64)] { };
"Grantrebukta" = lib.mkPoi "Grantrebukta" [(2350) (-136) (64)] { };
"Hestelandsby" = lib.mkPoi "Hestelandsby" [(2734) (-984) (64)] { };
"Bikkjeby" = lib.mkPoi "Bikkjeby" [(2950) (270) (64)] { };
"Storhavet i øst" = lib.mkPoi "Storhavet i øst" [(3000) (1000) (64)] { };
"Fyret" = lib.mkPoi "Fyret" [(3085) (424) (64)] { };
"Vinterstranda hotel & resort" = lib.mkPoi "Vinterstranda hotel & resort" [(3481) (210) (75)] { };
"Holmgard" = lib.mkPoi "Holmgard" [(4468) (1337) (64)] { };
"Der Kölner Dom" = lib.mkPoi "Der Kölner Dom" [(4630) (270) (64)] { };
"Storsnøfjella" = lib.mkPoi "Storsnøfjella" [(5470) (-270) (64)] { };
"Bondeby" = lib.mkPoi "Bondeby" [(6200) (-300) (64)] { };
"Kirkemyr" = lib.mkPoi "Kirkemyr" [(6880) (-465) (64)] { };
};
}

View File

@@ -1,6 +0,0 @@
{ lib, icons }:
{
byer = import ./byer.nix { inherit lib icons; };
spesial = import ./spesial.nix { inherit lib icons; };
}

View File

@@ -1,12 +0,0 @@
{ lib, icons }:
{
label = "Spesielle steder";
toggleable = true;
default-hidden = false;
sorting = 100;
markers = {
nullpunkt = lib.mkPoi "Nullpunkt" [0 0 64] { /*icon = icons.green*/ };
};
}

26
pyproject.toml Normal file
View File

@@ -0,0 +1,26 @@
[project]
name = "minecraft-kartverket"
version = "0.1.0"
description = "Map data for our favourite minecraft server"
readme = "README.md"
authors = [
{ name = "pvv", email = "projects@pvv.ntnu.no" }
]
requires-python = ">=3.12"
dependencies = []
[project.scripts]
mckart = "cli.main:main"
[tool.hatch.build.targets.wheel]
packages = [
"src/cli",
"src/lib_marker",
"src/bluemap_exporter",
"src/mapcrafter_exporter",
"src/marker_sets",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

View File

@@ -0,0 +1,3 @@
from .exporter import generate_bluemap_output
__all__ = ["generate_bluemap_output"]

View File

@@ -0,0 +1,98 @@
import json
from pathlib import Path
from textwrap import indent
from typing import Any
from lib_marker import Point, Track
from marker_sets import WORLDS
def generate_bluemap_output(output_dir: Path):
output_dir.mkdir(parents=True, exist_ok=True)
data = convert_data_to_bluemap_structure()
for world_name, marker_sets in data.items():
with open(output_dir / f"{slug(world_name)}.hocon", "w") as f:
f.write(to_hocon(marker_sets))
def slug(s: str) -> str:
return (
s.lower()
.replace(" ", "-")
.replace("ø", "o")
.replace("æ", "ae")
.replace("å", "a")
)
def convert_data_to_bluemap_structure() -> dict:
result = {}
for world_name, marker_sets in WORLDS.items():
result[world_name] = {}
for marker_set in marker_sets:
result[world_name][slug(marker_set.name)] = {
"label": marker_set.name,
"toggleable": True,
"default-hidden": not marker_set.show_by_default,
"markers": {},
}
for marker in marker_set.markers:
poi_markers = result[world_name][slug(marker_set.name)]["markers"]
track_markers = result[world_name][slug(marker_set.name)]["markers"]
if isinstance(marker, Point):
# https://bluemap.bluecolored.de/wiki/customization/Markers.html#poi-markers
poi_markers[slug(marker.name)] = {
"type": "poi",
"label": marker.name,
"position": {
"x": marker.x,
"y": marker.y,
"z": marker.z,
},
}
elif isinstance(marker, Track):
# https://bluemap.bluecolored.de/wiki/customization/Markers.html#line-markers
track_markers[slug(marker.name)] = {
"type": "line",
# TODO: Calculate the median point of the track for the label position
"position": {
"x": marker.points[0][0],
"y": marker.points[0][1],
"z": marker.points[0][2],
},
"label": marker.name,
"line": [
{"x": p[0], "y": p[1], "z": p[2]} for p in marker.points
],
}
else:
raise NotImplementedError(f"Unknown marker type: {marker}")
return result
def to_hocon(x: Any) -> str:
if isinstance(x, str):
return json.dumps(x, ensure_ascii=False)
elif isinstance(x, int):
return str(x)
elif isinstance(x, float):
return str(x)
elif isinstance(x, bool):
return str(x).lower()
elif isinstance(x, list):
items = [indent(to_hocon(y), " ") for y in x]
return f"[\n{'\n'.join(items)}\n]"
elif isinstance(x, dict):
items = [
f' "{k}": {indent(to_hocon(v), " ").removeprefix(" ")}'
for k, v in x.items()
]
return f"{{\n{'\n'.join(items)}\n}}"
else:
raise ValueError(f"Unknown type: {x}")

0
src/cli/__init__.py Normal file
View File

65
src/cli/main.py Normal file
View File

@@ -0,0 +1,65 @@
import argparse
from pathlib import Path
from .print import main as pretty_print_marker_data
from .validate import main as validate_marker_data
from mapcrafter_exporter import generate_mapcrafter_output
from bluemap_exporter import generate_bluemap_output
def parse_args():
parser = argparse.ArgumentParser(description="Minecraft map data exporter cli")
subparsers = parser.add_subparsers(dest="command")
ebh_parser = subparsers.add_parser(
"export-bluemap", help="Export map data to Bluemap format"
)
ebh_parser.add_argument(
"--output-dir",
help="Output dir",
type=Path,
metavar="DIR",
default=Path("bluemap"),
)
emj_parser = subparsers.add_parser(
"export-mapcrafter", help="Export map data to Mapcrafter format"
)
emj_parser.add_argument(
"--output-dir",
help="Output dir",
type=Path,
metavar="DIR",
default=Path("mapcrafter"),
)
subparsers.add_parser("validate", help="Validate the map data")
subparsers.add_parser("print", help="Print the map data")
return parser.parse_args()
def main():
args = parse_args()
match args.command:
case "export-bluemap":
generate_bluemap_output(args.output_dir)
case "export-mapcrafter":
generate_mapcrafter_output(args.output_dir)
case "validate":
validate_marker_data()
case "print":
pretty_print_marker_data()
case _:
print("Unknown command")
if __name__ == "__main__":
main()

55
src/cli/print.py Normal file
View File

@@ -0,0 +1,55 @@
from lib_marker import MarkerSet, Marker, Point, Track, Area
from marker_sets import WORLDS
def main() -> None:
tree = format_tree(
"Worlds", [format_world(name, world) for name, world in WORLDS.items()]
)
print(tree)
def format_world(name: str, world: list[MarkerSet]) -> str:
tree = format_tree(name, [format_marker_set(marker_set) for marker_set in world])
return tree
def format_marker_set(marker_set: MarkerSet) -> str:
tree = format_tree(
marker_set.name, [format_marker(marker) for marker in marker_set.markers]
)
return tree
def format_marker(marker: Marker) -> str:
if isinstance(marker, Point):
return format_point_marker(marker)
elif isinstance(marker, Track):
return f"{marker.name} - {marker.points[0]} <--[{len(marker.points)}]--> {marker.points[-1]}"
elif isinstance(marker, Area):
return f"{marker.name} ({len(marker.points)} points)"
else:
raise ValueError(f"Unknown marker type: {marker}")
def format_point_marker(marker: Point) -> str:
return f"{marker.name} ({marker.x}, {marker.y}, {marker.z})"
def format_tree(name: str, items: list[str]) -> str:
result = [name]
for i, item in enumerate(items):
if i == len(items) - 1:
for k, line in enumerate(item.splitlines()):
if k == 0:
result.append(f"└─ {line}")
else:
result.append(f" {line}")
else:
for k, line in enumerate(item.splitlines()):
if k == 0:
result.append(f"├─ {line}")
else:
result.append(f"{line}")
return "\n".join(result)

396
src/cli/validate.py Normal file
View File

@@ -0,0 +1,396 @@
from dataclasses import dataclass
from textwrap import indent
from typing import Any, Callable
from lib_marker import MarkerSet, Point, Track
from marker_sets import WORLDS
def main() -> None:
result_is_ok = True
worlds_to_be_validated = remove_markers_not_to_be_validated(WORLDS)
for test_name, test_f in _tests.items():
results = test_f(worlds_to_be_validated)
if len(results) > 0:
result_is_ok = False
print(f"[X] {test_name}")
print()
for result in results:
print(indent(result.pretty_print(), " "))
print()
else:
print(f"[✓] {test_name}")
if not result_is_ok:
exit(1)
class ValidationError:
error_type: str
world_name: str | None
marker_set_name: str | None
marker_name: str | None
note: str | None
details: dict[str, Any]
def __init__(
self,
error_type: str,
world_name: str | None = None,
marker_set_name: str | None = None,
marker_name: str | None = None,
note: str | None = None,
**details,
):
self.error_type = error_type
self.world_name = world_name
self.marker_set_name = marker_set_name
self.marker_name = marker_name
self.note = note
self.details = details
def pretty_print(self) -> str:
result = []
location_parts = [
x
for x in (self.world_name, self.marker_set_name, self.marker_name)
if x is not None
]
if len(location_parts) > 0:
result.append(f"[{'/'.join(location_parts)}] ")
else:
result.append("[]")
result.append(indent(f"type: {self.error_type}", " "))
if self.note:
result.append(indent(f"note: {self.note.removesuffix('.')}", " "))
for key, value in self.details.items():
result.append(indent(f"{key}: {value}", " "))
return "\n".join(result)
@dataclass
class ValidationErrorPoint:
x: int
y: int
z: int
def __init__(self, x: int, y: int, z: int) -> None:
self.x = x
self.y = y
self.z = z
def __str__(self) -> str:
return f"({self.x}, {self.y}, {self.z})"
def remove_markers_not_to_be_validated(
worlds: dict[str, list[MarkerSet]],
) -> dict[str, list[MarkerSet]]:
result = {}
for world_name, marker_sets in worlds.items():
result[world_name] = []
for marker_set in marker_sets:
new_marker_set = MarkerSet(
name=marker_set.name,
show_by_default=marker_set.show_by_default,
markers=[
marker
for marker in marker_set.markers
if not marker.skip_validation
],
)
result[world_name].append(new_marker_set)
return result
def validate_no_non_included_files(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
# TODO: Implement this function
return []
def validate_no_invalid_y_values(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
result = []
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
for marker in marker_set.markers:
if isinstance(marker, Point):
if marker.y < -64 or marker.y > 255:
result.append(
ValidationError(
error_type="invalid_y_value",
world_name=world_name,
marker_set_name=marker_set.name,
marker_name=marker.name,
y_value=marker.y,
note="Point has coordinate above 255 or below -64.",
)
)
elif isinstance(marker, Track):
for i, point in enumerate(marker.points):
_, y, _ = point
if y < -64 or y > 255:
result.append(
ValidationError(
error_type="invalid_y_value",
world_name=world_name,
marker_set_name=marker_set.name,
marker_name=marker.name,
index=i,
y_value=y,
note="Track point has coordinate above 255 or below -64.",
)
)
return result
def validate_no_consecutive_duplicate_points_in_tracks(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
result = []
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
for marker in marker_set.markers:
if isinstance(marker, Track):
for p1, p2 in zip(marker.points, marker.points[1:]):
if p1 == p2:
result.append(
ValidationError(
error_type="duplicate_point_in_track",
world_name=world_name,
marker_set_name=marker_set.name,
marker_name=marker.name,
point=ValidationErrorPoint(*p1),
note="Consecutive points in a track must not be identical, this is likely a copy-paste error.",
)
)
return result
def validate_no_duplicate_marker_names(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
result = []
marker_names = set()
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
for marker in marker_set.markers:
full_name = (world_name, marker_set.name, marker.name)
if full_name in marker_names:
result.append(
ValidationError(
error_type="duplicate_marker_name",
world_name=world_name,
marker_set_name=marker_set.name,
marker_name=marker.name,
note="Marker names must be unique within a marker set.",
)
)
marker_names.add(full_name)
return result
def validate_no_duplicate_marker_set_names(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
result = []
marker_set_names = set()
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
if (world_name, marker_set.name) in marker_set_names:
result.append(
ValidationError(
error_type="duplicate_marker_set_name",
world_name=world_name,
marker_set_name=marker_set.name,
note="Marker set names must be unique within a world.",
)
)
marker_set_names.add((world_name, marker_set.name))
return result
def validate_no_unused_icons(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
# TODO: Implement this function
return []
def validate_no_duplicate_points(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
result = []
for world_name, marker_sets in worlds.items():
points = {}
for marker_set in marker_sets:
for marker in marker_set.markers:
if isinstance(marker, Point):
point = (marker.x, marker.y, marker.z)
if point in points:
result.append(
ValidationError(
error_type="duplicate_point",
world_name=world_name,
marker_set_name=points[point][1],
marker_name=points[point][2],
other_marker_set_name=marker_set.name,
other_marker_name=marker.name,
location=ValidationErrorPoint(*point),
note="Two markers should not share the same coordinates.",
)
)
points[point] = (world_name, marker_set.name, marker.name)
return result
def validate_all_tracks_have_at_least_two_points(
worlds: dict[str, list[MarkerSet]],
) -> list[ValidationError]:
result = []
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
for marker in marker_set.markers:
if isinstance(marker, Track) and len(marker.points) < 2:
result.append(
ValidationError(
error_type="track_too_short",
world_name=world_name,
marker_set_name=marker_set.name,
marker_name=marker.name,
note="Tracks must have at least two points.",
)
)
return result
def validate_track_points_only_change_in_two_directions_at_once(worlds: dict[str, list[MarkerSet]]) -> list[ValidationError]:
result = []
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
for marker in marker_set.markers:
if isinstance(marker, Track):
for i in range(len(marker.points) - 1):
x1, y1, z1 = marker.points[i]
x2, y2, z2 = marker.points[i + 1]
changes = sum([
x1 != x2,
y1 != y2,
z1 != z2,
])
if changes > 2:
result.append(
ValidationError(
error_type = "invalid_track_movement",
world_name = world_name,
marker_set_name = marker_set.name,
marker_name = marker.name,
point_indices = (i, i + 1),
point_a = ValidationErrorPoint(*marker.points[i]),
point_b = ValidationErrorPoint(*marker.points[i + 1]),
note = "All of X, Y, and Z changed between these two points, please only change in two directions at once.",
)
)
return result
def validate_xy_zy_track_ratios(worlds: dict[str, list[MarkerSet]]) -> list[ValidationError]:
result = []
for world_name, marker_sets in worlds.items():
for marker_set in marker_sets:
for marker in marker_set.markers:
if isinstance(marker, Track):
for i in range(len(marker.points) - 1):
x1, y1, z1 = marker.points[i]
x2, y2, z2 = marker.points[i + 1]
if y1 == y2:
continue
if x1 != x2 and z1 != z2:
continue
dy = abs(y2 - y1)
dh = abs(x2 - x1) + abs(z2 - z1)
if dh == 0:
result.append(
ValidationError(
error_type = "vertical_movement",
world_name = world_name,
marker_set_name = marker_set.name,
marker_name = marker.name,
point_indices = (i, i + 1),
point_a = ValidationErrorPoint(*marker.points[i]),
point_b = ValidationErrorPoint(*marker.points[i + 1]),
)
)
elif dy - dh > 1:
result.append(
ValidationError(
error_type = "excessive_slope",
world_name = world_name,
marker_set_name = marker_set.name,
marker_name = marker.name,
point_indices = (i, i + 1),
point_a = ValidationErrorPoint(*marker.points[i]),
point_b = ValidationErrorPoint(*marker.points[i + 1]),
dy = dy,
dh = dh,
)
)
elif dh - dy > 1:
result.append(
ValidationError(
error_type = "shallow_slope",
world_name = world_name,
marker_set_name = marker_set.name,
marker_name = marker.name,
point_indices = (i, i + 1),
point_a = ValidationErrorPoint(*marker.points[i]),
point_b = ValidationErrorPoint(*marker.points[i + 1]),
dy = dy,
dh = dh,
)
)
return result
_tests: dict[str, Callable[[dict[str, list[MarkerSet]]], list[ValidationError]]] = {
"No non-included files": validate_no_non_included_files,
"No invalid y coordinates": validate_no_invalid_y_values,
"No consecutive duplicate points in tracks": validate_no_consecutive_duplicate_points_in_tracks,
"No duplicate marker names": validate_no_duplicate_marker_names,
"No duplicate marker-set names": validate_no_duplicate_marker_set_names,
"No unused icons": validate_no_unused_icons,
"No duplicate points": validate_no_duplicate_points,
"All tracks have at least two points": validate_all_tracks_have_at_least_two_points,
"Track points only change in two directions at once": validate_track_points_only_change_in_two_directions_at_once,
"Track slopes are not too steep or too shallow": validate_xy_zy_track_ratios,
}

View File

@@ -0,0 +1,15 @@
from .area import Area
from .color import Color
from .marker import Marker
from .marker_set import MarkerSet
from .point import Point
from .track import Track
__all__ = [
"Area",
"Color",
"Marker",
"MarkerSet",
"Point",
"Track",
]

17
src/lib_marker/area.py Normal file
View File

@@ -0,0 +1,17 @@
from .marker import Marker
class Area(Marker):
points: list[tuple[int, int, int]]
icon: str | None = None
def __init__(
self,
name: str,
points: list[tuple[int, int, int]],
icon: str | None = None,
skip_validation: bool = False,
) -> None:
super().__init__(name, skip_validation)
self.points = points
self.icon = icon

8
src/lib_marker/color.py Normal file
View File

@@ -0,0 +1,8 @@
from dataclasses import dataclass
@dataclass
class Color:
r: int
g: int
b: int

10
src/lib_marker/marker.py Normal file
View File

@@ -0,0 +1,10 @@
from abc import ABC
class Marker(ABC):
name: str
skip_validation: bool = False
# icon: str | None = None
def __init__(self, name: str, skip_validation: bool = False) -> None:
self.name = name
self.skip_validation = skip_validation

View File

@@ -0,0 +1,26 @@
from dataclasses import dataclass
from .color import Color
from .marker import Marker
@dataclass
class MarkerSet:
name: str
markers: list[Marker]
# Icon to use for the marker group. If None, the default icon will be used.
icon: str | None = None
# Default icon to use for the markers in the group. If None, the default icon will be used.
default_icon: str | None = None
# Default icon size to use for the markers in the group. If None, the default icon size will be used.
default_icon_size: tuple[int, int] | None = None
# Default color to use for the markers in the group. If None, the default color will be used.
default_color: Color | None = None
# Whether to show this marker group by default.
show_by_default: bool = True

23
src/lib_marker/point.py Normal file
View File

@@ -0,0 +1,23 @@
from .marker import Marker
class Point(Marker):
x: int
y: int
z: int
icon: str | None = None
skip_validation: bool = False
def __init__(
self,
name: str,
x: int,
y: int,
z: int,
icon: str | None = None,
skip_validation: bool = False,
) -> None:
super().__init__(name, skip_validation)
self.x = x
self.y = y
self.z = z
self.icon = icon

16
src/lib_marker/track.py Normal file
View File

@@ -0,0 +1,16 @@
from .marker import Marker
class Track(Marker):
points: list[tuple[int, int, int]]
icon: str | None = None
def __init__(
self,
name: str,
points: list[tuple[int, int, int]],
icon: str | None = None,
skip_validation: bool = False,
) -> None:
super().__init__(name, skip_validation)
self.points = points
self.icon = icon

View File

@@ -0,0 +1,3 @@
from .exporter import generate_mapcrafter_output
__all__ = ["generate_mapcrafter_output"]

View File

@@ -0,0 +1,92 @@
import json
from pathlib import Path
import re
from lib_marker import Area, Point, Track
from marker_sets import WORLDS
def generate_mapcrafter_output(output_dir: Path):
output_dir.mkdir(parents=True, exist_ok=True)
with (Path(__file__).parent / "markers-template.js").open(
"r", encoding="utf-8"
) as f:
template = f.read()
exported_markers_json = json.dumps(
convert_data_to_mapcrafter_structure(),
indent=2,
ensure_ascii=False,
)
# Fjern "[" "]" rundt teksten.
exported_markers_json = re.sub(
r'^\s*\[\s*(.*\})\s*\]\s*$',
r'\1',
exported_markers_json,
flags=re.DOTALL)
template = template.replace('"@EXPORTED_MARKERS@"', exported_markers_json)
with (output_dir / "markers.js").open("w", encoding="utf-8") as f:
f.write(template)
def get_list_entry(lst, name):
for dings in lst:
if 'id' in dings and dings['id'] == name:
return dings
return None
def get_mapcrafter_world_name(name):
names = {
"Overworld": "verden",
"Nether": "underverden",
"The End": "enden",
}
if name in names:
return names[name]
raise ValueError(f"Unknown world name: {name}")
def convert_data_to_mapcrafter_structure() -> list:
result = []
for world_name, world_marker_sets in WORLDS.items():
mapc_world_name = get_mapcrafter_world_name(world_name)
for marker_set in world_marker_sets:
# https://mapcrafter.readthedocs.io/en/latest/markers.html#manually-specifying-markers
id = marker_set.name.lower().replace(" ", "-")
result_entry = get_list_entry(result, id)
if not result_entry:
result_entry = {
"id": id,
"name": marker_set.name,
"showDefault": marker_set.show_by_default,
"markers": {},
}
result.append(result_entry)
result_entry["markers"][mapc_world_name] = []
for marker in marker_set.markers:
if isinstance(marker, Point):
result_entry["markers"][mapc_world_name].append(
{"pos": [marker.x, marker.z, marker.y], "title": marker.name}
)
elif isinstance(marker, Track):
print(
f"Track markers are not natively supported in Mapcrafter, skipping '{marker.name}'..."
)
elif isinstance(marker, Area):
print(
f"Area markers are not natively supported in Mapcrafter, skipping '{marker.name}'..."
)
else:
raise ValueError(f"Unknown marker type: {marker}")
return result

View File

@@ -0,0 +1,67 @@
var MAPCRAFTER_MARKERS = [
"@EXPORTED_MARKERS@",
{
//
// Tegner et rutenett som viser regiongrensene, og
// koordinatene for hver region.
//
"id" : "regioner",
"name" : "Regioner",
"showDefault" : false,
"createMarker" : function(ui, groupInfo, markerInfo) {
var rmax = 15;
var objekter = [];
var multilatlngs = [];
for (var rx = -rmax; rx < rmax; rx++) {
var latlngs = [];
var miny = -rmax*512;
var maxy = rmax*512;
var x = rx*512;
// use the ui.mcToLatLng-function to convert Minecraft coords to LatLngs
latlngs.push(ui.mcToLatLng(x, miny, 64));
latlngs.push(ui.mcToLatLng(x, maxy, 64));
multilatlngs.push(latlngs);
}
for (var ry = -rmax; ry < rmax; ry++) {
var latlngs = [];
var minx = -rmax*512;
var maxx = rmax*512;
var y = ry*512;
// use the ui.mcToLatLng-function to convert Minecraft coords to LatLngs
latlngs.push(ui.mcToLatLng(minx, y, 64));
latlngs.push(ui.mcToLatLng(maxx, y, 64));
multilatlngs.push(latlngs);
}
for (var rx = -rmax; rx < rmax; rx++) {
for (var ry = -rmax; ry < rmax; ry++) {
var x = rx*512 + 256;
var y = ry*512 + 256;
var myIcon = L.divIcon({iconSize: L.point(55, 20), html: "<center>r." + String(rx) + "." + String(ry) + "</center>"});
var myMarker = L.marker(ui.mcToLatLng(x, y, 64), {icon: myIcon});
objekter.push(myMarker);
}
}
var linjer = [];
if (L.version.startsWith("0.7.")) {
linjer = L.multiPolyline(multilatlngs, {"color" : markerInfo.color});
} else {
linjer = L.polyline(multilatlngs, {"color" : markerInfo.color});
}
objekter.push(linjer);
return L.layerGroup(objekter);
},
"markers" : {
"verden" : [
{
"color" : "yellow",
},
],
},
},
];

View File

@@ -0,0 +1,11 @@
from lib_marker import MarkerSet
from .nether import MARKER_SETS as nether_marker_sets
from .overworld import MARKER_SETS as overworld_marker_sets
from .the_end import MARKER_SETS as the_end_marker_sets
WORLDS: dict[str, list[MarkerSet]] = {
"Nether": nether_marker_sets,
"Overworld": overworld_marker_sets,
"The End": the_end_marker_sets,
}

View File

@@ -0,0 +1,14 @@
from .area_names import MARKER_SET as area_names_marker_set
from .ice_tracks import MARKER_SET as ice_tracks_marker_set
from .other import MARKER_SET as other_marker_set
from .portals import MARKER_SET as portals_marker_set
from .railways import MARKER_SET as railways_marker_set
MARKER_SETS = [
area_names_marker_set,
ice_tracks_marker_set,
other_marker_set,
portals_marker_set,
railways_marker_set,
]

View File

@@ -0,0 +1,14 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Area Names",
markers=[
Point(
name="Grisebukta",
icon=None,
x=-900,
y=64,
z=27,
),
],
)

View File

@@ -0,0 +1,24 @@
from lib_marker import MarkerSet, Track
MARKER_SET = MarkerSet(
name="Ice tracks",
markers=[
Track(
name="Sentralskøytebane Sør",
points=[
(0, 65, 42),
(0, 65, 821),
(206, 65, 821),
],
),
Track(
name="Sentralskøytebane Mer Sør",
points=[
(232, 46, 814),
(232, 46, 1025),
(397, 46, 1025),
],
),
],
)

View File

@@ -0,0 +1,15 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Other",
markers=[
Point(
name="Nullpunkt",
icon=None,
x=0,
y=64,
z=0,
),
],
)

View File

@@ -0,0 +1,36 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Portals",
markers=[
Point(
name="Til Grisebukta",
icon=None,
x=-900,
y=64,
z=27,
),
Point(
name="Til Hemmelig mesabiom",
icon=None,
x=-120,
y=64,
z=510,
),
Point(
name="Til Biosfæren",
icon=None,
x=120,
y=64,
z=-420,
),
Point(
name="Til Holmgard",
icon=None,
x=520,
y=64,
z=220,
),
],
)

View File

@@ -0,0 +1,62 @@
from lib_marker import MarkerSet, Track
MARKER_SET = MarkerSet(
name="Railways",
markers=[
Track(
name="Distriktsbane Sør",
points=[
(-24, 90, -112),
(-22, 90, -112),
(-22, 90, -113),
(-22, 78, -126),
(-22, 78, -128),
(-19, 78, -128),
(-19, 78, -127),
(-19, 53, -101),
(-19, 53, -100),
(-18, 53, -100),
(-13, 49, -100),
(-13, 49, -98),
(-13, 16, -64),
(-13, 16, -1610),
(-13, 35, -1630),
(-13, 35, -1641),
(-13, 36, -1643),
(-13, 36, -1646),
],
),
Track(
name="Gruvebane Øst",
points=[
(-11, 16, -61),
(1030, 16, -61),
(1030, 16, -253),
],
),
Track(
name="Distriktsbane Øst",
points=[
(4, 90, -123),
(1352, 90, -123),
(1352, 90, -124),
(1352, 85, -130),
(1355, 85, -130),
(1355, 85, -140),
],
),
Track(
name="Griseruta",
points=[
(4, 64, -366),
(4, 64, -397),
(-823, 64, -397),
(-823, 64, -4),
(-823, 37, 24),
(-823, 37, 31),
(-881, 37, 31),
],
),
],
)

View File

@@ -0,0 +1,29 @@
from .area_names import MARKER_SET as area_names_marker_set
from .buildings import MARKER_SET as buildings_marker_set
from .cities import MARKER_SET as cities_marker_set
from .homes import MARKER_SET as homes_marker_set
from .huts import MARKER_SET as huts_marker_set
from .infrastructure import MARKER_SET as infrastructure_marker_set
from .mines import MARKER_SET as mines_marker_set
from .nature import MARKER_SET as nature_marker_set
from .other import MARKER_SET as other_marker_set
from .railways import MARKER_SET as railways_marker_set
from .roads import MARKER_SET as roads_marker_set
from .tunnels import MARKER_SET as tunnels_marker_set
from .villages import MARKER_SET as villages_marker_set
MARKER_SETS = [
area_names_marker_set,
buildings_marker_set,
cities_marker_set,
homes_marker_set,
huts_marker_set,
infrastructure_marker_set,
mines_marker_set,
nature_marker_set,
other_marker_set,
railways_marker_set,
roads_marker_set,
tunnels_marker_set,
villages_marker_set,
]

View File

@@ -0,0 +1,126 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Area Names",
markers=[
Point(
name="Storhavet i vest",
icon=None,
x=-2700,
y=64,
z=-500,
),
Point(
name="Storhavet i vest",
icon=None,
x=-2100,
y=64,
z=1700,
),
Point(
name="Snøgolfen",
icon=None,
x=-1300,
y=64,
z=380,
),
Point(
name="England",
icon=None,
x=-1189,
y=64,
z=1556,
),
Point(
name="Hemmelig mesabiom",
icon=None,
x=-980,
y=64,
z=4090,
),
Point(
name="Shirobukta",
icon=None,
x=-940,
y=64,
z=-1300,
),
Point(
name="Vestisødet",
icon=None,
x=-800,
y=64,
z=0,
),
Point(
name="Svartehavet",
icon=None,
x=-700,
y=64,
z=2240,
),
Point(
name="Heimhavet",
icon=None,
x=-500,
y=64,
z=1000,
),
Point(
name="Summefjord",
icon=None,
x=-220,
y=64,
z=2890,
),
Point(
name="Tangenhavet",
icon=None,
x=0,
y=67,
z=-2300,
),
Point(
name="Nordørkenen",
icon=None,
x=0,
y=67,
z=-485,
),
Point(
name="Nordhavet",
icon=None,
x=1050,
y=64,
z=-1350,
),
Point(
name="Ishavet",
icon=None,
x=1730,
y=64,
z=-3070,
),
Point(
name="Andøya",
icon=None,
x=2300,
y=64,
z=250,
),
Point(
name="Ishavet",
icon=None,
x=2350,
y=64,
z=-2050,
),
Point(
name="Bjørkesjø",
icon=None,
x=3050,
y=64,
z=-500,
),
],
)

View File

@@ -0,0 +1,70 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Buildings",
markers=[
Point(
name="Der Kölner Dom",
icon=None,
x=4630,
y=64,
z=270,
),
Point(
name="Fyret",
icon=None,
x=3085,
y=64,
z=424,
),
Point(
name="Hodeskallegrotten",
icon=None,
x=267,
y=89,
z=-496,
),
Point(
name="Isjungelkatedralen",
icon=None,
x=-378,
y=64,
z=153,
),
Point(
name="Jesus Christ Superstore",
icon=None,
x=280,
y=64,
z=-2800,
),
Point(
name="Kystbasen",
icon=None,
x=-334,
y=116,
z=473,
),
Point(
name="Tårnodden",
icon=None,
x=-22,
y=76,
z=67,
),
Point(
name="Tegltårnet",
icon=None,
x=-99,
y=99,
z=1025,
),
Point(
name="Vinterstranda hotel & resort",
icon=None,
x=3481,
y=75,
z=210,
),
],
)

View File

@@ -0,0 +1,56 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Cities",
markers=[
Point(
name="Heimfjell",
icon=None,
x=177,
y=64,
z=177,
),
Point(
name="Holmgard",
icon=None,
x=4468,
y=64,
z=1337,
),
Point(
name="Tangen",
icon=None,
x=58,
y=63,
z=-2860,
),
Point(
name="Tosjødalen",
icon=None,
x=960,
y=64,
z=460,
),
Point(
name="Vikingelandsbyen",
icon=None,
x=360,
y=64,
z=-460,
),
Point(
name="Shiro",
icon=None,
x=-790,
y=64,
z=-837,
),
Point(
name="Singapore",
icon=None,
x=-600,
y=64,
z=-1130,
),
],
)

View File

@@ -0,0 +1,105 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Homes",
markers=[
Point(
name="Akasienborg",
icon=None,
x=-128,
y=64,
z=1460,
),
Point(
name="Bikkjeby",
icon=None,
x=2950,
y=64,
z=270,
),
Point(
name="Biosfæren",
icon=None,
x=834,
y=65,
z=-3089,
),
Point(
name="Bjørkeholmen",
icon=None,
x=102,
y=77,
z=37,
),
Point(
name="Bjørnebukten",
icon=None,
x=473,
y=78,
z=-3043,
),
Point(
name="Blomenholm",
icon=None,
x=-295,
y=64,
z=2372,
),
Point(
name="Huløyeid",
icon=None,
x=-54,
y=64,
z=234,
),
Point(
name="Linder",
icon=None,
x=4321,
y=64,
z=-8778,
),
Point(
name="Singapore",
icon=None,
x=-600,
y=64,
z=1130,
),
Point(
name="Summevatn",
icon=None,
x=-110,
y=64,
z=3465,
),
Point(
name="SuperTheodors fiskehus",
icon=None,
x=1570,
y=64,
z=3285,
),
Point(
name="Svartskoghavn",
icon=None,
x=-370,
y=64,
z=2390,
),
Point(
name="Vestlandet",
icon=None,
x=-2204,
y=64,
z=447,
),
Point(
name="Østsumpøyene",
icon=None,
x=4070,
y=64,
z=2900,
),
],
)

View File

@@ -0,0 +1,35 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Huts",
markers=[
Point(
name="Dypskiferhytta",
icon=None,
x=-1563,
y=64,
z=-1966,
),
Point(
name="Grensebuelvbu",
icon=None,
x=550,
y=64,
z=-1480,
),
Point(
name="Tretopphuset",
icon=None,
x=642,
y=105,
z=13,
),
Point(
name="Nyverdenhytta",
icon=None,
x=848,
y=70,
z=1583,
),
],
)

View File

@@ -0,0 +1,21 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Infrastructure",
markers=[
Point(
name="Grantrebukta",
icon=None,
x=2350,
y=64,
z=-136,
),
Point(
name="Smalelvbro",
icon=None,
x=77,
y=69,
z=-348,
),
],
)

View File

@@ -0,0 +1,21 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Mines",
markers=[
Point(
name="Sandsteingruva",
icon=None,
x=56,
y=67,
z=-266,
),
Point(
name="Slimegruva",
icon=None,
x=-206,
y=64,
z=-35,
),
],
)

View File

@@ -0,0 +1,140 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Nature",
markers=[
Point(
name="Blindodden",
icon=None,
x=874,
y=65,
z=160,
),
Point(
name="Hulevatna",
icon=None,
x=240,
y=64,
z=3070,
),
Point(
name="Kirkemyr",
icon=None,
x=6880,
y=64,
z=-465,
),
Point(
name="Lamafjellet",
icon=None,
x=1810,
y=64,
z=-1630,
),
Point(
name="Langstrand",
icon=None,
x=-305,
y=64,
z=535,
),
Point(
name="Lille isbjørnelv",
icon=None,
x=730,
y=64,
z=-1900,
),
Point(
name="Naturreservat",
icon=None,
x=4,
y=64,
z=800,
),
Point(
name="Piratbukta",
icon=None,
x=150,
y=64,
z=80,
),
Point(
name="Snøklippan",
icon=None,
x=-1654,
y=64,
z=-654,
),
Point(
name="Store isbjørnelv",
icon=None,
x=850,
y=64,
z=-2130,
),
Point(
name="Storhavet i øst",
icon=None,
x=3000,
y=64,
z=1000,
),
Point(
name="Storhavskanalen",
icon=None,
x=1005,
y=64,
z=1335,
),
Point(
name="Storsnøfjella",
icon=None,
x=5470,
y=64,
z=-270,
),
Point(
name="Storvatnet",
icon=None,
x=675,
y=64,
z=362,
),
Point(
name="SuperTheodors strand",
icon=None,
x=2111,
y=64,
z=3777,
),
Point(
name="Trangdalen",
icon=None,
x=-47,
y=64,
z=146,
),
Point(
name="Turrikkelmyra",
icon=None,
x=-160,
y=64,
z=1480,
),
Point(
name="Ulveelva",
icon=None,
x=910,
y=64,
z=3050,
),
Point(
name="Vestsumpland",
icon=None,
x=-1818,
y=64,
z=98,
),
],
)

View File

@@ -0,0 +1,21 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Other",
markers=[
Point(
name="Leirhavn",
icon=None,
x=512,
y=64,
z=-3320,
),
Point(
name="Nullpunkt",
icon=None,
x=0,
y=64,
z=0,
),
],
)

View File

@@ -0,0 +1,539 @@
from lib_marker import MarkerSet, Track
MARKER_SET = MarkerSet(
name="Railways",
markers=[
Track(
name="Sentralbane vest",
points=[
(147, 71, 127),
(44, 71, 127),
(41, 71, 123),
(-32, 71, 123),
(-32, 71, 126),
(-34, 71, 128),
(-53, 71, 128),
(-68, 71, 128),
(-77, 71, 128),
(-106, 71, 173),
(-106, 71, 180),
(-129, 71, 203),
(-271, 71, 203),
(-308, 71, 165),
(-358, 71, 165),
],
),
Track(
name="Sentralbane nord",
points=[
(147, 71, 127),
(147, 71, 16),
(157, 71, 7),
(157, 71, -65),
(216, 71, -123),
(216, 71, -132),
(275, 71, -190),
(275, 71, -203),
(297, 71, -224),
(297, 71, -236),
(333, 71, -271),
(333, 71, -272),
(333, 72, -274),
(333, 72, -286),
(350, 72, -302),
(350, 72, -424),
(350, 72, -421),
(328, 72, -421),
(310, 72, -439),
(310, 72, -481),
(293, 72, -497),
(293, 72, -518),
(285, 72, -525),
(285, 72, -647),
(306, 72, -667),
(306, 72, -852),
(304, 72, -852),
(304, 72, -854),
(302, 72, -854),
(306, 72, -854),
(306, 72, -1003),
(345, 72, -1041),
(345, 72, -1095),
(412, 72, -1161),
(412, 72, -1181),
(482, 72, -1250),
(482, 72, -1278),
(564, 72, -1359),
(564, 72, -1374),
(642, 72, -1451),
(642, 72, -1464),
(716, 72, -1537),
(716, 72, -1552),
(775, 72, -1610),
(775, 72, -2316),
(770, 72, -2316),
(770, 72, -2318),
(776, 72, -2318),
(775, 72, -2318),
(775, 72, -2319),
(776, 72, -2319),
(776, 72, -2323),
(776, 81, -2333),
(776, 81, -2554),
(776, 86, -2560),
(776, 86, -2700),
(729, 86, -2700),
(729, 86, -2989),
(729, 88, -2992),
(729, 88, -3011),
(685, 88, -3011),
(685, 88, -3010),
(682, 88, -3010),
(682, 88, -3012),
],
),
Track(
name="Biosfæren Lokaltog",
points=[
(684, 88, -3014),
(687, 88, -3014),
(689, 87, -3014),
(692, 85, -3014),
(693, 85, -3014),
(695, 84, -3014),
(697, 83, -3014),
(699, 82, -3014),
(701, 81, -3014),
(703, 80, -3014),
(705, 79, -3014),
(707, 78, -3014),
(710, 76, -3014),
(713, 74, -3014),
(715, 73, -3014),
(718, 71, -3014),
(722, 68, -3014),
(727, 64, -3014),
(729, 63, -3014),
(732, 61, -3014),
(737, 62, -3014),
(739, 63, -3014),
(741, 64, -3014),
(745, 67, -3014),
(747, 67, -3014),
(747, 67, -3020),
(747, 66, -3022),
(747, 66, -3041),
(747, 67, -3043),
(747, 67, -3045),
(747, 68, -3047),
(747, 68, -3048),
(747, 70, -3051),
(747, 71, -3053),
(747, 71, -3057),
(747, 72, -3059),
(747, 72, -3062),
(747, 73, -3064),
(747, 73, -3072),
(807, 132, -3072),
(809, 132, -3072),
(809, 132, -3070),
(830, 132, -3070),
(830, 132, -3068),
(833, 132, -3068),
(833, 132, -3070),
(834, 132, -3070),
(833, 132, -3070),
(833, 132, -3068),
(830, 132, -3070),
(830, 132, -3076),
(809, 132, -3076),
(809, 132, -3074),
(807, 132, -3074),
(747, 73, -3074),
(747, 73, -3073),
],
),
Track(
name="Tangenbanen",
points=[
(680, 88, -3014),
(523, 88, -3014),
(508, 74, -3014),
(483, 74, -3014),
(483, 74, -3016),
(475, 74, -3016),
(473, 75, -3016),
(451, 75, -3016),
(446, 79, -3016),
(445, 79, -3016),
(445, 79, -3017),
(440, 79, -3017),
(440, 79, -3016),
(440, 79, -3018),
(435, 79, -3018),
(431, 82, -3018),
(485, 82, -3018),
(474, 72, -3018),
(306, 72, -3018),
(306, 72, -2998),
(83, 72, -2998),
(83, 72, -3000),
(79, 72, -3000),
(79, 72, -2998),
(76, 72, -2998),
(72, 69, -2998),
(72, 65, -3003),
(63, 57, -3003),
(60, 57, -3003),
(60, 57, -2965),
(58, 57, -2963),
(58, 57, -2951),
(47, 57, -2951),
(44, 59, -2951),
(44, 59, -2895),
(22, 59, -2895),
(22, 59, -2887),
(17, 59, -2887),
(17, 59, -2877),
(17, 59, -2887),
(28, 59, -2887),
(28, 59, -2889),
(38, 68, -2889),
(40, 68, -2889),
(40, 72, -2884),
(36, 72, -2884),
],
),
Track(
name="Sentralbane Sør",
points=[
(156, 71, 132),
(156, 71, 176),
(175, 71, 194),
(175, 71, 653),
(164, 71, 663),
(164, 71, 725),
(126, 71, 763),
(94, 71, 763),
(69, 71, 788),
(69, 71, 797),
(31, 71, 835),
(19, 71, 835),
(1, 71, 854),
(-21, 71, 854),
(-60, 71, 893),
(-60, 71, 991),
(-58, 71, 991),
(-58, 71, 993),
(-58, 71, 985),
(61, 71, 985),
(88, 71, 1013),
(117, 71, 1013),
(120, 71, 1016),
(120, 71, 1034),
(173, 71, 1086),
(173, 71, 1338),
(139, 71, 1371),
(139, 71, 1501),
(71, 71, 1569),
(-11, 71, 1569),
(-67, 71, 1626),
(-149, 71, 1626),
(-194, 71, 1671),
(-194, 71, 1961),
(-243, 71, 2010),
(-291, 71, 2010),
(-314, 71, 2033),
(-314, 71, 2065),
(-358, 71, 2109),
(-387, 71, 2109),
(-399, 71, 2122),
(-406, 71, 2122),
(-424, 71, 2140),
(-424, 71, 2260),
(-416, 71, 2267),
(-416, 71, 2294),
(-395, 71, 2314),
(-395, 71, 2406),
],
),
Track(
name="Sentralbane Øst",
points=[
(157, 71, 129),
(230, 71, 129),
(239, 71, 139),
(286, 71, 139),
(325, 71, 179),
(422, 71, 179),
(426, 74, 179),
(437, 74, 179),
(455, 74, 160),
(479, 74, 160),
(515, 74, 123),
(547, 74, 123),
(565, 74, 104),
(636, 74, 104),
(646, 74, 93),
(727, 74, 93),
(748, 74, 71),
(760, 74, 71),
(786, 74, 44),
(811, 74, 44),
(811, 74, 43),
(812, 74, 43),
(814, 73, 43),
(820, 73, 43),
(822, 74, 43),
(826, 74, 43),
(846, 74, 23),
(846, 74, 7),
(886, 74, -33),
(908, 74, -33),
(946, 74, -71),
(946, 74, -83),
(977, 74, -114),
(984, 74, -114),
(1022, 74, -152),
(1022, 74, -164),
(1054, 74, -195),
(1054, 74, -202),
(1079, 74, -227),
(1086, 74, -227),
(1117, 74, -259),
(1124, 74, -259),
(1155, 74, -291),
(1167, 74, -291),
(1188, 74, -313),
(1223, 74, -313),
(1249, 74, -340),
(1257, 74, -340),
],
),
Track(
name="Østbane B",
points=[
(817, 73, 43),
(817, 73, 46),
(817, 74, 48),
(817, 74, 49),
(817, 72, 52),
(828, 72, 62),
(828, 72, 63),
(828, 71, 65),
(828, 71, 71),
(828, 70, 73),
(828, 70, 164),
(830, 70, 166),
(848, 70, 166),
(848, 70, 167),
(851, 70, 167),
(851, 70, 168),
(853, 70, 168),
(857, 70, 173),
(860, 70, 173),
(860, 70, 174),
(940, 70, 174),
(944, 70, 178),
(944, 70, 213),
(944, 69, 215),
(944, 69, 216),
(944, 68, 218),
(944, 67, 220),
(944, 66, 222),
(944, 65, 224),
(944, 65, 473),
],
),
Track(
name="Tosjødalen gruveekspress",
points=[
(943, 65, 473),
(943, 65, 436),
(941, 65, 436),
(941, 64, 438),
(941, 64, 439),
(941, 63, 441),
(941, 62, 443),
(941, 62, 444),
(940, 62, 444),
(938, 61, 444),
(937, 61, 444),
(935, 60, 444),
(934, 60, 444),
(934, 60, 443),
(934, 59, 441),
(934, 58, 439),
(934, 58, 438),
(935, 58, 438),
(937, 57, 438),
(938, 57, 438),
(940, 56, 438),
(942, 56, 438),
(942, 56, 512),
(946, 56, 512),
(954, 63, 512),
(954, 63, 519),
(954, 64, 521),
(954, 64, 522),
(960, 64, 522),
(962, 65, 522),
(969, 65, 522),
(971, 64, 522),
(972, 64, 522),
(974, 63, 522),
(974, 63, 625),
(972, 63, 625),
(970, 64, 625),
(966, 67, 625),
(957, 75, 625),
(956, 75, 625),
(952, 72, 625),
(935, 72, 625),
(935, 72, 621),
(928, 72, 621),
(928, 72, 623),
(929, 72, 623),
(929, 70, 626),
(927, 69, 626),
(927, 68, 624),
(929, 67, 624),
(929, 65, 627),
(928, 65, 627),
(928, 64, 629),
(927, 64, 629),
(927, 64, 628),
(927, 61, 624),
(927, 61, 623),
(931, 61, 623),
(933, 60, 623),
(933, 59, 625),
(932, 59, 625),
(930, 58, 625),
(930, 58, 624),
(930, 57, 622),
(932, 56, 622),
(932, 56, 623),
(933, 56, 623),
(933, 55, 625),
(931, 54, 625),
(931, 53, 623),
(933, 52, 623),
(933, 51, 625),
(932, 51, 625),
(930, 50, 625),
(930, 49, 623),
(932, 48, 623),
# TODO: denne går egentlig lengre, men kartet viser ikke mer.
# Må undersøkes i spillet selv.
],
),
Track(
name="Shiro Monorail",
points=[
(-689, 73, -770),
(-685, 73, -770),
(-685, 73, -765),
(-685, 74, -764),
(-685, 74, -760),
(-720, 74, -760),
(-737, 76, -760),
(-737, 76, -646),
(-739, 77, -646),
(-742, 77, -646),
(-744, 78, -646),
(-759, 92, -646),
(-815, 92, -646),
(-815, 92, -643),
(-815, 101, -633),
(-815, 101, -385),
(-815, 97, -380),
(-815, 97, -377),
(-815, 90, -370),
(-813, 90, -370),
(-813, 73, -352),
(-813, 73, -306),
(-813, 68, -300),
(-813, 68, -298),
(-817, 68, -298),
(-819, 67, -298),
(-822, 67, -298),
],
),
Track(
name="Chikatetsu End-sen",
points=[
(-772, 63, -953),
(-780, 63, -953),
(-780, 63, -952),
(-787, 63, -952),
(-789, 63, -942),
(-789, 62, -940),
(-789, 61, -938),
(-789, 60, -936),
(-789, 59, -934),
(-789, 58, -932),
(-789, 57, -930),
(-789, 56, -928),
(-789, 55, -926),
(-789, 54, -924),
(-789, 53, -922),
(-789, 52, -920),
(-789, 51, -918),
(-789, 50, -916),
(-789, 49, -914),
(-789, 48, -912),
(-789, 47, -910),
(-789, 46, -908),
(-789, 45, -906),
(-789, 44, -904),
(-789, 43, -902),
(-789, 42, -900),
(-789, 41, -898),
(-789, 40, -896),
(-789, 39, -894),
(-789, 38, -892),
(-789, 37, -890),
(-789, 36, -888),
(-789, 35, -886),
(-789, 34, -884),
(-789, 33, -882),
(-789, 32, -880),
(-789, 31, -878),
(-789, 30, -876),
(-789, 29, -874),
(-789, 28, -872),
(-789, 27, -870),
(-789, 26, -868),
(-789, 25, -866),
(-789, 25, -857),
(-756, 25, -825),
(-756, 25, -218),
(-765, 25, -218),
],
),
Track(
name="Chikatetsu Kyassuru-sen",
points=[
(-772, 63, -945),
(-780, 63, -945),
(-780, 60, -941),
(-780, 60, -523),
],
),
Track(
name="Chikatetsu Doukutsu-sen",
points=[
(-688, 72, -772),
(-682, 72, -772),
(-682, 72, -765),
(-682, 31, -723),
(-682, 31, -715),
(-682, 15, -698),
(-682, 15, -691),
(-682, -48, -627),
(-802, -48, -627),
],
),
],
)

View File

@@ -0,0 +1,7 @@
from lib_marker import MarkerSet
MARKER_SET = MarkerSet(
name="Roads",
markers=[],
)

View File

@@ -0,0 +1,30 @@
from lib_marker import MarkerSet, Track
MARKER_SET = MarkerSet(
name="Tunnels",
markers=[
Track(
name="Danio's Warp Tunnel End Connection",
points=[
(-829, 23, -196),
(-981, 23, -196),
(-997, 15, -196),
(-1000, 15, -196),
],
),
Track(
name="Danio's Warp Tunnel North",
points=[
(-1000, 23, -1161),
(-1000, 23, 1000),
],
),
Track(
name="Danio's Warp Tunnel South",
points=[
(-1000, 23, 1000),
(-1000, 23, 4980),
],
),
],
)

View File

@@ -0,0 +1,70 @@
from lib_marker import MarkerSet, Point
MARKER_SET = MarkerSet(
name="Villages",
markers=[
Point(
name="Bondeby",
icon=None,
x=6200,
y=64,
z=-300,
),
Point(
name="Gammelleirnes",
icon=None,
x=800,
y=64,
z=-3300,
),
Point(
name="Hestelandsby",
icon=None,
x=2734,
y=64,
z=-984,
),
Point(
name="Isbjørnby",
icon=None,
x=723,
y=64,
z=-2380,
),
Point(
name="Ittoqqortoormiit",
icon=None,
x=100,
y=64,
z=-3830,
),
Point(
name="Kløfteby",
icon=None,
x=695,
y=64,
z=2812,
),
Point(
name="NPC-landsby nord",
icon=None,
x=337,
y=64,
z=-853,
),
Point(
name="NPC-landsby",
icon=None,
x=1278,
y=64,
z=-397,
),
Point(
name="Smedby",
icon=None,
x=-1760,
y=64,
z=-2440,
),
],
)

View File

@@ -0,0 +1,7 @@
from .other import MARKER_SET as other_marker_set
from .portals import MARKER_SET as portals_marker_set
MARKER_SETS = [
other_marker_set,
portals_marker_set,
]

View File

@@ -0,0 +1,7 @@
from lib_marker import MarkerSet
MARKER_SET = MarkerSet(
name="Other",
markers=[],
)

View File

@@ -0,0 +1,7 @@
from lib_marker import MarkerSet
MARKER_SET = MarkerSet(
name="Portals",
markers=[],
)

8
uv.lock generated Normal file
View File

@@ -0,0 +1,8 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "minecraft-kartverket"
version = "0.1.0"
source = { editable = "." }