There can be situations when the built-in controls are not enough. Imagine a web application where the user can search for a term, such as 'Everest', and the application must find its location and pan to it. In this case, you need to navigate by code and not using a control.
This recipe shows you some programmatic ways to move around the map without using the default controls. The source code can be found in ch01/ch01-moving-around
, and here's what we'll end up with:
There's a fair bit going on here, as we've introduced manual control over a range of map navigation methods. We've also hooked into map events, animations and projection conversions. It's time to take a closer look at what's going on:
The tile service for this recipe is from the Stamen source with the watercolor layer style. This is another source that OpenLayers has built-in support for and is made easy to include.
For this recipe, we are using longitude and latitude values to navigate around the map. However, the default projection for the map view is EPSG:3857
(Spherical Mercator) and longitude and latitude is in the EPSG:4326
projection. We need a way to convert these longitude and latitude coordinates.
Luckily for us, ol.proj
has many helpful methods, one of which is to convert coordinates from longitude and latitude to EPSG:3857
, which we've just used. You can also pass a target projection as the second parameter to fromLonLat
, but the default target projection is EPSG:3857
anyway, so we don't need to bother.
The DOM elements that the user interacts with have been cached into variables for efficiency. We refer to these elements in order to retrieve and update values.
A function called updateUI
has been created in order to synchronize the input fields with the current map state. This function will either be called upon page initialization or as an event handler. To account for both these scenarios, the map view will derive from either the event argument if it is available (event.currentTarget
will be the map view in this case), or we grab it ourselves (map.getView()
). Of course, we could have used map.getView
in both scenarios, but it's good to familiarize ourselves with some of the available map event properties.
Updating the zoom and rotation values are easy with simple get
methods offered from the view (getZoom
and getRotation
).
The center positions need a little more work. Remember that the map view projection is in EPSG:3857
, but we want to display the coordinates in longitude and latitude. We do the opposite of what we did before when setting up the view using the ol.proj.toLonLat
method to convert the coordinates from Spherical Mercator to EPSG:4326
. This method accepts a second parameter to identify the source projection. The default source projection is EPSG:3857
, which matches our map view projection anyway, so we can skip specifying this.
The result returns an array, which we store in centerLonLat
. We then retrieve the respective values for display in the input field and constrain the decimal points to 3
.
The ol.View
class has an on
method which enables us to subscribe to particular events from the view and specify an event handler. We attach three event listeners to view
: center
, resolution
, and rotation
. The resolution event listener is for changes in the zoom level. When any of these view properties change, our updateUI
event handler is called.
Within this recipe, we need to set a new center position from a range of different places in the code. To make this a bit easier for ourselves, we've created a setCenter
function, which takes the lon
and lat
values. It converts the provided longitude and latitude coordinates into map projection coordinates and sets the new center position.
As the longitude and latitude values will come from input elements as strings, we pass the values into the parseFloat
JavaScript method in order to ensure they're in the expected type format for OpenLayers.
We attach a global keyup
event listener to the window object rather than adding individual event listeners per input field. When this event handler is called, we determine what actions are performed by inspecting the target element ID attribute through a switch
statement.
For example, if the zoom input field value is modified, then the target ID will be js-zoom
because the HTML markup is <input type="number" id="js-zoom">
:
The first switch case is for the zoom input field. Instead of simply setting the new zoom level on the map view, we'd prefer to animate the transition between zoom levels. To do this, we add functions to be called before rendering the zoom change via the ol.Map.beforeRender
method. It expects one or more functions of type ol.PreRenderFunction
, ol.animation.zoom
method returns this particular function type, which animates the resolution transition.
The resolution
property of ol.animation.zoom
provides the starting point of the animation, which is the current resolution. The duration
property is given in milliseconds, so this will be a quick and snappy animation.
After we've attached the prerender function, we take the user input value and set the final zoom level (setZoom
) via the parseInt
JavaScript method, which ensures that the input field string is converted to the expected number type for OpenLayers.
This switch case catches the rotation input field. Similar to the previous zoom control, we want to animate the transition again. To do this, we create a prerender function with ol.animate.rotate
. We pass in the current rotation of the view and also a custom duration of 250
milliseconds. After this, we set the new rotation amount from the input field value with the setRotation
map view method. Again, we ensure the input string is converted to a float value for OpenLayers via the parseFloat
method.
These switch cases match the longitude and latitude input field changes. Along with the longitude and latitude changes, we've decided to snap to the new center position rather than animate it. We call our own setCenter
method that was discussed earlier with the longitude and latitude values to use. As the longitude and latitude values are paired, the one that wasn't changed is grabbed from the respective input field.
Finally, we attach a change
event to the city selection menu. We've decided to animate the panning from the old center position to the new one. Just like the zoom and rotation transitions, we use the pan-specific ol.animation.pan
method. We provide the source
property with the starting position and set a duration of half a second.
Once the prerender function is in place, we can set the new center position. Once again, we call our custom setCenter
function to do this for us.
The HTML for a specific option in the city selection menu contains the longitude and latitude values as a string. For example, if we want to pan to London, the value inside the option is a comma delimited string: <option value="-0.117,51.5">London (England)</option>
. We convert this string ("-0.117,51.5"
) into an array with the JavaScript split
method to provide a distinct separation of the values. However, our setCenter
function expects two parameters, not an array of values. To get around this, we use the JavaScript apply
method, which calls setCenter
with an array of arguments, producing the same result.
This completes a thorough look at how to navigate around the map without the default controls, offering a great deal of flexibility.