diff --git a/genPOI.py b/genPOI.py new file mode 100755 index 0000000..6bb0123 --- /dev/null +++ b/genPOI.py @@ -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=""" + +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() diff --git a/overviewer_core/assetmanager.py b/overviewer_core/assetmanager.py index 131d986..6407c1d 100644 --- a/overviewer_core/assetmanager.py +++ b/overviewer_core/assetmanager.py @@ -42,11 +42,6 @@ directory. self.outputdir = outputdir 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 try: with open(os.path.join(self.outputdir, "overviewerConfig.js")) as c: @@ -65,13 +60,6 @@ directory. 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): """Similar to finalize() but calls the tilesets' get_initial_data() 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") 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 js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src") if not os.path.isdir(js_src): diff --git a/overviewer_core/data/js_src/overviewer.js b/overviewer_core/data/js_src/overviewer.js index 2f9d651..a8ad80b 100644 --- a/overviewer_core/data/js_src/overviewer.js +++ b/overviewer_core/data/js_src/overviewer.js @@ -29,7 +29,14 @@ overviewer.collections = { */ 'infoWindow': null, - 'worldViews': [] + 'worldViews': [], + + 'haveSigns': false, + + /** + * Hold the raw marker data for each tilest + */ + 'markerInfo': {} }; overviewer.classes = { diff --git a/overviewer_core/data/js_src/util.js b/overviewer_core/data/js_src/util.js index 2daa770..143ba5f 100644 --- a/overviewer_core/data/js_src/util.js +++ b/overviewer_core/data/js_src/util.js @@ -58,6 +58,11 @@ overviewer.util = { var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'}); coordsdiv.render(); + if (overviewer.collections.haveSigns) { + var signs = new overviewer.views.SignControlView(); + signs.registerEvents(signs); + } + // Update coords on mousemove google.maps.event.addListener(overviewer.map, 'mousemove', function (event) { coordsdiv.updateCoords(event.latLng); @@ -102,6 +107,7 @@ overviewer.util = { overviewer.map.setZoom(zoom); } + }); var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'}); @@ -116,15 +122,43 @@ overviewer.util = { // Jump to the hash if given overviewer.util.initHash(); + overviewer.util.initializeMarkers(); /* overviewer.util.initializeMapTypes(); overviewer.util.initializeMap(); - overviewer.util.initializeMarkers(); overviewer.util.initializeRegions(); 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 = '

' + marker.title.replace(/\n/g,'
') + '

'; + 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 * and this seems like the best way to avoid re-creating the same methods diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js index 70867e0..7a1921e 100644 --- a/overviewer_core/data/js_src/views.js +++ b/overviewer_core/data/js_src/views.js @@ -219,3 +219,159 @@ 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(); + 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; + console.log("sign control things %r is the new current tileset", curMarkerSet); + var dataRoot = markers[curMarkerSet]; + var groupsForThisTileSet = jQuery.map(dataRoot, function(elem, i) { return elem.groupName;}) + for (markerSet in markersDB) { + console.log("checking to see if markerset %r should be hidden (is it not in %r)", markerSet, groupsForThisTileSet); + if (jQuery.inArray(markerSet, groupsForThisTileSet) == -1){ + // hide these + console.log("nope, going to hide these"); + if (markersDB[markerSet].created) { + jQuery.each(markersDB[markerSet].raw, function(i, elem) { + //console.log("attempting to set %r to visible(%r)", elem.markerObj, checked); + elem.markerObj.setVisible(false); + }); + } + } else { + //make sure the checkbox is checked + //TODO fix this + //console.log("trying to checkbox for " + markerSet); + //console.log($("[_mc_groupname=" + markerSet + "]")); + } + + } + + }); + + }, + /** + * SignControlView::render + */ + render: function() { + + var curMarkerSet = overviewer.mapView.options.currentTileSet.attributes.path; + console.log(curMarkerSet); + //var dataRoot = overviewer.collections.markerInfo[curMarkerSet]; + var dataRoot = markers[curMarkerSet]; + + console.log(dataRoot); + + // before re-building this control, we need to hide all currently displayed signs + // TODO + + this.el.innerHTML="" + + 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]; + console.log(group); + this.addItem({label: group.displayName, groupName:group.groupName, action:function(this_item, checked) { + console.log("%r is %r", this_item, checked); + console.log("group name is %r", this_item.groupName); + jQuery.each(markersDB[this_item.groupName].raw, function(i, elem) { + //console.log("attempting to set %r to visible(%r)", elem.markerObj, checked); + 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); + } + console.log("Added marker for %r", entity); + 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 = '' + item.label + '
'; + } else { + textNode.innerHTML = item.label + '
'; + } + + itemDiv.appendChild(textNode); + + + }, +}); + diff --git a/overviewer_core/data/web_assets/index.html b/overviewer_core/data/web_assets/index.html index 0766727..e8010a0 100644 --- a/overviewer_core/data/web_assets/index.html +++ b/overviewer_core/data/web_assets/index.html @@ -17,6 +17,7 @@ + diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index 67fee81..5773dd2 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -75,6 +75,7 @@ renders = Setting(required=True, default=util.OrderedDict(), "rerenderprob": Setting(required=True, validator=validateFloat, default=0), "crop": Setting(required=False, validator=validateCrop, 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) "worldname": Setting(required=False, default=None, diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index f6fdd77..8aedc30 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -43,6 +43,13 @@ def checkBadEscape(s): fixed = True 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): _, worldpath = checkBadEscape(worldpath)