diff --git a/quadtree.py b/quadtree.py
index 88b6204..3f9a223 100644
--- a/quadtree.py
+++ b/quadtree.py
@@ -22,6 +22,7 @@ import functools
import re
import shutil
import collections
+import json
from PIL import Image
@@ -119,6 +120,11 @@ class QuadtreeGen(object):
with open(os.path.join(self.destdir, "index.html"), 'w') as output:
output.write(html)
+
+
+ with open(os.path.join(self.destdir, "markers.js"), 'w') as output:
+ output.write("var markerData=%s" % json.dumps(self.world.POI))
+
def _get_cur_depth(self):
"""How deep is the quadtree currently in the destdir? This glances in
index.html to see what maxZoom is set to.
diff --git a/template.html b/template.html
index c0e5bc9..ef2868f 100644
--- a/template.html
+++ b/template.html
@@ -7,6 +7,7 @@
body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; }
#mcmap { height: 100% }
+
@@ -74,7 +75,57 @@
};
var map;
-
+ var prot;
+
+ var markersInit = false;
+
+ function convertCoords (x,y,z) {
+
+ var imgx = 0;
+ var imgy = 0;
+
+ imgx = imgx + (12*x);
+ imgy = imgy - (6*x);
+
+ imgx = imgx + (12 * y);
+ imgy = imgy + (6* y);
+
+ imgy = imgy - (12*z);
+
+ // this math is mysterious. i don't fully understand it
+ // but the idea is to assume that block 0,0,0 in chunk 0,0
+ // is drawn in the very middle of the gmap at (192,192)
+ return [192*Math.pow(2,config.maxZoom)+imgx, 192*Math.pow(2,config.maxZoom)+imgy+768+768];
+ }
+
+ function initMarkers() {
+ if (markersInit) { return; }
+
+ markersInit = true;
+
+ prot = map.getProjection();
+
+ for (i in markerData) {
+ var item = markerData[i];
+
+ var converted = convertCoords(item.x-16, item.z, item.y);
+
+
+ var x = converted[0] / Math.pow(2, config.maxZoom);
+ var y = converted[1] / Math.pow(2, config.maxZoom);
+ var p = new google.maps.Point(x,y);
+ var marker = new google.maps.Marker({
+position: prot.fromPointToLatLng(p),
+map: map,
+title:item.msg
+});
+
+}
+
+
+
+}
+
function initialize() {
var mapOptions = {
zoom: config.defaultZoom,
@@ -89,12 +140,33 @@
if(config.debug) {
map.overlayMapTypes.insertAt(0, new CoordMapType(new google.maps.Size(config.tileSize, config.tileSize)));
}
+
+
// Now attach the coordinate map type to the map's registry
map.mapTypes.set('mcmap', MCMapType);
// We can now set the map to use the 'coordinate' map type
map.setMapTypeId('mcmap');
+
+ prot = map.getProjection();
+
+ if (config.debug)
+ google.maps.event.addListener(map, 'click', function(event) {
+ console.log("latLng: " + event.latLng.lat() + ", " + event.latLng.lng());
+ var pnt = prot.fromLatLngToPoint(event.latLng);
+
+ console.log("point: " + pnt);//
+ var pxx = pnt.x * Math.pow(2,config.maxZoom);
+ var pxy = pnt.y * Math.pow(2,config.maxZoom);
+ console.log("pixel: " + pxx + ", " + pxy);
+ });
+
+ google.maps.event.addListener(map, 'projection_changed', function(event) {
+ initMarkers();
+ });
+
+
}
diff --git a/world.py b/world.py
index 4e906b1..64c6f2d 100644
--- a/world.py
+++ b/world.py
@@ -17,6 +17,7 @@ import functools
import os
import os.path
import multiprocessing
+import numpy
from PIL import Image
@@ -57,6 +58,29 @@ def _convert_coords(chunks):
return mincol, maxcol, minrow, maxrow, chunks_translated
+
+def base36encode(number, alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
+ '''
+ Convert an integer to a base36 string.
+ '''
+ if not isinstance(number, (int, long)):
+ raise TypeError('number must be an integer')
+
+ newn = abs(number)
+
+ # Special case for zero
+ if number == 0:
+ return '0'
+
+ base36 = ''
+ while newn != 0:
+ newn, i = divmod(newn, len(alphabet))
+ base36 = alphabet[i] + base36
+
+ if number < 0:
+ return "-" + base36
+ return base36
+
class WorldRenderer(object):
"""Renders a world's worth of chunks.
worlddir is the path to the minecraft world
@@ -67,6 +91,47 @@ class WorldRenderer(object):
self.caves = False
self.cachedir = cachedir
+ # stores Points Of Interest to be mapped with markers
+ # a list of dictionaries, see below for an example
+ self.POI = []
+
+ def findTrueSpawn(self):
+ """Adds the true spawn location to self.POI. The spawn Y coordinate
+ is almost always the default of 64. Find the first air block above
+ that point for the true spawn location"""
+
+ ## read spawn info from level.dat
+ data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]
+ spawnX = data['Data']['SpawnX']
+ spawnY = data['Data']['SpawnY']
+ spawnZ = data['Data']['SpawnZ']
+
+ ## The chunk that holds the spawn location
+ chunkX = spawnX/16
+ chunkY = spawnZ/16
+
+ ## The filename of this chunk
+ chunkFile = "%s/%s/c.%s.%s.dat" % (base36encode(chunkX % 64),
+ base36encode(chunkY % 64),
+ base36encode(chunkX),
+ base36encode(chunkY))
+
+
+ data=nbt.load(os.path.join(self.worlddir, chunkFile))[1]
+ level = data['Level']
+ blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128))
+
+ ## The block for spawn *within* the chunk
+ inChunkX = spawnX - (chunkX*16)
+ inChunkZ = spawnZ - (chunkY*16)
+
+ ## find the first air block
+ while (blockArray[inChunkX, inChunkZ, spawnY] != 0):
+ spawnY += 1
+
+
+ self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ, msg="Spawn"))
+
def go(self, procs):
"""Starts the render. This returns when it is finished"""
@@ -83,6 +148,8 @@ class WorldRenderer(object):
self.minrow = minrow
self.maxrow = maxrow
+ self.findTrueSpawn()
+
def _find_chunkfiles(self):
"""Returns a list of all the chunk file locations, and the file they
correspond to.