/* Appends a slippy map into an SVG object. The map tiles are updated based on the position and size of the svg viewBox. svg_map(svg_object,{parameters}) The following parameters can be defined: Tile provider: api_key : The api key for the tile provider (required) max_zoom : Maximum zoom of the tile provider (default: 18) min_zoom : Minimum zoom of the tile provider (default: 0) tile_size: : Tile size in pixels by tile provider (default: 256) Position definitions x : Horizontal position of the upper left corner in SVG coordinate space y : Vertical position of the upper left corner in SVG coordinate space width : The width (and height) of the map rectangle in SVG coordinate space frame : The ID of any SVG object with attibutes x,y and width. Map will be positioned inside this object. The object must exist at map creation. This overrides any previously defined x,y,width attributes Tiling: zoom : The initial zoom level zoom_levels : If set, the available zoom levels will be limited to this list zoom_exclude : If set, the tiler will ignore the zoom levels in this list Timers: update_delay : Time between each check for new tiles (default: 50) zoom_delay : How long we freeze old tiles when zooming (default: 1000) Todo: Create event upon zoom-level change */ function svg_map(svg,p) { return new svg_map_obj(svg,p) } function test(arr,val){ for (i=0;i val) { answer =arr[i-1];break} } return answer } function svg_map_obj(svg,p) { var p = p ? p : {}, map_x= p.x ? parseFloat(p.x): 0, map_y= p.y ? parseFloat(p.y) : 0, map_width = p.width? p.width: 1, x0 = p.x0 ? p.x0 :0.75, // default x start location [0,1] y0 = p.y0 ? p.y0 : 0.25, // default y start location [0,1] zoom = p.zoom ? p.zoom : 6, // default zoom at 6 update_delay = p.update_freq ? p.update_freq : 50, // How fast we look for new tiles zoom_delay = p.zoom_delay ? p.zoom_delay : 1000, // How long we keep old tiles when zooming tile_size = p.tile_size ? p.tile_size : 256, zoom_levels = (p.zoom_levels ? p.zoom_levels : []).sort(function(a,b) { return a-b;}), zoom_exclude = p.zoom_exclude ? p.zoom_exclude : [], tiles = [], keepTiles = [], tilerBusy = false, // variables holding the tile ids mapLayer = svg.append("svg:g").attr("class","mapLayer"); if (!svg.attr("viewBox")) svg.attr("viewBox",map_x+" "+map_y+" "+map_width+" "+map_width); viewBox = svg[0][0].viewBox.baseVal; svg.AAA=3 mapLayer.BBB=4 console.log("map x is "+map_x) if (p.frame) { var frame = svg.select(p.frame), map_x = frame.attr("x"), map_y= frame.attr("y"), map_width = frame.attr("width") } if (p.id) { mapLayer.attr("id",p.id) } function PixelWidthSVG() { return svg[0][0].clientWidth ? svg[0][0].clientWidth : svg[0][0].parentNode.clientWidth;} function PixelHeightSVG() { return svg[0][0].clientHeight ? svg[0][0].clientHeight : svg[0][0].parentNode.clientHeight;} function lng2map(lng) { return ((+lng+180)/360)*map_width+map_x; } function lat2map(lat) { return (((1-Math.log(Math.tan(+lat*Math.PI/180) + 1/Math.cos(+lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,0)))*map_width+map_y; } function resize() { new_zoom = Math.floor(checkZoom()); aspect_raio = viewBox.width / viewBox.height; viewBox.width = map_width * PixelWidthSVG() / (Math.pow(2,new_zoom)*256) ; viewBox.height = map_width * PixelHeightSVG() / (Math.pow(2,new_zoom)*256) ; loadTiles(); } function checkZoom() { var best_zoom = Math.floor(Math.min(Math.max(Math.log(map_width * PixelWidthSVG()/(viewBox.width*256))/(Math.log(2)),0),18)); /* if (p.zoom_levels) { for (i=0;i best_zoom) { best_zoom = zoom_levels[i-1];break} } }*/ return best_zoom } function debugZoom() { var best_zoom = Math.floor(Math.min(Math.max(Math.log(map_width * PixelWidthSVG()/(viewBox.width*256))/(Math.log(2)),0),18)); console.log("best zoom: "+best_zoom) if (p.zoom_levels) { for (i=0;i best_zoom) { best_zoom = zoom_levels[i-1];break} }i } console.log("chosen zoom: "+best_zoom) } function loadTiles() { if (!tilerBusy) { tilerBusy = true; new_zoom = Math.floor(checkZoom()); if (zoom != new_zoom) { zoom = new_zoom; if (keepTiles.length == 0) { keepTiles = tiles.slice(); setTimeout(function (){ keepTiles = []},1000) } } no_x = Math.ceil(PixelWidthSVG() / 256)+5, no_y = Math.ceil(PixelHeightSVG() / 256)+4, max_tiles = Math.pow(2,zoom); // The number of tiles for the given zoom level firstX = Math.max(Math.floor(((viewBox.x-map_x) * max_tiles)/map_width)-3,0); firstY = Math.max(Math.floor(((viewBox.y-map_y) * max_tiles)/map_width)-3,0); tiles = []; for (x=firstX; x"+zoom+" checkzoom:"+checkZoom()) console.log(tiles) } function busy(d) { tilerBusy = d;} svg.attr("preserveAspectRatio", "xMinYMin meet"); mapdebug = {map_x:map_x,lat2map:lat2map,map_width:map_width,lng2map:lng2map,checkZoom:checkZoom,viewBox:viewBox,resize:resize,loadTiles:loadTiles,lat2map:lat2map,lng2map:lng2map,printGlobals:printGlobals,debugZoom:debugZoom,busy:busy}; mapLayer[0][0].svg_map = mapdebug d3.select(window).on("resize",resize) console.log(mapLayer) return mapLayer[0][0].svg_map; }