Added initial work on a structure to allow map annocations
The spawn point is automatically added to the gmap as a marker. Adding other markers (signs, mob spawners, etc) should be fairly easy. Note: the math that converts from in-game block coordinates to pixel coordinates is iffy. it requires a careful codereview
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; }
|
||||
#mcmap { height: 100% }
|
||||
</style>
|
||||
<script type="text/javascript" src="markers.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="http://maps.google.com/maps/api/js?sensor=false">
|
||||
</script>
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
67
world.py
67
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.
|
||||
|
||||
Reference in New Issue
Block a user