Created
October 18, 2016 19:23
-
-
Save gkrathwohl/3a5680c09304bb91c69c241fb55e0393 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
<html> | |
<style> | |
#map{ | |
background: gray; | |
cursor: move; | |
height: 100%; | |
width: 100%; | |
overflow: hidden; | |
position: relative; | |
} | |
#tiles{ | |
pointer-events: none; | |
} | |
.grab{ cursor:grabbing;} | |
</style> | |
<div class="grab" id="map"> | |
<div id="tiles"> | |
</div> | |
</div> | |
<script> | |
// Latitude and longitude | |
var startLat = 42.6792; | |
var startLon = -70.8412; | |
// Starting zoom | |
var zoom = 16; | |
// Pixel amount the map has been dragged | |
var xPosition = 0; | |
var yPosition = 0; | |
// Load the tiles. This is called again every time your finsh dragging the map. | |
loadTiles(); | |
// Calculate tile numbers based on latitude and longitude | |
// source: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames | |
function long2tile(lon,zoom) { return (Math.floor((lon+180)/360*Math.pow(2,zoom))); } | |
function lat2tile(lat,zoom) { return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom))); } | |
function tile2long(x,z) {return (x/Math.pow(2,z)*360-180);} | |
function tile2lat(y,z) { var n=Math.PI-2*Math.PI*y/Math.pow(2,z); return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n)))); } | |
// modified to return fractional tile value (does not floor result) | |
function lonToFractionalTile(lon,zoom) { return (lon+180)/360*Math.pow(2,zoom); } | |
function latToFractionalTile(lat,zoom) { return (1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom); } | |
function addTileElement(row, col){ | |
var tiles = document.getElementById("tiles") | |
var newTile = document.createElement("div"); | |
tiles.appendChild(newTile); | |
//var yStartTile = long2tile(startLon, zoom); | |
//var xStartTile = lat2tile(startLat, zoom); | |
var rowStartTile = lat2tile(startLat, zoom); | |
var colStartTile = long2tile(startLon, zoom); | |
var rowGlobalTile = rowStartTile + row; | |
var colGlobalTile = colStartTile + col; | |
var image = document.createElement("img"); | |
newTile.appendChild(image); | |
// Filename(url) format is /zoom/x/y.png | |
image.src = "https://api.tiles.mapbox.com/v4/mapbox.streets/" + zoom +"/" + colGlobalTile + "/" + rowGlobalTile +".png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpandmbXliNDBjZWd2M2x6bDk3c2ZtOTkifQ._QA7i5Mpkd_m30IGElHziw"; | |
newTile.innerHTML = newTile.innerHTML+ "tile " + rowGlobalTile + "," + colGlobalTile; | |
newTile.style.position = "absolute"; | |
newTile.style.left = 256 * col + "px"; | |
newTile.style.top = 256 * row +"px"; | |
newTile.style.width = "256px"; | |
newTile.style.height = "256px"; | |
} | |
function loadTiles(){ | |
console.log("load tiles"); | |
document.getElementById("tiles").innerHTML = ""; | |
// Calculate which tiles are currently visible given the bounds, relative to starting tile (0,0) | |
var map = document.getElementById("map"); | |
var leftMostTile = Math.floor((xPosition * -1) / 256); | |
var rightMostTile = Math.floor((xPosition * -1 + map.offsetWidth) / 256); | |
var topMostTile = Math.floor((yPosition * -1) / 256); | |
var bottomMostTile = Math.floor((yPosition * -1 + map.offsetHeight) / 256); | |
// Iterate through tiles and create the DOM elements | |
for(var row = topMostTile; row <= bottomMostTile; row++){ | |
for(var col = leftMostTile; col <= rightMostTile; col++){ | |
addTileElement(row, col); | |
} | |
} | |
// | |
document.getElementById("tiles").style.transform = "translate(" + xPosition + "px ," + yPosition +"px)"; | |
} | |
function getLatLng(y, x){ | |
// go from screen coords to map coords (since map has been panned) | |
y += yPosition * -1; | |
x += xPosition * -1; | |
// xy coordinates | |
//console.log("cicked: " + y + " " + x); | |
// relative tile number (relative to map origin) | |
var col = (x / 256); | |
var row = (y / 256); | |
//console.log("clicked tile # " + row + "," + col); | |
// global tile number | |
var rowStartTile = lat2tile(startLat, zoom); | |
var colStartTile = long2tile(startLon, zoom); | |
var rowGlobalTile = rowStartTile + row; | |
var colGlobalTile = colStartTile + col; | |
//console.log("clicked global tile # " + rowGlobalTile + "," + colGlobalTile); | |
// tile to latlng | |
var lon = tile2long(colGlobalTile, zoom); | |
var lat = tile2lat(rowGlobalTile, zoom); | |
//console.log("tile lat lng: " + lat + " " + lon); | |
return {lat: lat, lon: lon}; | |
} | |
var lastZoomTime; | |
function zoomMap(zoomEvent){ | |
if( lastZoomTime != null && zoomEvent.timeStamp - lastZoomTime < 100){ | |
return; | |
} | |
lastZoomTime = zoomEvent.timeStamp; | |
//console.log(zoomEvent); | |
// prevent whole page from scrolling | |
zoomEvent.preventDefault(); | |
// get lat lon of scroller | |
var scrollerX = zoomEvent.offsetX; | |
var scrollerY = zoomEvent.offsetY; | |
var latlon = getLatLng(scrollerY, scrollerX); | |
var lat = latlon.lat; | |
var lon = latlon.lon; | |
// when we go to a new zooom level (up or down), we want the scrolling position to stay constant. | |
// we know the scroller position, and the scroller latlon. | |
// after we zoom, we have to calculate where this is and change the map position | |
// | |
//console.log(position); | |
if(zoomEvent.wheelDelta > 5){ | |
zoom += 1; | |
//zoom = 15; | |
setMapPositionAfterZooming(scrollerY, scrollerX, lat, lon) | |
loadTiles(); | |
} | |
if(zoomEvent.wheelDelta < -5){ | |
zoom -= 1; | |
setMapPositionAfterZooming(scrollerY, scrollerX, lat, lon) | |
loadTiles(); | |
} | |
} | |
function zoomIn(zoomEvent){ | |
zoomEvent.preventDefault(); | |
console.log("zoom in"); | |
var scrollerX = zoomEvent.offsetX; | |
var scrollerY = zoomEvent.offsetY; | |
var latlon = getLatLng(scrollerY, scrollerX); | |
var lat = latlon.lat; | |
var lon = latlon.lon; | |
zoom += 1; | |
//zoom = 15; | |
setMapPositionAfterZooming(scrollerY, scrollerX, lat, lon); | |
loadTiles(); | |
} | |
function setMapPositionAfterZooming(cursorY, cursorX, lat, lon){ | |
console.log("setMapPositionAfterZooming"); | |
console.log("latlon: " + lat + " " + lon); | |
// find global tile number | |
var colGlobalTile = lonToFractionalTile(lon, zoom); | |
var rowGlobalTile = latToFractionalTile(lat, zoom); | |
console.log("global tile number: " + colGlobalTile + " " + rowGlobalTile); | |
// find relative tile numer | |
var rowStartTile = lat2tile(startLat, zoom); | |
var colStartTile = long2tile(startLon, zoom); | |
//console.log("start tiles: " + colStartTile + " " + rowStartTile); | |
var row = rowGlobalTile - rowStartTile; | |
var col = colGlobalTile - colStartTile; | |
console.log("relative tile numbers: " + row + " " + col); | |
// find pixel coordinates | |
var y = row * 256; | |
var x = col * 256; | |
console.log("pixel coordinates: " + y + " " + x); | |
// find map offset (to line up calculated pixel coordiantes with cursor coordinates) | |
var yMapPosition = (cursorY - y); | |
var xMapPosition = (cursorX - x); | |
console.log("map position to align coordiantes: " + yMapPosition + " " + xMapPosition); | |
// set map position | |
yPosition = yMapPosition; | |
xPosition = xMapPosition; | |
} | |
function startDrag(event) { | |
window.onmousemove = mouseMove; | |
window.onmouseup = mouseUp; | |
var startX = event.offsetX; | |
var startY = event.offsetY; | |
var latlng = getLatLng(startY, startX); | |
function mouseMove(event) { | |
var offsetX = event.offsetX - startX; | |
var offsetY = event.offsetY - startY; | |
xTempPosition = xPosition + offsetX; | |
yTempPosition = yPosition + offsetY; | |
document.getElementById("tiles").style.transform = "translate(" + xTempPosition + "px ," + yTempPosition +"px)"; | |
} | |
function mouseUp(event){ | |
window.onmousemove = null; | |
var offsetX = event.offsetX - startX; | |
var offsetY = event.offsetY - startY; | |
xPosition = xPosition + offsetX; | |
yPosition = yPosition + offsetY; | |
// we have to make sure we don't duplicate tiles, and the we delete tile no longer in view. | |
loadTiles(); | |
}; | |
} | |
document.getElementById("map").onmousedown = startDrag; | |
document.getElementById("map").addEventListener('dblclick', zoomIn) | |
document.getElementById("map").addEventListener('mousewheel', zoomMap); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment