Side by Side Map¶
This complete example displays a split view of a movable and zoomable map,
embedded inside an html element on a webpage, with each side of the
view rendered in a different style, for example, a plain map on the left
and satellite imagery on the right.
Overview¶
The map is centered on a desired initial location,
which can be defined either as a point,
using a (longitude, latitude) coordinate pair with altitude in meters,
or as a bounding box area.
In the case of specifying the region as a bounding box,
the (longitude, latitude) coordinates of both the upper left and
of the lower right corners of the bounding box rectangle have to be defined.
The map supports pan and zoom, and is fully 3D,
so holding down the shift key and panning will
simultaneously rotate and tilt the map.
Each map view uses normalized coordinates to define the area of
the viewport which it covers.
The center separator can be dragged to the right or to the left,
to view the same region on the map in either the style on the left,
without imagery in this case, or the style on the right, with imagery.
If you want to jump right in, you can download the HTML file and the JavaScript file and the map style for this example in the same directory, and then load the HTML file in a web browser to try it out right now!
Or you can continue reading for a detailed explanation of the code.
How it works¶
1// Start by setting your token from https://developer.magiclane.com/api/projects 2gem.core.App.token="your_API_key_token";The first step in JavaScript is to set your API key tokengem.core.App.token
, which you can get at the Magic Lane website, see the Getting Started tutorial.You only need to type your email address and create a new password.5var defaultView; 6var secondView; 7 8// The main function 9gem.core.App.registerInitialCallFunction(function () { 10 // Get app default view 11 dragElement(document.getElementById("viewsSlider")); 12 let defaultScreen = gem.core.App.getDefaultScreen(); 13 // View of the map 14 defaultView = defaultScreen.getDefaultMapView(); 15 secondView = defaultScreen.createView({ x: 0., y: 0, width: 1.0, height: 1.0 }); 16 readBinaryFile(secondView, "./Satellite.style"); 17 defaultView.setClippingArea({ x: 0, y: 0, width: 0.5, height: 1.0 }); 18 secondView.setClippingArea({ x: 0.5, y: 0, width: 0.5, height: 1.0 }); 19 // @see gem.Coordinates 20 var coords = { latitude: 43.6438892, longitude: 9.066603, altitude: 1000000, bearing: 0.0 }; 21 defaultView.centerOnCoordinates(coords, 0); 22}); 23 24// Initializes the app 25gem.core.App.initApp();The main function isgem.core.App.registerInitialCallFunction(function()
and this is where execution starts.gem.core.App.getDefaultScreen().getDefaultMapView()
loads the default view of the map.createView
is used to make the second view.setClippingArea
defines the size of each view, where the containing viewport has normalized coordinates, that is, 0,0 at the top left, and 1,1 at the bottom right, so 0.5,0.5 is the center of the viewport.A latitude, longitude coordinate pair is defined anddefaultView.centerOnCoordinates(coords,0)
is used to set the camera at that location.Altitude is in meters, and bearing(heading) is in degrees, where 0 is north, 90 is east, 180 is south and 270 (or -90) is west.The 0 is the duration of the camera flight to the specified coordinates, in milliseconds, so the map is instantaneously displayed at the desired location.26function readBinaryFile(pView, file) { 27 var rawFile = new XMLHttpRequest(); 28 rawFile.open("GET", file, true); 29 rawFile.responseType = "arraybuffer"; 30 rawFile.onreadystatechange = function () { 31 if (rawFile.readyState === 4) { 32 if (rawFile.status === 200 || rawFile.status == 0) { 33 var arrayBuffer = rawFile.response; 34 if (arrayBuffer) { 35 var byteArray = new Uint8Array(arrayBuffer); 36 pView.updateCurrentStyleFromStyleBuffer(byteArray); 37 } 38 } 39 } 40 } 41 rawFile.send(null); 42}readBinaryFile
loads a map style file which can be created with the online maps studio. The style is only applied to one of the views.44function dragElement(elmnt) { 45 var pos1 = 0, pos2 = 0; 46 if (document.getElementById(elmnt.id + "header")) { 47 // if present, the header is where you move the DIV from: 48 document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; 49 } else { 50 // otherwise, move the DIV from anywhere inside the DIV: 51 elmnt.onmousedown = dragMouseDown; 52 } 53 function dragMouseDown(e) { 54 e = e || window.event; 55 e.preventDefault(); 56 // get the mouse cursor position at startup: 57 pos2 = e.clientX; 58 document.onmouseup = closeDragElement; 59 // call a function whenever the cursor moves: 60 document.onmousemove = elementDrag; 61 } 62 function elementDrag(e) { 63 e = e || window.event; 64 e.preventDefault(); 65 // calculate the new cursor position: 66 pos1 = pos2 - e.clientX; 67 pos2 = e.clientX; 68 69 let parentDiv = elmnt.parentNode; 70 let boundingBox = parentDiv.getBoundingClientRect(); 71 let newPosition = elmnt.offsetLeft - pos1; 72 let normalizedPos = newPosition / boundingBox.width; 73 elmnt.style.left = normalizedPos * 100 + "%"; 74 defaultView.setClippingArea({ x: 0, y: 0, width: normalizedPos, height: 1.0 }); 75 secondView.setClippingArea({ x: normalizedPos, y: 0, width: 1.0 - normalizedPos, height: 1.0 }); 76 } 77 function closeDragElement() { 78 // stop moving when mouse button is released: 79 document.onmouseup = null; 80 document.onmousemove = null; 81 } 82}1<!DOCTYPE html> 2<html lang="en-us"> 3 <head> 4 <meta charset="utf-8" /> 5 <title>Side By Side Map Example - MagicLane Maps SDK for JavaScript</title> 6 <link rel="stylesheet" type="text/css" href="https://www.magiclane.com/sdk/js/gem.css" /> 7 <style> 8 body { 9 overflow: hidden; 10 margin: 0; 11 background-color: white; 12 } 13 .tooltiptext { 14 visibility: visible; 15 width: 240px; 16 background-color: black; 17 color: #fff; 18 text-align: center; 19 border-radius: 6px; 20 padding: 0px 0; 21 position: absolute; 22 z-index: 4 !important; 23 bottom: 150%; 24 left: 50%; 25 margin-left: -120px; 26 display: inline-block; 27 } 28 .tooltiptext::after { 29 content: ""; 30 position: absolute; 31 top: 100%; 32 left: 50%; 33 margin-left: -5px; 34 border-width: 5px; 35 border-style: solid; 36 border-color: black transparent transparent transparent; 37 } 38 </style> 39 </head> 40 <body> 41 <div id="wrapper"> 42 <div class="profile-main-loader"> 43 <div class="loader"> 44 <svg class="circular-loader" viewBox="25 25 50 50"> 45 <circle class="loader-path" cx="50" cy="50" r="20" fill="none" stroke="#70c542" stroke-width="2" /> 46 </svg> 47 </div> 48 </div> 49 </div> 50 <div id="canvasDiv" style="width: 100%; height: 100%; position: absolute; overflow: hidden"> 51 <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex="0"></canvas> 52 <div id="viewsSlider" style="width: 5px; height: 100%; left: 50%; top: 0px; cursor: col-resize; background-color: black; position: absolute; z-index: 4 !important"></div> 53 </div> 54 <script src="https://www.magiclane.com/sdk/js/gemapi.js"></script> 55 <script type="text/javascript" src="token.js"></script> 56 <script type="text/javascript" src="jsSideBySide.js"></script> 57 </body> 58</html>Thecanvas
is the drawing area where the map is rendered. The canvas is configured to fill the browser window.Note that thediv
elements must be defined before loading the JavaScript source.The circular-loader is the rotating circle animation seen while loading.At the bottom, gemapi.js, the Maps SDK for JavaScript is loaded.Next, the JavaScript source of this example is loaded.
The example is 2 plain text files, one with the
HTML code (
.html
file extension) and the other with the
JavaScript code (.js
file extension), and an optional
style file (.style
file extension) which can be created
with the online map studio.To run the example, the HTML file is loaded in a browser.
The
.js
file should be in the same directory, as it will be
loaded automatically.Source code for this example:
HTML and the JavaScript and the map styleright-click on the links and select Save As.