Merge branch 'signs' into devel
This commit is contained in:
@@ -443,6 +443,21 @@ values. The valid configuration keys are listed below.
|
|||||||
removed some tiles, you may need to do some manual deletion on the
|
removed some tiles, you may need to do some manual deletion on the
|
||||||
remote side.
|
remote side.
|
||||||
|
|
||||||
|
.. _option_markers:
|
||||||
|
|
||||||
|
``markers``
|
||||||
|
This controls the display of markers, signs, and other points of interest
|
||||||
|
in the output HTML. It should be a list of filter functions.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Setting this configuration option alone does nothing. In order to get
|
||||||
|
markers and signs on our map, you must also run the genPO script. See
|
||||||
|
the :doc:`Signs and markers<signs>` section for more details and documenation.
|
||||||
|
|
||||||
|
|
||||||
|
**Default:** ``[]`` (an empty list)
|
||||||
|
|
||||||
.. _customrendermodes:
|
.. _customrendermodes:
|
||||||
|
|
||||||
Custom Rendermodes and Rendermode Primitives
|
Custom Rendermodes and Rendermode Primitives
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ Documentation Contents
|
|||||||
building
|
building
|
||||||
running
|
running
|
||||||
config
|
config
|
||||||
|
signs
|
||||||
win_tut/windowsguide
|
win_tut/windowsguide
|
||||||
faq
|
faq
|
||||||
design/designdoc
|
design/designdoc
|
||||||
|
|||||||
75
docs/signs.rst
Normal file
75
docs/signs.rst
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
.. _signsmarkers:
|
||||||
|
|
||||||
|
=================
|
||||||
|
Signs and Markers
|
||||||
|
=================
|
||||||
|
|
||||||
|
The Overviewer can display signs, markers, and other points of interest on your
|
||||||
|
map. This works a little differently than it has in the past, so be sure to read
|
||||||
|
these docs carefully.
|
||||||
|
|
||||||
|
In these docs, we use the term POI (or point of interest) to refer to entities and
|
||||||
|
tileentities.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration File
|
||||||
|
==================
|
||||||
|
|
||||||
|
|
||||||
|
Filter Functions
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A filter function is a python function that is used to figure out if a given POI
|
||||||
|
should be part of a markerSet of not. The function should accept one argument
|
||||||
|
(a dictionary, also know as an associative array), and return a boolean::
|
||||||
|
|
||||||
|
def signFilter(poi):
|
||||||
|
"All signs"
|
||||||
|
return poi['id'] == 'Sign'
|
||||||
|
|
||||||
|
The single argument will either a TileEntity, or an Entity taken directly from
|
||||||
|
the chunk file. In this example, this function returns true only if the type
|
||||||
|
of entity is a sign. For more information of TileEntities and Entities, see
|
||||||
|
the `Chunk Format <http://www.minecraftwiki.net/wiki/Chunk_format>`_ page on
|
||||||
|
the Minecraft Wiki.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The doc string ("All signs" in this example) is important. It is the label
|
||||||
|
that appears in your rendered map
|
||||||
|
|
||||||
|
A more advanced filter may also look at other entity fields, such as the sign text::
|
||||||
|
|
||||||
|
def goldFilter(poi):
|
||||||
|
"Gold"
|
||||||
|
return poi['id'] == 'Sign' and (\
|
||||||
|
'gold' in poi['Text1] or
|
||||||
|
'gold' in poi['Text2'])
|
||||||
|
|
||||||
|
This looks for the word 'gold' in either the first or second line of the signtext.
|
||||||
|
|
||||||
|
Since writing these filters can be a little tedious, a set of predefined filters
|
||||||
|
functions are provided. See the :ref:`predefined_filter_functions` section for
|
||||||
|
details.
|
||||||
|
|
||||||
|
Render Dictionary Key
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Each render can specify a list of zero or more filter functions. Each of these
|
||||||
|
filter functions become a selectable item in the 'Signs' drop-down menu in the
|
||||||
|
rendered map. For example::
|
||||||
|
|
||||||
|
renders['myrender'] = {
|
||||||
|
'world': 'myworld',
|
||||||
|
'title': "Example",
|
||||||
|
'markers': [allFilter, anotherFilter],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. _predefined_filter_functions:
|
||||||
|
|
||||||
|
Predefined Filter Functions
|
||||||
|
===========================
|
||||||
|
|
||||||
|
TODO write some filter functions, then document them here
|
||||||
|
|
||||||
132
genPOI.py
Executable file
132
genPOI.py
Executable file
@@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/python2
|
||||||
|
|
||||||
|
'''
|
||||||
|
genPOI.py
|
||||||
|
|
||||||
|
Scans regionsets for TileEntities and Entities, filters them, and writes out
|
||||||
|
POI/marker info.
|
||||||
|
|
||||||
|
A markerSet is list of POIs to display on a tileset. It has a display name,
|
||||||
|
and a group name.
|
||||||
|
|
||||||
|
markersDB.js holds a list of POIs in each group
|
||||||
|
markers.js holds a list of which markerSets are attached to each tileSet
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
from overviewer_core import logger
|
||||||
|
from overviewer_core import nbt
|
||||||
|
from overviewer_core import configParser, world
|
||||||
|
|
||||||
|
helptext = """
|
||||||
|
%prog --config=<config file>"""
|
||||||
|
|
||||||
|
logger.configure()
|
||||||
|
|
||||||
|
def handleSigns(rset, outputdir, render, rname):
|
||||||
|
|
||||||
|
if hasattr(rset, "_pois"):
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("Looking for entities in %r", rset)
|
||||||
|
|
||||||
|
filters = render['markers']
|
||||||
|
rset._pois = dict(TileEntities=[], Entities=[])
|
||||||
|
|
||||||
|
for (x,z,mtime) in rset.iterate_chunks():
|
||||||
|
data = rset.get_chunk(x,z)
|
||||||
|
rset._pois['TileEntities'] += data['TileEntities']
|
||||||
|
rset._pois['Entities'] += data['Entities']
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = OptionParser(usage=helptext)
|
||||||
|
parser.add_option("--config", dest="config", action="store", help="Specify the config file to use.")
|
||||||
|
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
if not options.config:
|
||||||
|
parser.print_help()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse the config file
|
||||||
|
mw_parser = configParser.MultiWorldParser()
|
||||||
|
mw_parser.parse(options.config)
|
||||||
|
try:
|
||||||
|
config = mw_parser.get_validated_config()
|
||||||
|
except Exception:
|
||||||
|
logging.exception("An error was encountered with your configuration. See the info below.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
destdir = config['outputdir']
|
||||||
|
# saves us from creating the same World object over and over again
|
||||||
|
worldcache = {}
|
||||||
|
|
||||||
|
markersets = set()
|
||||||
|
markers = dict()
|
||||||
|
|
||||||
|
for rname, render in config['renders'].iteritems():
|
||||||
|
try:
|
||||||
|
worldpath = config['worlds'][render['world']]
|
||||||
|
except KeyError:
|
||||||
|
logging.error("Render %s's world is '%s', but I could not find a corresponding entry in the worlds dictionary.",
|
||||||
|
rname, render['world'])
|
||||||
|
return 1
|
||||||
|
render['worldname_orig'] = render['world']
|
||||||
|
render['world'] = worldpath
|
||||||
|
|
||||||
|
# find or create the world object
|
||||||
|
if (render['world'] not in worldcache):
|
||||||
|
w = world.World(render['world'])
|
||||||
|
worldcache[render['world']] = w
|
||||||
|
else:
|
||||||
|
w = worldcache[render['world']]
|
||||||
|
|
||||||
|
rset = w.get_regionset(render['dimension'])
|
||||||
|
if rset == None: # indicates no such dimension was found:
|
||||||
|
logging.error("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'], render_name)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
for f in render['markers']:
|
||||||
|
markersets.add((f, rset))
|
||||||
|
name = f.__name__ + hex(hash(f))[-4:] + "_" + hex(hash(rset))[-4:]
|
||||||
|
try:
|
||||||
|
l = markers[rname]
|
||||||
|
l.append(dict(groupName=name, displayName = f.__doc__))
|
||||||
|
except KeyError:
|
||||||
|
markers[rname] = [dict(groupName=name, displayName=f.__doc__),]
|
||||||
|
|
||||||
|
handleSigns(rset, os.path.join(destdir, rname), render, rname)
|
||||||
|
|
||||||
|
logging.info("Done scanning regions")
|
||||||
|
logging.info("Writing out javascript files")
|
||||||
|
markerSetDict = dict()
|
||||||
|
for (flter, rset) in markersets:
|
||||||
|
# generate a unique name for this markerset. it will not be user visible
|
||||||
|
name = flter.__name__ + hex(hash(flter))[-4:] + "_" + hex(hash(rset))[-4:]
|
||||||
|
markerSetDict[name] = dict(created=False, raw=[])
|
||||||
|
for poi in rset._pois['TileEntities']:
|
||||||
|
if flter(poi):
|
||||||
|
markerSetDict[name]['raw'].append(poi)
|
||||||
|
#print markerSetDict
|
||||||
|
|
||||||
|
with open(os.path.join(destdir, "markersDB.js"), "w") as output:
|
||||||
|
output.write("var markersDB=")
|
||||||
|
json.dump(markerSetDict, output, indent=2)
|
||||||
|
output.write(";\n");
|
||||||
|
with open(os.path.join(destdir, "markers.js"), "w") as output:
|
||||||
|
output.write("var markers=")
|
||||||
|
json.dump(markers, output, indent=2)
|
||||||
|
output.write(";\n");
|
||||||
|
with open(os.path.join(destdir, "baseMarkers.js"), "w") as output:
|
||||||
|
output.write("overviewer.util.injectMarkerScript('markersDB.js');\n")
|
||||||
|
output.write("overviewer.util.injectMarkerScript('markers.js');\n")
|
||||||
|
output.write("overviewer.collections.haveSigns=true;\n")
|
||||||
|
logging.info("Done")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -406,6 +406,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
|||||||
# only pass to the TileSet the options it really cares about
|
# only pass to the TileSet the options it really cares about
|
||||||
render['name'] = render_name # perhaps a hack. This is stored here for the asset manager
|
render['name'] = render_name # perhaps a hack. This is stored here for the asset manager
|
||||||
tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist"])
|
tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist"])
|
||||||
|
tileSetOpts.update({"spawn": w.find_true_spawn()}) # TODO find a better way to do this
|
||||||
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir)
|
||||||
tilesets.append(tset)
|
tilesets.append(tset)
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,6 @@ directory.
|
|||||||
self.outputdir = outputdir
|
self.outputdir = outputdir
|
||||||
self.renders = dict()
|
self.renders = dict()
|
||||||
|
|
||||||
# stores Points Of Interest to be mapped with markers
|
|
||||||
# This is a dictionary of lists of dictionaries
|
|
||||||
# Each regionset's name is a key in this dictionary
|
|
||||||
self.POI = dict()
|
|
||||||
|
|
||||||
# look for overviewerConfig in self.outputdir
|
# look for overviewerConfig in self.outputdir
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(self.outputdir, "overviewerConfig.js")) as c:
|
with open(os.path.join(self.outputdir, "overviewerConfig.js")) as c:
|
||||||
@@ -65,13 +60,6 @@ directory.
|
|||||||
return dict()
|
return dict()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def found_poi(self, regionset, poi_type, contents, chunkX, chunkY):
|
|
||||||
if regionset.name not in self.POI.keys():
|
|
||||||
POI[regionset.name] = []
|
|
||||||
# TODO based on the type, so something
|
|
||||||
POI[regionset.name].append
|
|
||||||
|
|
||||||
def initialize(self, tilesets):
|
def initialize(self, tilesets):
|
||||||
"""Similar to finalize() but calls the tilesets' get_initial_data()
|
"""Similar to finalize() but calls the tilesets' get_initial_data()
|
||||||
instead of get_persistent_data() to compile the generated javascript
|
instead of get_persistent_data() to compile the generated javascript
|
||||||
@@ -152,6 +140,12 @@ directory.
|
|||||||
global_assets = os.path.join(util.get_program_path(), "web_assets")
|
global_assets = os.path.join(util.get_program_path(), "web_assets")
|
||||||
mirror_dir(global_assets, self.outputdir)
|
mirror_dir(global_assets, self.outputdir)
|
||||||
|
|
||||||
|
# write a dummy baseMarkers.js if none exists
|
||||||
|
if not os.path.exists(os.path.join(self.outputdir, "baseMarkers.js")):
|
||||||
|
with open(os.path.join(self.outputdir, "baseMarkers.js"), "w") as f:
|
||||||
|
f.write("// if you wants signs, please see genPOI.py\n");
|
||||||
|
|
||||||
|
|
||||||
# create overviewer.js from the source js files
|
# create overviewer.js from the source js files
|
||||||
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src")
|
||||||
if not os.path.isdir(js_src):
|
if not os.path.isdir(js_src):
|
||||||
|
|||||||
@@ -29,7 +29,19 @@ overviewer.collections = {
|
|||||||
*/
|
*/
|
||||||
'infoWindow': null,
|
'infoWindow': null,
|
||||||
|
|
||||||
'worldViews': []
|
'worldViews': [],
|
||||||
|
|
||||||
|
'haveSigns': false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hold the raw marker data for each tilest
|
||||||
|
*/
|
||||||
|
'markerInfo': {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* holds a reference to the spawn marker.
|
||||||
|
*/
|
||||||
|
'spawnMarker': null,
|
||||||
};
|
};
|
||||||
|
|
||||||
overviewer.classes = {
|
overviewer.classes = {
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ overviewer.util = {
|
|||||||
var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'});
|
var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'});
|
||||||
coordsdiv.render();
|
coordsdiv.render();
|
||||||
|
|
||||||
|
if (overviewer.collections.haveSigns) {
|
||||||
|
var signs = new overviewer.views.SignControlView();
|
||||||
|
signs.registerEvents(signs);
|
||||||
|
}
|
||||||
|
|
||||||
|
var spawnmarker = new overviewer.views.SpawnIconView();
|
||||||
|
|
||||||
// Update coords on mousemove
|
// Update coords on mousemove
|
||||||
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
|
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) {
|
||||||
coordsdiv.updateCoords(event.latLng);
|
coordsdiv.updateCoords(event.latLng);
|
||||||
@@ -72,6 +79,7 @@ overviewer.util = {
|
|||||||
overviewer.mapView.updateCurrentTileset();
|
overviewer.mapView.updateCurrentTileset();
|
||||||
|
|
||||||
compass.render();
|
compass.render();
|
||||||
|
spawnmarker.render();
|
||||||
|
|
||||||
// re-center on the last viewport
|
// re-center on the last viewport
|
||||||
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
||||||
@@ -102,6 +110,7 @@ overviewer.util = {
|
|||||||
overviewer.map.setZoom(zoom);
|
overviewer.map.setZoom(zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'});
|
var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'});
|
||||||
@@ -116,15 +125,43 @@ overviewer.util = {
|
|||||||
// Jump to the hash if given
|
// Jump to the hash if given
|
||||||
overviewer.util.initHash();
|
overviewer.util.initHash();
|
||||||
|
|
||||||
|
overviewer.util.initializeMarkers();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
overviewer.util.initializeMapTypes();
|
overviewer.util.initializeMapTypes();
|
||||||
overviewer.util.initializeMap();
|
overviewer.util.initializeMap();
|
||||||
overviewer.util.initializeMarkers();
|
|
||||||
overviewer.util.initializeRegions();
|
overviewer.util.initializeRegions();
|
||||||
overviewer.util.createMapControls();
|
overviewer.util.createMapControls();
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'injectMarkerScript': function(url) {
|
||||||
|
var m = document.createElement('script'); m.type = 'text/javascript'; m.async = false;
|
||||||
|
m.src = url;
|
||||||
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.appendChild(m);
|
||||||
|
},
|
||||||
|
|
||||||
|
'initializeMarkers': function() {
|
||||||
|
return;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
'createMarkerInfoWindow': function(marker) {
|
||||||
|
var windowContent = '<div class="infoWindow"><img src="' + marker.icon +
|
||||||
|
'"/><p>' + marker.title.replace(/\n/g,'<br/>') + '</p></div>';
|
||||||
|
var infowindow = new google.maps.InfoWindow({
|
||||||
|
'content': windowContent
|
||||||
|
});
|
||||||
|
google.maps.event.addListener(marker, 'click', function() {
|
||||||
|
if (overviewer.collections.infoWindow) {
|
||||||
|
overviewer.collections.infoWindow.close();
|
||||||
|
}
|
||||||
|
infowindow.open(overviewer.map, marker);
|
||||||
|
overviewer.collections.infoWindow = infowindow;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This adds some methods to these classes because Javascript is stupid
|
* This adds some methods to these classes because Javascript is stupid
|
||||||
* and this seems like the best way to avoid re-creating the same methods
|
* and this seems like the best way to avoid re-creating the same methods
|
||||||
@@ -239,7 +276,6 @@ overviewer.util = {
|
|||||||
|
|
||||||
var zoomLevels = model.get("zoomLevels");
|
var zoomLevels = model.get("zoomLevels");
|
||||||
var north_direction = model.get('north_direction');
|
var north_direction = model.get('north_direction');
|
||||||
//console.log("fromWorldToLatLng: north_direction is %r", north_direction);
|
|
||||||
|
|
||||||
// the width and height of all the highest-zoom tiles combined,
|
// the width and height of all the highest-zoom tiles combined,
|
||||||
// inverted
|
// inverted
|
||||||
@@ -406,7 +442,6 @@ overviewer.util = {
|
|||||||
// save this info is a nice easy to parse format
|
// save this info is a nice easy to parse format
|
||||||
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
||||||
currentWorldView.options.lastViewport = [x,y,z,zoom];
|
currentWorldView.options.lastViewport = [x,y,z,zoom];
|
||||||
//console.log("Updated lastViewport: %r" , [x,y,z,zoom]);
|
|
||||||
window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + w + "/" + maptype);
|
window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + w + "/" + maptype);
|
||||||
},
|
},
|
||||||
'updateHash': function() {
|
'updateHash': function() {
|
||||||
|
|||||||
@@ -3,13 +3,9 @@ overviewer.views= {}
|
|||||||
|
|
||||||
overviewer.views.WorldView = Backbone.View.extend({
|
overviewer.views.WorldView = Backbone.View.extend({
|
||||||
initialize: function(opts) {
|
initialize: function(opts) {
|
||||||
//console.log("WorldView::initialize()");
|
|
||||||
//console.log(this.model.get("tileSets"));
|
|
||||||
this.options.mapTypes = [];
|
this.options.mapTypes = [];
|
||||||
this.options.mapTypeIds = [];
|
this.options.mapTypeIds = [];
|
||||||
this.model.get("tileSets").each(function(tset, index, list) {
|
this.model.get("tileSets").each(function(tset, index, list) {
|
||||||
//console.log(" eaching");
|
|
||||||
//console.log(" Working on tileset %s" , tset.get("name"));
|
|
||||||
var ops = {
|
var ops = {
|
||||||
getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), tset.get("imgextension")),
|
getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), tset.get("imgextension")),
|
||||||
'tileSize': new google.maps.Size(
|
'tileSize': new google.maps.Size(
|
||||||
@@ -57,7 +53,6 @@ overviewer.views.WorldSelectorView = Backbone.View.extend({
|
|||||||
"change select": "changeWorld"
|
"change select": "changeWorld"
|
||||||
},
|
},
|
||||||
changeWorld: function() {
|
changeWorld: function() {
|
||||||
//console.log("change world!");
|
|
||||||
var selectObj = this.$("select")[0];
|
var selectObj = this.$("select")[0];
|
||||||
var selectedOption = selectObj.options[selectObj.selectedIndex];
|
var selectedOption = selectObj.options[selectObj.selectedIndex];
|
||||||
|
|
||||||
@@ -120,11 +115,8 @@ overviewer.views.CoordboxView = Backbone.View.extend({
|
|||||||
|
|
||||||
overviewer.views.GoogleMapView = Backbone.View.extend({
|
overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||||
initialize: function(opts) {
|
initialize: function(opts) {
|
||||||
//console.log(this);
|
|
||||||
this.options.map = null;
|
this.options.map = null;
|
||||||
var curWorld = this.model.get("currentWorldView").model;
|
var curWorld = this.model.get("currentWorldView").model;
|
||||||
//console.log("Current world:");
|
|
||||||
//console.log(curWorld);
|
|
||||||
|
|
||||||
var curTset = curWorld.get("tileSets").at(0);
|
var curTset = curWorld.get("tileSets").at(0);
|
||||||
|
|
||||||
@@ -145,12 +137,6 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
|||||||
|
|
||||||
var mapOptions = {};
|
var mapOptions = {};
|
||||||
//
|
//
|
||||||
curWorld.get("tileSets").each(function(elem, index, list) {
|
|
||||||
//console.log("Setting up map for:");
|
|
||||||
//console.log(elem);
|
|
||||||
//console.log("for %s generating url func with %s and %s", elem.get("name"), elem.get("path"), elem.get("base"));
|
|
||||||
|
|
||||||
});
|
|
||||||
// init the map with some default options. use the first tileset in the first world
|
// init the map with some default options. use the first tileset in the first world
|
||||||
this.options.mapOptions = {
|
this.options.mapOptions = {
|
||||||
zoom: curTset.get("defaultZoom"),
|
zoom: curTset.get("defaultZoom"),
|
||||||
@@ -174,7 +160,6 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
|||||||
// register every ImageMapType with the map
|
// register every ImageMapType with the map
|
||||||
$.each(overviewer.collections.worldViews, function( index, worldView) {
|
$.each(overviewer.collections.worldViews, function( index, worldView) {
|
||||||
$.each(worldView.options.mapTypes, function(i_index, maptype) {
|
$.each(worldView.options.mapTypes, function(i_index, maptype) {
|
||||||
//console.log("registered %s with the maptype registery", worldView.model.get("name") + maptype.shortname);
|
|
||||||
overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId +
|
overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId +
|
||||||
worldView.model.get("name") + maptype.shortname , maptype);
|
worldView.model.get("name") + maptype.shortname , maptype);
|
||||||
});
|
});
|
||||||
@@ -185,7 +170,6 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
|||||||
* Should be called when the current world has changed in GoogleMapModel
|
* Should be called when the current world has changed in GoogleMapModel
|
||||||
*/
|
*/
|
||||||
render: function() {
|
render: function() {
|
||||||
//console.log("GoogleMapView::render()");
|
|
||||||
var view = this.model.get("currentWorldView");
|
var view = this.model.get("currentWorldView");
|
||||||
this.options.mapOptions.mapTypeControlOptions = {
|
this.options.mapOptions.mapTypeControlOptions = {
|
||||||
mapTypeIds: view.options.mapTypeIds};
|
mapTypeIds: view.options.mapTypeIds};
|
||||||
@@ -200,14 +184,11 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
|||||||
* Keeps track of the currently visible tileset
|
* Keeps track of the currently visible tileset
|
||||||
*/
|
*/
|
||||||
updateCurrentTileset: function() {
|
updateCurrentTileset: function() {
|
||||||
//console.log("GoogleMapView::updateCurrentTileset()");
|
|
||||||
var currentWorldView = this.model.get("currentWorldView");
|
var currentWorldView = this.model.get("currentWorldView");
|
||||||
var gmapCurrent = overviewer.map.getMapTypeId();
|
var gmapCurrent = overviewer.map.getMapTypeId();
|
||||||
for (id in currentWorldView.options.mapTypeIds) {
|
for (id in currentWorldView.options.mapTypeIds) {
|
||||||
if (currentWorldView.options.mapTypeIds[id] == gmapCurrent) {
|
if (currentWorldView.options.mapTypeIds[id] == gmapCurrent) {
|
||||||
//console.log("updating currenttileset");
|
|
||||||
this.options.currentTileSet = currentWorldView.model.get("tileSets").at(id);
|
this.options.currentTileSet = currentWorldView.model.get("tileSets").at(id);
|
||||||
//console.log(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,3 +200,188 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SignControlView
|
||||||
|
*/
|
||||||
|
overviewer.views.SignControlView = Backbone.View.extend({
|
||||||
|
/** SignControlView::initialize
|
||||||
|
*/
|
||||||
|
initialize: function(opts) {
|
||||||
|
$(this.el).addClass("customControl");
|
||||||
|
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.el);
|
||||||
|
|
||||||
|
},
|
||||||
|
registerEvents: function(me) {
|
||||||
|
google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function(event) {
|
||||||
|
overviewer.mapView.updateCurrentTileset();
|
||||||
|
|
||||||
|
// workaround IE issue. bah!
|
||||||
|
if (typeof markers=="undefined") { return; }
|
||||||
|
me.render();
|
||||||
|
// hide markers, if necessary
|
||||||
|
// for each markerSet, check:
|
||||||
|
// if the markerSet isnot part of this tileset, hide all of the markers
|
||||||
|
var curMarkerSet = overviewer.mapView.options.currentTileSet.attributes.path;
|
||||||
|
var dataRoot = markers[curMarkerSet];
|
||||||
|
if (!dataRoot) {
|
||||||
|
// this tileset has no signs, so hide all of them
|
||||||
|
for (markerSet in markersDB) {
|
||||||
|
if (markersDB[markerSet].created) {
|
||||||
|
jQuery.each(markersDB[markerSet].raw, function(i, elem) {
|
||||||
|
elem.markerObj.setVisible(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var groupsForThisTileSet = jQuery.map(dataRoot, function(elem, i) { return elem.groupName;})
|
||||||
|
for (markerSet in markersDB) {
|
||||||
|
if (jQuery.inArray(markerSet, groupsForThisTileSet) == -1){
|
||||||
|
// hide these
|
||||||
|
if (markersDB[markerSet].created) {
|
||||||
|
jQuery.each(markersDB[markerSet].raw, function(i, elem) {
|
||||||
|
elem.markerObj.setVisible(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
markersDB[markerSet].checked=false;
|
||||||
|
}
|
||||||
|
// make sure the checkboxes checked if necessary
|
||||||
|
$("[_mc_groupname=" + markerSet + "]").attr("checked", markersDB[markerSet].checked);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* SignControlView::render
|
||||||
|
*/
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
var curMarkerSet = overviewer.mapView.options.currentTileSet.attributes.path;
|
||||||
|
//var dataRoot = overviewer.collections.markerInfo[curMarkerSet];
|
||||||
|
var dataRoot = markers[curMarkerSet];
|
||||||
|
|
||||||
|
this.el.innerHTML=""
|
||||||
|
|
||||||
|
// if we have no markerSets for this tileset, do nothing:
|
||||||
|
if (!dataRoot) { return; }
|
||||||
|
|
||||||
|
|
||||||
|
var controlText = document.createElement('DIV');
|
||||||
|
controlText.innerHTML = "Signs";
|
||||||
|
|
||||||
|
var controlBorder = document.createElement('DIV');
|
||||||
|
$(controlBorder).addClass('top');
|
||||||
|
this.el.appendChild(controlBorder);
|
||||||
|
controlBorder.appendChild(controlText);
|
||||||
|
|
||||||
|
var dropdownDiv = document.createElement('DIV');
|
||||||
|
$(dropdownDiv).addClass('dropDown');
|
||||||
|
this.el.appendChild(dropdownDiv);
|
||||||
|
dropdownDiv.innerHTML='';
|
||||||
|
|
||||||
|
// add the functionality to toggle visibility of the items
|
||||||
|
$(controlText).click(function() {
|
||||||
|
$(controlBorder).toggleClass('top-active');
|
||||||
|
$(dropdownDiv).toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// add some menus
|
||||||
|
for (i in dataRoot) {
|
||||||
|
var group = dataRoot[i];
|
||||||
|
this.addItem({label: group.displayName, groupName:group.groupName, action:function(this_item, checked) {
|
||||||
|
markersDB[this_item.groupName].checked = checked;
|
||||||
|
jQuery.each(markersDB[this_item.groupName].raw, function(i, elem) {
|
||||||
|
elem.markerObj.setVisible(checked);
|
||||||
|
});
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
iconURL = overviewerConfig.CONST.image.signMarker;
|
||||||
|
//dataRoot['markers'] = [];
|
||||||
|
//
|
||||||
|
for (i in dataRoot) {
|
||||||
|
var groupName = dataRoot[i].groupName;
|
||||||
|
if (!markersDB[groupName].created) {
|
||||||
|
for (j in markersDB[groupName].raw) {
|
||||||
|
var entity = markersDB[groupName].raw[j];
|
||||||
|
var marker = new google.maps.Marker({
|
||||||
|
'position': overviewer.util.fromWorldToLatLng(entity.x,
|
||||||
|
entity.y, entity.z, overviewer.mapView.options.currentTileSet),
|
||||||
|
'map': overviewer.map,
|
||||||
|
'title': jQuery.trim(entity.Text1 + "\n" + entity.Text2 + "\n" + entity.Text3 + "\n" + entity.Text4),
|
||||||
|
'icon': iconURL,
|
||||||
|
'visible': false
|
||||||
|
});
|
||||||
|
if (entity['id'] == 'Sign') {
|
||||||
|
overviewer.util.createMarkerInfoWindow(marker);
|
||||||
|
}
|
||||||
|
jQuery.extend(entity, {markerObj: marker});
|
||||||
|
}
|
||||||
|
markersDB[groupName].created = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
addItem: function(item) {
|
||||||
|
var itemDiv = document.createElement('div');
|
||||||
|
var itemInput = document.createElement('input');
|
||||||
|
itemInput.type='checkbox';
|
||||||
|
|
||||||
|
// give it a name
|
||||||
|
$(itemInput).data('label',item.label);
|
||||||
|
$(itemInput).attr("_mc_groupname", item.groupName);
|
||||||
|
jQuery(itemInput).click((function(local_item) {
|
||||||
|
return function(e) {
|
||||||
|
item.action(local_item, e.target.checked);
|
||||||
|
};
|
||||||
|
})(item));
|
||||||
|
|
||||||
|
this.$(".dropDown")[0].appendChild(itemDiv);
|
||||||
|
itemDiv.appendChild(itemInput);
|
||||||
|
var textNode = document.createElement('text');
|
||||||
|
if(item.icon) {
|
||||||
|
textNode.innerHTML = '<img width="15" height="15" src="' +
|
||||||
|
item.icon + '">' + item.label + '<br/>';
|
||||||
|
} else {
|
||||||
|
textNode.innerHTML = item.label + '<br/>';
|
||||||
|
}
|
||||||
|
|
||||||
|
itemDiv.appendChild(textNode);
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpawnIconView
|
||||||
|
*/
|
||||||
|
overviewer.views.SpawnIconView = Backbone.View.extend({
|
||||||
|
render: function() {
|
||||||
|
//
|
||||||
|
var curTileSet = overviewer.mapView.options.currentTileSet;
|
||||||
|
if (overviewer.collections.spawnMarker) {
|
||||||
|
overviewer.collections.spawnMarker.setMap(null);
|
||||||
|
overviewer.collections.spawnMarker = null;
|
||||||
|
}
|
||||||
|
var spawn = curTileSet.get("spawn");
|
||||||
|
if (spawn) {
|
||||||
|
overviewer.collections.spawnMarker = new google.maps.Marker({
|
||||||
|
'position': overviewer.util.fromWorldToLatLng(spawn[0],
|
||||||
|
spawn[1], spawn[2], overviewer.mapView.options.currentTileSet),
|
||||||
|
'map': overviewer.map,
|
||||||
|
'title': 'spawn',
|
||||||
|
'icon': overviewerConfig.CONST.image.spawnMarker,
|
||||||
|
'visible': false
|
||||||
|
});
|
||||||
|
overviewer.collections.spawnMarker.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<script type="text/javascript" src="backbone.js"></script>
|
<script type="text/javascript" src="backbone.js"></script>
|
||||||
<script type="text/javascript" src="overviewerConfig.js"></script>
|
<script type="text/javascript" src="overviewerConfig.js"></script>
|
||||||
<script type="text/javascript" src="overviewer.js"></script>
|
<script type="text/javascript" src="overviewer.js"></script>
|
||||||
|
<script type="text/javascript" src="baseMarkers.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ renders = Setting(required=True, default=util.OrderedDict(),
|
|||||||
"rerenderprob": Setting(required=True, validator=validateFloat, default=0),
|
"rerenderprob": Setting(required=True, validator=validateFloat, default=0),
|
||||||
"crop": Setting(required=False, validator=validateCrop, default=None),
|
"crop": Setting(required=False, validator=validateCrop, default=None),
|
||||||
"changelist": Setting(required=False, validator=validateStr, default=None),
|
"changelist": Setting(required=False, validator=validateStr, default=None),
|
||||||
|
"markers": Setting(required=False, validator=validateMarkers, default=[]),
|
||||||
|
|
||||||
# Remove this eventually (once people update their configs)
|
# Remove this eventually (once people update their configs)
|
||||||
"worldname": Setting(required=False, default=None,
|
"worldname": Setting(required=False, default=None,
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ def checkBadEscape(s):
|
|||||||
fixed = True
|
fixed = True
|
||||||
return (fixed, fixed_string)
|
return (fixed, fixed_string)
|
||||||
|
|
||||||
|
def validateMarkers(filterlist):
|
||||||
|
if type(filterlist) != list:
|
||||||
|
raise ValidationException("Markers must specify a list of filters")
|
||||||
|
for x in filterlist:
|
||||||
|
if not callable(x):
|
||||||
|
raise ValidationException("%r must be a function"% x)
|
||||||
|
return filterlist
|
||||||
|
|
||||||
def validateWorldPath(worldpath):
|
def validateWorldPath(worldpath):
|
||||||
_, worldpath = checkBadEscape(worldpath)
|
_, worldpath = checkBadEscape(worldpath)
|
||||||
|
|||||||
@@ -518,6 +518,8 @@ class TileSet(object):
|
|||||||
last_rendertime = self.max_chunk_mtime,
|
last_rendertime = self.max_chunk_mtime,
|
||||||
imgextension = self.imgextension,
|
imgextension = self.imgextension,
|
||||||
)
|
)
|
||||||
|
if (self.regionset.get_type() == "overworld"):
|
||||||
|
d.update({"spawn": self.options.get("spawn")})
|
||||||
try:
|
try:
|
||||||
d['north_direction'] = self.regionset.north_dir
|
d['north_direction'] = self.regionset.north_dir
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import os.path
|
|||||||
from glob import glob
|
from glob import glob
|
||||||
import logging
|
import logging
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
@@ -91,8 +93,21 @@ class World(object):
|
|||||||
if not os.path.exists(os.path.join(self.worlddir, "level.dat")):
|
if not os.path.exists(os.path.join(self.worlddir, "level.dat")):
|
||||||
raise ValueError("level.dat not found in %s" % self.worlddir)
|
raise ValueError("level.dat not found in %s" % self.worlddir)
|
||||||
|
|
||||||
# Hard-code this to only work with format version 19133, "Anvil"
|
|
||||||
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
||||||
|
# it seems that reading a level.dat file is unstable, particularly with respect
|
||||||
|
# to the spawnX,Y,Z variables. So we'll try a few times to get a good reading
|
||||||
|
# empirically, it seems that 0,50,0 is a "bad" reading
|
||||||
|
retrycount = 0
|
||||||
|
while (data['SpawnX'] == 0 and data['SpawnY'] == 50 and data['SpawnZ'] ==0 ):
|
||||||
|
logging.debug("bad level read, retrying")
|
||||||
|
time.sleep(random.random())
|
||||||
|
data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1]['Data']
|
||||||
|
retrycount += 1
|
||||||
|
if retrycount > 10:
|
||||||
|
raise Exception("Failed to correctly read level.dat")
|
||||||
|
|
||||||
|
|
||||||
|
# Hard-code this to only work with format version 19133, "Anvil"
|
||||||
if not ('version' in data and data['version'] == 19133):
|
if not ('version' in data and data['version'] == 19133):
|
||||||
logging.critical("Sorry, This version of Minecraft-Overviewer only works with the 'Anvil' chunk format")
|
logging.critical("Sorry, This version of Minecraft-Overviewer only works with the 'Anvil' chunk format")
|
||||||
raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir)
|
raise ValueError("World at %s is not compatible with Overviewer" % self.worlddir)
|
||||||
@@ -163,7 +178,7 @@ class World(object):
|
|||||||
# location
|
# location
|
||||||
|
|
||||||
## read spawn info from level.dat
|
## read spawn info from level.dat
|
||||||
data = self.data
|
data = self.leveldat
|
||||||
disp_spawnX = spawnX = data['SpawnX']
|
disp_spawnX = spawnX = data['SpawnX']
|
||||||
spawnY = data['SpawnY']
|
spawnY = data['SpawnY']
|
||||||
disp_spawnZ = spawnZ = data['SpawnZ']
|
disp_spawnZ = spawnZ = data['SpawnZ']
|
||||||
@@ -175,24 +190,32 @@ class World(object):
|
|||||||
## clamp spawnY to a sane value, in-chunk value
|
## clamp spawnY to a sane value, in-chunk value
|
||||||
if spawnY < 0:
|
if spawnY < 0:
|
||||||
spawnY = 0
|
spawnY = 0
|
||||||
if spawnY > 127:
|
if spawnY > 255:
|
||||||
spawnY = 127
|
spawnY = 255
|
||||||
|
|
||||||
# Open up the chunk that the spawn is in
|
# Open up the chunk that the spawn is in
|
||||||
regionset = self.get_regionset(0)
|
regionset = self.get_regionset("overworld")
|
||||||
try:
|
try:
|
||||||
chunk = regionset.get_chunk(chunkX, chunkZ)
|
chunk = regionset.get_chunk(chunkX, chunkZ)
|
||||||
except ChunkDoesntExist:
|
except ChunkDoesntExist:
|
||||||
return (spawnX, spawnY, spawnZ)
|
return (spawnX, spawnY, spawnZ)
|
||||||
|
|
||||||
|
def getBlock(y):
|
||||||
|
"This is stupid and slow but I don't care"
|
||||||
|
targetSection = spawnY//16
|
||||||
|
for section in chunk['Sections']:
|
||||||
|
if section['Y'] == targetSection:
|
||||||
|
blockArray = section['Blocks']
|
||||||
|
return blockArray[inChunkX, inChunkZ, y % 16]
|
||||||
|
|
||||||
|
|
||||||
blockArray = chunk['Blocks']
|
|
||||||
|
|
||||||
## The block for spawn *within* the chunk
|
## The block for spawn *within* the chunk
|
||||||
inChunkX = spawnX - (chunkX*16)
|
inChunkX = spawnX - (chunkX*16)
|
||||||
inChunkZ = spawnZ - (chunkZ*16)
|
inChunkZ = spawnZ - (chunkZ*16)
|
||||||
|
|
||||||
## find the first air block
|
## find the first air block
|
||||||
while (blockArray[inChunkX, inChunkZ, spawnY] != 0) and spawnY < 127:
|
while (getBlock(spawnY) != 0) and spawnY < 256:
|
||||||
spawnY += 1
|
spawnY += 1
|
||||||
|
|
||||||
return spawnX, spawnY, spawnZ
|
return spawnX, spawnY, spawnZ
|
||||||
|
|||||||
Reference in New Issue
Block a user