Skip to content

Instantly share code, notes, and snippets.

@gkrathwohl
Created October 18, 2016 19:23
Show Gist options
  • Save gkrathwohl/3a5680c09304bb91c69c241fb55e0393 to your computer and use it in GitHub Desktop.
Save gkrathwohl/3a5680c09304bb91c69c241fb55e0393 to your computer and use it in GitHub Desktop.
<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