Created
July 22, 2011 12:05
-
-
Save agrif/1099315 to your computer and use it in GitHub Desktop.
a possible fix for overviewer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var overviewer = { | |
/** | |
* This holds the map, probably the most important var in this file | |
*/ | |
'map': null, | |
/** | |
* These are collections of data used in various places | |
*/ | |
'collections': { | |
/** | |
* A list of lists of raw marker data objects, this will allow for an | |
* arbitrary number of marker data sources. This replaces the old | |
* markerData var from markers.js. Now you can add markers by including | |
* a file with: | |
* overviewer.collections.markerDatas.push([<your list of markers>]); | |
*/ | |
'markerDatas': [], | |
/** | |
* The actual Marker objects are stored here. | |
*/ | |
'markers': {}, | |
/** | |
* Same as markerDatas, list of lists of raw region objects. | |
*/ | |
'regionDatas': [], | |
/** | |
* The actual Region objects. | |
*/ | |
'regions': {}, | |
/** | |
* Overlay mapTypes (like Spawn) will go in here. | |
*/ | |
'overlays': [], | |
/** | |
* MapTypes that aren't overlays will end up in here. | |
*/ | |
'mapTypes': {}, | |
/** | |
* The mapType names are in here. | |
*/ | |
'mapTypeIds': [], | |
/** | |
* This is the current infoWindow object, we keep track of it so that | |
* there is only one open at a time. | |
*/ | |
'infoWindow': null | |
}, | |
'util': { | |
/** | |
* General initialization function, called when the page is loaded. | |
* Probably shouldn't need changing unless some very different kind of new | |
* feature gets added. | |
*/ | |
'initialize': function() { | |
overviewer.util.initializeClassPrototypes(); | |
overviewer.util.initializeMapTypes(); | |
overviewer.util.initializeMap(); | |
overviewer.util.initializeMarkers(); | |
overviewer.util.initializeRegions(); | |
overviewer.util.createMapControls(); | |
}, | |
/** | |
* 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 | |
* on each object at object creation time. | |
*/ | |
'initializeClassPrototypes': function() { | |
overviewer.classes.MapProjection.prototype.fromLatLngToPoint = function(latLng) { | |
var x = latLng.lng() * overviewerConfig.CONST.tileSize; | |
var y = latLng.lat() * overviewerConfig.CONST.tileSize; | |
return new google.maps.Point(x, y); | |
}; | |
overviewer.classes.MapProjection.prototype.fromPointToLatLng = function(point) { | |
var lng = point.x * this.inverseTileSize; | |
var lat = point.y * this.inverseTileSize; | |
return new google.maps.LatLng(lat, lng); | |
}; | |
overviewer.classes.CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { | |
var div = ownerDocument.createElement('DIV'); | |
div.innerHTML = '(' + coord.x + ', ' + coord.y + ', ' + zoom + | |
')' + '<br />'; | |
//TODO: figure out how to get the current mapType, I think this | |
//will add the maptile url to the grid thing once it works | |
//div.innerHTML += overviewer.collections.mapTypes[0].getTileUrl(coord, zoom); | |
//this should probably just have a css class | |
div.style.width = this.tileSize.width + 'px'; | |
div.style.height = this.tileSize.height + 'px'; | |
div.style.fontSize = '10px'; | |
div.style.borderStyle = 'solid'; | |
div.style.borderWidth = '1px'; | |
div.style.borderColor = '#AAAAAA'; | |
return div; | |
}; | |
}, | |
/** | |
* Quote an arbitrary string for use in a regex matcher. | |
* WTB parametized regexes, JavaScript... | |
* | |
* From http://kevin.vanzonneveld.net | |
* original by: booeyOH | |
* improved by: Ates Goral (http://magnetiq.com) | |
* improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) | |
* bugfixed by: Onno Marsman | |
* example 1: preg_quote("$40"); | |
* returns 1: '\$40' | |
* example 2: preg_quote("*RRRING* Hello?"); | |
* returns 2: '\*RRRING\* Hello\?' | |
* example 3: preg_quote("\\.+*?[^]$(){}=!<>|:"); | |
* returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:' | |
*/ | |
"pregQuote": function(str) { | |
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); | |
}, | |
/** | |
* Setup the varous mapTypes before we actually create the map. This used | |
* to be a bunch of crap down at the bottom of functions.js | |
*/ | |
'initializeMapTypes': function() { | |
var mapOptions = {}; | |
for (i in overviewerConfig.mapTypes) { | |
var view = overviewerConfig.mapTypes[i]; | |
var imageFormat = view.imgformat ? view.imgformat : 'png'; | |
mapOptions[view.label] = { | |
'getTileUrl': overviewer.gmap.getTileUrlGenerator(view.path, | |
view.base, imageFormat), | |
'tileSize': new google.maps.Size( | |
overviewerConfig.CONST.tileSize, | |
overviewerConfig.CONST.tileSize), | |
'maxZoom': overviewerConfig.map.maxZoom, | |
'minZoom': overviewerConfig.map.minZoom, | |
'isPng': imageFormat.toLowerCase() == 'png' | |
} | |
overviewer.collections.mapTypes[view.label] = new google.maps.ImageMapType( | |
mapOptions[view.label]); | |
overviewer.collections.mapTypes[view.label].name = view.label; | |
overviewer.collections.mapTypes[view.label].alt = 'Minecraft ' + | |
view.label + ' Map'; | |
overviewer.collections.mapTypes[view.label].projection = | |
new overviewer.classes.MapProjection(); | |
if (view.overlay) { | |
overviewer.collections.overlays.push( | |
overviewer.collections.mapTypes[view.label]); | |
} else { | |
overviewer.collections.mapTypeIds.push( | |
overviewerConfig.CONST.mapDivId + view.label); | |
} | |
} | |
}, | |
/** | |
* This is where the magic happens. We setup the map with all it's | |
* options. The query string is also parsed here so we can know if | |
* we should be looking at a particular point on the map or just use | |
* the default view. | |
*/ | |
'initializeMap': function() { | |
var defaultCenter = overviewer.util.fromWorldToLatLng( | |
overviewerConfig.map.center[0], overviewerConfig.map.center[1], | |
overviewerConfig.map.center[2]); | |
var lat = defaultCenter.lat(); | |
var lng = defaultCenter.lng(); | |
var zoom = overviewerConfig.map.defaultZoom; | |
var mapcenter; | |
var queryParams = overviewer.util.parseQueryString(); | |
if (queryParams.debug) { | |
overviewerConfig.map.debug=true; | |
} | |
if (queryParams.lat) { | |
lat = parseFloat(queryParams.lat); | |
} | |
if (queryParams.lng) { | |
lng = parseFloat(queryParams.lng); | |
} | |
if (queryParams.zoom) { | |
if (queryParams.zoom == 'max') { | |
zoom = overviewerConfig.map.maxZoom; | |
} else if (queryParams.zoom == 'min') { | |
zoom = overviewerConfig.map.minZoom; | |
} else { | |
zoom = parseInt(queryParams.zoom); | |
if (zoom < 0 && zoom + overviewerConfig.map.maxZoom >= 0) { | |
//if zoom is negative, try to treat as "zoom out from max zoom" | |
zoom += overviewerConfig.map.maxZoom; | |
} else { | |
//fall back to default zoom | |
zoom = overviewerConfig.map.defaultZoom; | |
} | |
} | |
} | |
if (queryParams.x && queryParams.y && queryParams.z) { | |
mapcenter = overviewer.util.fromWorldToLatLng(queryParams.x, | |
queryParams.y, queryParams.z); | |
// Add a market indicating the user-supplied position | |
overviewer.collections.markerDatas.push([{ | |
'msg': 'Coordinates ' + queryParams.x + ', ' + | |
queryParams.y + ', ' + queryParams.z, | |
'y': parseFloat(queryParams.y), | |
'x': parseFloat(queryParams.x), | |
'z': parseFloat(queryParams.z), | |
'type': 'querypos'}]); | |
} else { | |
mapcenter = new google.maps.LatLng(lat, lng); | |
} | |
var mapOptions = { | |
zoom: zoom, | |
center: mapcenter, | |
panControl: overviewerConfig.map.controls.pan, | |
scaleControl: false, | |
mapTypeControl: overviewerConfig.map.controls.mapType && | |
overviewer.collections.mapTypeIds.length > 1, | |
mapTypeControlOptions: { | |
mapTypeIds: overviewer.collections.mapTypeIds | |
}, | |
mapTypeId: overviewer.util.getDefaultMapTypeId(), | |
streetViewControl: false, | |
overviewMapControl: true, | |
zoomControl: overviewerConfig.map.controls.zoom, | |
backgroundColor: overviewer.util.getMapTypeBackgroundColor( | |
overviewer.util.getDefaultMapTypeId()) | |
}; | |
overviewer.map = new google.maps.Map(document.getElementById( | |
overviewerConfig.CONST.mapDivId), mapOptions); | |
if (overviewerConfig.map.debug) { | |
overviewer.map.overlayMapTypes.insertAt(0, | |
new overviewer.classes.CoordMapType(new google.maps.Size( | |
overviewerConfig.CONST.tileSize, | |
overviewerConfig.CONST.tileSize))); | |
google.maps.event.addListener(overviewer.map, 'click', function(event) { | |
overviewer.util.debug('latLng: (' + event.latLng.lat() + | |
', ' + event.latLng.lng() + ')'); | |
var pnt = overviewer.map.getProjection().fromLatLngToPoint(event.latLng); | |
overviewer.util.debug('point: ' + pnt); | |
var pxx = pnt.x * overviewerConfig.CONST.tileSize * | |
Math.pow(2, overviewerConfig.map.maxZoom); | |
var pxy = pnt.y * overviewerConfig.CONST.tileSize * | |
Math.pow(2, overviewerConfig.map.maxZoom); | |
overviewer.util.debug('pixel: (' + pxx + ', ' + pxy + ')'); | |
}); | |
} | |
// Now attach the coordinate map type to the map's registry | |
for (i in overviewer.collections.mapTypes) { | |
overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId + | |
overviewer.collections.mapTypes[i].name, | |
overviewer.collections.mapTypes[i]); | |
} | |
// Jump to the hash if given | |
overviewer.util.initHash(); | |
// Add live hash update listeners | |
// Note: It is important to add them after jumping to the hash | |
google.maps.event.addListener(overviewer.map, 'dragend', function() { | |
overviewer.util.updateHash(); | |
}); | |
google.maps.event.addListener(overviewer.map, 'zoom_changed', function() { | |
overviewer.util.updateHash(); | |
}); | |
// Make the link again whenever the map changes | |
google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function() { | |
$('#'+overviewerConfig.CONST.mapDivId).css( | |
'background-color', overviewer.util.getMapTypeBackgroundColor( | |
overviewer.map.getMapTypeId())); | |
//smuggled this one in here for maptypeid hash generation --CounterPillow | |
overviewer.util.updateHash(); | |
}); | |
}, | |
/** | |
* Read through overviewer.collections.markerDatas and create Marker | |
* objects and stick them in overviewer.collections.markers . This | |
* should probably be done differently at some point so that we can | |
* support markers that change position more easily. | |
*/ | |
'initializeMarkers': function() { | |
//first, give all collections an empty array to work with | |
for (i in overviewerConfig.objectGroups.signs) { | |
overviewer.util.debug('Found sign group: ' + | |
overviewerConfig.objectGroups.signs[i].label); | |
overviewer.collections.markers[ | |
overviewerConfig.objectGroups.signs[i].label] = []; | |
} | |
for (i in overviewer.collections.markerDatas) { | |
var markerData = overviewer.collections.markerDatas[i]; | |
for (j in markerData) { | |
var item = markerData[j]; | |
// a default: | |
var iconURL = ''; | |
if (item.type == 'spawn') { | |
// don't filter spawn, always display | |
var marker = new google.maps.Marker({ | |
'position': overviewer.util.fromWorldToLatLng(item.x, | |
item.y, item.z), | |
'map': overviewer.map, | |
'title': jQuery.trim(item.msg), | |
'icon': overviewerConfig.CONST.image.spawnMarker | |
}); | |
continue; | |
} | |
if (item.type == 'querypos') { | |
// Set on page load if MC x/y/z coords are given in the | |
// query string | |
var marker = new google.maps.Marker({ | |
'position': overviewer.util.fromWorldToLatLng(item.x, | |
item.y, item.z), | |
'map': overviewer.map, | |
'title': jQuery.trim(item.msg), | |
'icon': overviewerConfig.CONST.image.queryMarker | |
}); | |
continue; | |
} | |
var matched = false; | |
for (j in overviewerConfig.objectGroups.signs) { | |
var signGroup = overviewerConfig.objectGroups.signs[j]; | |
var label = signGroup.label; | |
if (signGroup.match(item)) { | |
matched = true; | |
// can add custom types of images for externally defined | |
// item types, like 'command' here. | |
if (item.type == 'sign') { | |
iconURL = overviewerConfig.CONST.image.signMarker; | |
} | |
overviewer.util.debug('Sign icon: ' + signGroup.icon); | |
if (signGroup.icon) { | |
iconURL = signGroup.icon; | |
} | |
var marker = new google.maps.Marker({ | |
'position': overviewer.util.fromWorldToLatLng(item.x, | |
item.y, item.z), | |
'map': overviewer.map, | |
'title': jQuery.trim(item.msg), | |
'icon': iconURL, | |
'visible': false | |
}); | |
item.marker = marker; | |
overviewer.util.debug(label); | |
overviewer.collections.markers[label].push(marker); | |
if (item.type == 'sign') { | |
overviewer.util.createMarkerInfoWindow(marker); | |
} | |
} | |
} | |
if (!matched) { | |
// is this signpost doesn't match any of the groups in | |
// config.js, add it automatically to the "__others__" group | |
if (item.type == 'sign') { | |
iconURL = overviewerConfig.CONST.image.signMarker; | |
} | |
var marker = new google.maps.Marker({ | |
'position': overviewer.util.fromWorldToLatLng(item.x, | |
item.y, item.z), | |
'map': overviewer.map, | |
'title': jQuery.trim(item.msg), | |
'icon': iconURL, | |
'visible': false | |
}); | |
item.marker = marker; | |
if (overviewer.collections.markers['__others__']) { | |
overviewer.collections.markers['__others__'].push(marker); | |
} else { | |
overviewer.collections.markers['__others__'] = [marker]; | |
} | |
if (item.type == 'sign') { | |
overviewer.util.createMarkerInfoWindow(marker, item); | |
} | |
} | |
} | |
} | |
}, | |
/** | |
* Same as initializeMarkers() for the most part. | |
*/ | |
'initializeRegions': function() { | |
for (i in overviewerConfig.objectGroups.regions) { | |
overviewer.collections.regions[overviewerConfig.objectGroups.regions[i].label] = []; | |
} | |
for (i in overviewer.collections.regionDatas) { | |
var regionData = overviewer.collections.regionDatas[i]; | |
for (j in regionData) { | |
var region = regionData[j]; | |
// pull all the points out of the regions file. | |
var converted = new google.maps.MVCArray(); | |
for (k in region.path) { | |
var point = region.path[k]; | |
converted.push(overviewer.util.fromWorldToLatLng( | |
point.x, point.y, point.z)); | |
} | |
if (region.label) { | |
var name = region.label; | |
} else { | |
var name = "rawr"; | |
} | |
if(region.opacity) { | |
var strokeOpacity = region.opacity; | |
var fillOpacity = region.opacity * 0.25; | |
} else { | |
var strokeOpacity = region.strokeOpacity; | |
var fillOpacity = region.fillOpacity; | |
} | |
var shapeOptions = { | |
'name': name, | |
'geodesic': false, | |
'map': null, | |
'strokeColor': region.color, | |
'strokeOpacity': strokeOpacity, | |
'strokeWeight': overviewerConfig.CONST.regionStrokeWeight, | |
'zIndex': j | |
}; | |
if (region.closed) { | |
shapeOptions["fillColor"] = region.color; | |
shapeOptions["fillOpacity"] = fillOpacity; | |
shapeOptions["paths"] = converted; | |
} else { | |
shapeOptions["path"] = converted; | |
} | |
var matched = false; | |
for (k in overviewerConfig.objectGroups.regions) { | |
var regionGroup = overviewerConfig.objectGroups.regions[k]; | |
var clickable = regionGroup.clickable; | |
var label = regionGroup.label; | |
if (!regionGroup.match(region)) | |
continue; | |
matched = true; | |
if (!region.label) { | |
clickable = false; // if it doesn't have a name, we dont have to show it. | |
} | |
if (region.closed) { | |
var shape = new google.maps.Polygon(shapeOptions); | |
} else { | |
var shape = new google.maps.Polyline(shapeOptions); | |
} | |
overviewer.collections.regions[label].push(shape); | |
if (clickable) { | |
overviewer.util.createRegionInfoWindow(shape); | |
} | |
} | |
// if we haven't matched anything, go ahead and add it | |
if (!matched) { | |
if (region.closed) { | |
var shape = new google.maps.Polygon(shapeOptions); | |
} else { | |
var shape = new google.maps.Polyline(shapeOptions); | |
} | |
shape.setMap(overviewer.map); | |
} | |
} | |
} | |
}, | |
/** | |
* Change the map's div's background color according to the mapType's bg_color setting | |
* | |
* @param string mapTypeId | |
* @return string | |
*/ | |
'getMapTypeBackgroundColor': function(mapTypeId) { | |
for(i in overviewerConfig.mapTypes) { | |
if( overviewerConfig.CONST.mapDivId + | |
overviewerConfig.mapTypes[i].label == mapTypeId ) { | |
overviewer.util.debug('Found background color for: ' + | |
overviewerConfig.mapTypes[i].bg_color); | |
return overviewerConfig.mapTypes[i].bg_color; | |
} | |
} | |
}, | |
/** | |
* Gee, I wonder what this does. | |
* | |
* @param string msg | |
*/ | |
'debug': function(msg) { | |
if (overviewerConfig.map.debug) { | |
console.log(msg); | |
} | |
}, | |
/** | |
* Simple helper function to split the query string into key/value | |
* pairs. Doesn't do any type conversion but both are lowercase'd. | |
* | |
* @return Object | |
*/ | |
'parseQueryString': function() { | |
var results = {}; | |
var queryString = location.search.substring(1); | |
var pairs = queryString.split('&'); | |
for (i in pairs) { | |
var pos = pairs[i].indexOf('='); | |
var key = pairs[i].substring(0,pos).toLowerCase(); | |
var value = pairs[i].substring(pos+1).toLowerCase(); | |
overviewer.util.debug( 'Found GET paramter: ' + key + ' = ' + value); | |
results[key] = value; | |
} | |
return results; | |
}, | |
'getDefaultMapTypeId': function() { | |
return overviewer.collections.mapTypeIds[0]; | |
}, | |
/** | |
* helper to get map LatLng from world coordinates takes arguments in | |
* X, Y, Z order (arguments are *out of order*, because within the | |
* function we use the axes like the rest of Minecraft Overviewer -- | |
* with the Z and Y flipped from normal minecraft usage.) | |
* | |
* @param int x | |
* @param int z | |
* @param int y | |
* | |
* @return google.maps.LatLng | |
*/ | |
'fromWorldToLatLng': function(x, z, y) { | |
// the width and height of all the highest-zoom tiles combined, | |
// inverted | |
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * | |
Math.pow(2, overviewerConfig.map.maxZoom)); | |
// This information about where the center column is may change with | |
// a different drawing implementation -- check it again after any | |
// drawing overhauls! | |
// point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2) | |
// so the Y coordinate is at 0.5, and the X is at 0.5 - | |
// ((tileSize / 2) / (tileSize * 2^maxZoom)) | |
// or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) | |
var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); | |
var lat = 0.5; | |
// the following metrics mimic those in ChunkRenderer.chunk_render | |
// in "chunk.py" or, equivalently, chunk_render in src/iterate.c | |
// each block on X axis adds 12px to x and subtracts 6px from y | |
lng += 12 * x * perPixel; | |
lat -= 6 * x * perPixel; | |
// each block on Y axis adds 12px to x and adds 6px to y | |
lng += 12 * y * perPixel; | |
lat += 6 * y * perPixel; | |
// each block down along Z adds 12px to y | |
lat += 12 * (128 - z) * perPixel; | |
// add on 12 px to the X coordinate to center our point | |
lng += 12 * perPixel; | |
return new google.maps.LatLng(lat, lng); | |
}, | |
/** | |
* The opposite of fromWorldToLatLng | |
* NOTE: X, Y and Z in this function are Minecraft world definitions | |
* (that is, X is horizontal, Y is altitude and Z is vertical). | |
* | |
* @param float lat | |
* @param float lng | |
* | |
* @return Array | |
*/ | |
'fromLatLngToWorld': function(lat, lng) { | |
// Initialize world x/y/z object to be returned | |
var point = Array(); | |
point.x = 0; | |
point.y = 64; | |
point.z = 0; | |
// the width and height of all the highest-zoom tiles combined, | |
// inverted | |
var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * | |
Math.pow(2, overviewerConfig.map.maxZoom)); | |
// Revert base positioning | |
// See equivalent code in fromWorldToLatLng() | |
lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); | |
lat -= 0.5; | |
// I'll admit, I plugged this into Wolfram Alpha: | |
// a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) | |
// And I don't know the math behind solving for for X and Z given | |
// A (lng) and B (lat). But Wolfram Alpha did. :) I'd welcome | |
// suggestions for splitting this up into long form and documenting | |
// it. -RF | |
point.x = (lng - 2 * lat) / (24 * perPixel) | |
point.z = (lng + 2 * lat) / (24 * perPixel) | |
// Adjust for the fact that we we can't figure out what Y is given | |
// only latitude and longitude, so assume Y=64. | |
point.x += 64; | |
point.z -= 64; | |
return point; | |
}, | |
/** | |
* Create and draw the various map controls and other related things | |
* like the compass, current view link, etc. | |
*/ | |
'createMapControls': function() { | |
// compass rose, in the top right corner | |
var compassDiv = document.createElement('DIV'); | |
compassDiv.style.padding = '5px'; | |
var compassImg = document.createElement('IMG'); | |
compassImg.src = overviewerConfig.CONST.image.compass; | |
compassDiv.appendChild(compassImg); | |
compassDiv.index = 0; | |
// add it to the map, top right. | |
if (overviewerConfig.map.controls.compass) { | |
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(compassDiv); | |
} | |
// Spawn button | |
var homeControlDiv = document.createElement('DIV'); | |
var homeControl = new overviewer.classes.HomeControl(homeControlDiv); | |
$(homeControlDiv).addClass('customControl'); | |
homeControlDiv.index = 1; | |
if (overviewerConfig.map.controls.spawn) { | |
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv); | |
} | |
// Coords box | |
var coordsDiv = document.createElement('DIV'); | |
coordsDiv.id = 'coordsDiv'; | |
coordsDiv.innerHTML = ''; | |
if (overviewerConfig.map.controls.coordsBox) { | |
overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(coordsDiv); | |
} | |
// Update coords on mousemove | |
google.maps.event.addListener(overviewer.map, 'mousemove', function (event) { | |
var worldcoords = overviewer.util.fromLatLngToWorld(event.latLng.lat(), event.latLng.lng()); | |
coordsDiv.innerHTML = "Coords: X " + Math.round(worldcoords.x) + ", Z " + Math.round(worldcoords.z); | |
}); | |
// only need to create the control if there are items in the list. | |
// as defined in config.js | |
if (overviewerConfig.objectGroups.signs.length > 0) { | |
// signpost display control | |
var items = []; | |
for (i in overviewerConfig.objectGroups.signs) { | |
var signGroup = overviewerConfig.objectGroups.signs[i]; | |
// don't create an option for this group if empty | |
if (overviewer.collections.markers[signGroup.label].length == 0) { | |
continue; | |
} | |
var iconURL = signGroup.icon; | |
if(!iconURL) { | |
iconURL = overviewerConfig.CONST.image.defaultMarker; | |
} | |
items.push({ | |
'label': signGroup.label, | |
'checked': signGroup.checked, | |
'icon': iconURL, | |
'action': function(n, item, checked) { | |
jQuery.each(overviewer.collections.markers[item.label], | |
function(i, elem) { | |
elem.setVisible(checked); | |
} | |
); | |
overviewer.util.debug('Adding sign item: ' + item); | |
} | |
}); | |
} | |
// only create drop down if there's used options | |
if (items.length > 0) { | |
overviewer.util.createDropDown('Signposts', items); | |
} | |
} | |
// if there are any regions data, lets show the option to hide/show them. | |
if (overviewerConfig.objectGroups.regions.length > 0) { | |
// region display control | |
var items = []; | |
for (i in overviewerConfig.objectGroups.regions) { | |
var regionGroup = overviewerConfig.objectGroups.regions[i]; | |
items.push({ | |
'label': regionGroup.label, | |
'checked': regionGroup.checked, | |
'action': function(n, item, checked) { | |
jQuery.each(overviewer.collections.regions[item.label], | |
function(i,elem) { | |
// Thanks to LeastWeasel for this line! | |
elem.setMap(checked ? overviewer.map : null); | |
}); | |
overviewer.util.debug('Adding region item: ' + item); | |
} | |
}); | |
} | |
overviewer.util.createDropDown('Regions', items); | |
} | |
if (overviewerConfig.map.controls.overlays && overviewer.collections.overlays.length > 0) { | |
// overlay maps control | |
var items = []; | |
for (i in overviewer.collections.overlays) { | |
var overlay = overviewer.collections.overlays[i]; | |
items.push({ | |
'label': overlay.name, | |
'checked': false, | |
'overlay': overlay, | |
'action': function(i, item, checked) { | |
if (checked) { | |
overviewer.map.overlayMapTypes.push(item.overlay); | |
} else { | |
var idx_to_delete = -1; | |
overviewer.map.overlayMapTypes.forEach(function(e, j) { | |
if (e == item.overlay) { | |
idx_to_delete = j; | |
} | |
}); | |
if (idx_to_delete >= 0) { | |
overviewer.map.overlayMapTypes.removeAt(idx_to_delete); | |
} | |
} | |
} | |
}); | |
} | |
overviewer.util.createDropDown('Overlays', items); | |
} | |
// call out to create search box, as it's pretty complicated | |
overviewer.util.createSearchBox(); | |
}, | |
/** | |
* Reusable method for creating drop-down menus | |
* | |
* @param string title | |
* @param array items | |
*/ | |
'createDropDown': function(title, items) { | |
var control = document.createElement('DIV'); | |
// let's let a style sheet do most of the styling here | |
$(control).addClass('customControl'); | |
var controlText = document.createElement('DIV'); | |
controlText.innerHTML = title; | |
var controlBorder = document.createElement('DIV'); | |
$(controlBorder).addClass('top'); | |
control.appendChild(controlBorder); | |
controlBorder.appendChild(controlText); | |
var dropdownDiv = document.createElement('DIV'); | |
$(dropdownDiv).addClass('dropDown'); | |
control.appendChild(dropdownDiv); | |
dropdownDiv.innerHTML=''; | |
// add the functionality to toggle visibility of the items | |
$(controlText).click(function() { | |
$(controlBorder).toggleClass('top-active'); | |
$(dropdownDiv).toggle(); | |
}); | |
// add that control box we've made back to the map. | |
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control); | |
for(i in items) { | |
// create the visible elements of the item | |
var item = items[i]; | |
overviewer.util.debug(item); | |
var itemDiv = document.createElement('div'); | |
var itemInput = document.createElement('input'); | |
itemInput.type='checkbox'; | |
// give it a name | |
$(itemInput).data('label',item.label); | |
jQuery(itemInput).click((function(local_idx, local_item) { | |
return function(e) { | |
item.action(local_idx, local_item, e.target.checked); | |
}; | |
})(i, item)); | |
// if its checked, its gotta do something, do that here. | |
if (item.checked) { | |
itemInput.checked = true; | |
item.action(i, item, item.checked); | |
} | |
dropdownDiv.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); | |
} | |
}, | |
/** | |
* Create search box and dropdown in the top right google maps area. | |
*/ | |
'createSearchBox': function() { | |
var searchControl = document.createElement("div"); | |
searchControl.id = "searchControl"; | |
var searchInput = document.createElement("input"); | |
searchInput.type = "text"; | |
searchControl.appendChild(searchInput); | |
var searchDropDown = document.createElement("div"); | |
searchDropDown.id = "searchDropDown"; | |
searchControl.appendChild(searchDropDown); | |
$(searchInput).keyup(function(e) { | |
var newline_stripper = new RegExp("[\\r\\n]", "g") | |
if(searchInput.value.length !== 0) { | |
$(searchDropDown).fadeIn(); | |
$(searchDropDown).empty(); | |
overviewer.collections.markerDatas.forEach(function(marker_list) { | |
marker_list.forEach(function(sign) { | |
var regex = new RegExp(overviewer.util.pregQuote(searchInput.value), "mi"); | |
if(sign.msg.match(regex)) { | |
if(sign.marker !== undefined && sign.marker.getVisible()) { | |
var t = document.createElement("div"); | |
t.className = "searchResultItem"; | |
var i = document.createElement("img"); | |
i.src = sign.marker.getIcon(); | |
t.appendChild(i); | |
var s = document.createElement("span"); | |
$(s).text(sign.msg.replace(newline_stripper, "")); | |
t.appendChild(s); | |
searchDropDown.appendChild(t); | |
$(t).click(function(e) { | |
$(searchDropDown).fadeOut(); | |
overviewer.map.setZoom(7); | |
overviewer.map.setCenter(sign.marker.getPosition()); | |
}); | |
} | |
} | |
}); | |
}); | |
} else { | |
$(searchDropDown).fadeOut(); | |
} | |
}); | |
if (overviewerConfig.map.controls.searchBox) { | |
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchControl); | |
} | |
}, | |
/** | |
* Create the pop-up infobox for when you click on a region, this can't | |
* be done in-line because of stupid Javascript scoping problems with | |
* closures or something. | |
* | |
* @param google.maps.Polygon|google.maps.Polyline shape | |
*/ | |
'createRegionInfoWindow': function(shape) { | |
var infowindow = new google.maps.InfoWindow(); | |
google.maps.event.addListener(shape, 'click', function(event, i) { | |
if (overviewer.collections.infoWindow) { | |
overviewer.collections.infoWindow.close(); | |
} | |
// Replace our Info Window's content and position | |
var point = overviewer.util.fromLatLngToWorld(event.latLng.lat(),event.latLng.lng()); | |
var contentString = '<b>Region: ' + shape.name + '</b><br />' + | |
'Clicked Location: <br />' + Math.round(point.x,1) + ', ' + point.y | |
+ ', ' + Math.round(point.z,1) | |
+ '<br />'; | |
infowindow.setContent(contentString); | |
infowindow.setPosition(event.latLng); | |
infowindow.open(overviewer.map); | |
overviewer.collections.infoWindow = infowindow; | |
}); | |
}, | |
/** | |
* Same as createRegionInfoWindow() | |
* | |
* @param google.maps.Marker marker | |
*/ | |
'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; | |
}); | |
}, | |
'initHash': function() { | |
if(window.location.hash.split("/").length > 1) { | |
overviewer.util.goToHash(); | |
// Clean up the hash. | |
overviewer.util.updateHash(); | |
// Add a marker indicating the user-supplied position | |
var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng()); | |
overviewer.collections.markerDatas.push([{ | |
'msg': 'Coordinates ' + Math.floor(coordinates.x) + ', ' + Math.floor(coordinates.y) + ', ' + Math.floor(coordinates.z), | |
'x': coordinates.x, | |
'y': coordinates.y, | |
'z': coordinates.z, | |
'type': 'querypos'}]); | |
} | |
}, | |
'setHash': function(x, y, z, zoom, maptype) { | |
window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + maptype); | |
}, | |
'updateHash': function() { | |
var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng()); | |
var zoom = overviewer.map.getZoom(); | |
var maptype = overviewer.map.getMapTypeId(); | |
if (zoom == overviewerConfig.map.maxZoom) { | |
zoom = 'max'; | |
} else if (zoom == overviewerConfig.map.minZoom) { | |
zoom = 'min'; | |
} else { | |
// default to (map-update friendly) negative zooms | |
zoom -= overviewerConfig.map.maxZoom; | |
} | |
overviewer.util.setHash(coordinates.x, coordinates.y, coordinates.z, zoom, maptype); | |
}, | |
'goToHash': function() { | |
// Note: the actual data begins at coords[1], coords[0] is empty. | |
var coords = window.location.hash.split("/"); | |
var latlngcoords = overviewer.util.fromWorldToLatLng(parseInt(coords[1]), parseInt(coords[2]), parseInt(coords[3])); | |
var zoom; | |
var maptype = ''; | |
// The if-statements try to prevent unexpected behaviour when using incomplete hashes, e.g. older links | |
if (coords.length > 4) { | |
zoom = coords[4]; | |
} | |
if (coords.length > 5) { | |
maptype = coords[5]; | |
} | |
if (zoom == 'max') { | |
zoom = overviewerConfig.map.maxZoom; | |
} else if (zoom == 'min') { | |
zoom = overviewerConfig.map.minZoom; | |
} else { | |
zoom = parseInt(zoom); | |
if (zoom < 0 && zoom + overviewerConfig.map.maxZoom >= 0) { | |
// if zoom is negative, treat it as a "zoom out from max" | |
zoom += overviewerConfig.map.maxZoom; | |
} else { | |
// fall back to default zoom | |
zoom = overviewerConfig.map.defaultZoom; | |
} | |
} | |
// If the maptype isn't set, set the default one. | |
if (maptype == '') { | |
// We can now set the map to use the 'coordinate' map type | |
overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId()); | |
} else { | |
overviewer.map.setMapTypeId(maptype); | |
} | |
overviewer.map.setCenter(latlngcoords); | |
overviewer.map.setZoom(zoom); | |
}, | |
}, | |
/** | |
* The various classes needed in this file. | |
*/ | |
'classes': { | |
/** | |
* This is the button that centers the map on spawn. Not sure why we | |
* need a separate class for this and not some of the other controls. | |
* | |
* @param documentElement controlDiv | |
*/ | |
'HomeControl': function(controlDiv) { | |
controlDiv.style.padding = '5px'; | |
// Set CSS for the control border | |
var control = document.createElement('DIV'); | |
$(control).addClass('top'); | |
control.title = 'Click to center the map on the Spawn'; | |
controlDiv.appendChild(control); | |
// Set CSS for the control interior | |
var controlText = document.createElement('DIV'); | |
controlText.innerHTML = 'Spawn'; | |
$(controlText).addClass('button'); | |
control.appendChild(controlText); | |
// Setup the click event listeners: simply set the map to map center | |
// as definned below | |
google.maps.event.addDomListener(control, 'click', function() { | |
overviewer.map.panTo(overviewer.util.fromWorldToLatLng( | |
overviewerConfig.map.center[0], | |
overviewerConfig.map.center[1], | |
overviewerConfig.map.center[2])); | |
overviewer.util.updateHash(); | |
}); | |
}, | |
/** | |
* Our custom projection maps Latitude to Y, and Longitude to X as | |
* normal, but it maps the range [0.0, 1.0] to [0, tileSize] in both | |
* directions so it is easier to position markers, etc. based on their | |
* position (find their position in the lowest-zoom image, and divide | |
* by tileSize) | |
*/ | |
'MapProjection' : function() { | |
this.inverseTileSize = 1.0 / overviewerConfig.CONST.tileSize; | |
}, | |
/** | |
* This is a mapType used only for debugging, to draw a grid on the screen | |
* showing the tile co-ordinates and tile path. Currently the tile path | |
* part does not work. | |
* | |
* @param google.maps.Size tileSize | |
*/ | |
'CoordMapType': function(tileSize) { | |
this.tileSize = tileSize; | |
} | |
}, | |
/** | |
* Stuff that we give to the google maps code instead of using ourselves | |
* goes in here. | |
* | |
* Also, why do I keep writing these comments as if I'm multiple people? I | |
* should probably stop that. | |
*/ | |
'gmap': { | |
/** | |
* Generate a function to get the path to a tile at a particular location | |
* and zoom level. | |
* | |
* @param string path | |
* @param string pathBase | |
* @param string pathExt | |
*/ | |
'getTileUrlGenerator': function(path, pathBase, pathExt) { | |
return function(tile, zoom) { | |
var url = path; | |
var urlBase = ( pathBase ? pathBase : '' ); | |
if(tile.x < 0 || tile.x >= Math.pow(2, zoom) || | |
tile.y < 0 || tile.y >= Math.pow(2, zoom)) { | |
url += '/blank'; | |
} else if(zoom == 0) { | |
url += '/base'; | |
} else { | |
for(var z = zoom - 1; z >= 0; --z) { | |
var x = Math.floor(tile.x / Math.pow(2, z)) % 2; | |
var y = Math.floor(tile.y / Math.pow(2, z)) % 2; | |
url += '/' + (x + 2 * y); | |
} | |
} | |
url = url + '.' + pathExt; | |
if(overviewerConfig.map.cacheMinutes > 0) { | |
var d = new Date(); | |
url += '?c=' + Math.floor(d.getTime() / | |
(1000 * 60 * overviewerConfig.map.cacheMinutes)); | |
} | |
return(urlBase + url); | |
} | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment