#!/usr/bin/env python
# coding: utf-8
# # First step
# In this tutorial we will use [Brython](http://brython.info/), an implementation of Python written in javascript and Python, to access the OpenLayers javascript library and to manage the data to be used in the maps. To integrate Brython in the IPython notebook we are using an extension for the notebook called [brythonmagic](https://github.com/kikocorreoso/brythonmagic) that provides a new magic cell, **%%brython**
, that allow us to write and execute Brython code in the notebook.
# ## Installation of the brythonmagic IPython extension
# As stated before, we will use [Brython](http://www.brython.info), and [brythonmagic](https://github.com/kikocorreoso/brythonmagic) so first of all we need to load the extension and the Brython library.
#
# So, let's load the extension:
# In[ ]:
get_ipython().system('pip install brythonmagic')
# In[1]:
get_ipython().run_line_magic('load_ext', 'brythonmagic')
# And the brython js lib:
# In[2]:
from brythonmagic import load_brython_dev
load_brython_dev()
# **\[It is highly recommended that, at least, you read the [brythonmagic docs](https://github.com/kikocorreoso/brythonmagic#usage) to understand what it does. It is also recommended to have a quick look at the [Brython docs](http://www.brython.info/doc/en/index.html)].**
# # Warning
# In order to load javascript libraries in a safety way you should try to use https instead of http when possible (read more [here](http://mail.scipy.org/pipermail/ipython-dev/2014-July/014572.html)). If you don't trust the source and/or the source cannot be loaded using https then you could download the javascript library and load it from a local location.
# # Conventions used in the following tutorial.
# In the following tutorial I will try to follow several conventions to try to make it more readable.
#
# Code in cells that are not code cells:
#
#
# * a block of code will appear as follows:
#
# ```python
# # This is a block of code
# print("Hello world!")
# ```
#
#
# * Python/Brython code commented in a line of text will appear as follows, **this is a piece of Python/Brython code inline with the text**
#
#
# * Javascript code commented in a line of text will appear as follows, **this is a piece of javascript code inline with the text**
#
#
# * Most of new code used in a code cell will be commented in a paragraph starting with **[NEW CODE]**
#
#
# * When the Python and the javascript code is not exactly the same I will try to comment how the code would be in javascript.
# # What is OpenLayers?
# **OpenLayers** is an open source, **client side** *JavaScript* library for making **interactive web maps**, viewable in nearly any modern web browser. Since it is a client side library, it requires no special server side software or settings — you can use it without even downloading anything!
#
# The website for OpenLayers is located at [http://openlayers.org/](http://openlayers.org/). To begin, we need to download a copy of OpenLayers (or, we can directly link to the library — this is what we will do in the present tutorial). You can download the compressed library as either a .tar.gz or .zip, but both contain the same files.
#
# So, before continuing let's load the OpenLayers library.
# In[3]:
from brythonmagic import load_js_lib
load_js_lib("https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.11/OpenLayers.js")
# Right now OpenLayers3 (ol3) is in active development and OpenLayers2 is in maintenance mode. The main idea of ol3 is to simplify the use of the library. From their web page:
#
# *We've begun the development effort to make the next major version of OpenLayers a reality. OpenLayers 3 is a comprehensive rewrite of the library, targeting the latest in HTML5 and CSS3 features. The library will continue to have broad support for projections, standard protocols, and editing functionality from OpenLayers 2.x. The new version of the library will focus on performance improvements, lighter builds, prettier visual components, an improved API, and more. Some of the major highlights are:*
#
#
# * *WebGL promises to bring 3D capabilities and increased performance for all mapping needs to the latest browsers. OpenLayers 3.0 will offer WebGL, while degrading nicely in less capable browsers.*
#
#
# * *Cesium: The OpenLayers community will also integrate the new Cesium library to enable full 3D spinning globe capabilities directly into the 3.0 release.*
#
#
# * *Closure Compiler: By utilizing the Closure Compiler, applications developers will be able to create smaller and faster libraries, easing the use of the extensive OpenLayers 3.0 toolkit.*
#
#
# * *A new codebase: This offers an opportunity to clean up some of the “clunky” ways of doing things in OpenLayers. The team will also create with new API designs, which will be more accessible to all.*
#
#
# * *High-quality documentation: The new release will also feature documentation with fresh examples and default designs in OpenLayers 3.0. Making a toolkit standout is about more than the actual code.*
#
#
# In this tutorial we will be using OpenLayers2.
# # Main pieces of our mapping application
# As this is just an introductory tutorial we will be working mainly with the following classes:
#
# * [`OpenLayers.Map`](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html) : an instance of this class will be the main piece of the mapping app. Their [methods](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.Functions) will be called when we want to zoom to areas, keep track of the layers, play with events,...
#
#
# * [`OpenLayers.Layer`](http://dev.openlayers.org/apidocs/files/OpenLayers/Layer-js.html) : The info/data/... of our map will be an instance of `OpenLayers.Layer` or one of its subclasses. Each map will need, at least, a layer, the base layer.
# ### First example: A simple map with a base layer
# First we create some simple HTML code. This HTML code will contain our map. We will not use complicated HTML code during the tutorial to keep it simple and to be focused in *'How to create interactive maps in the browser with Python'*. There is a lot of amazing [resources](http://www.w3schools.com/) to learn about HTML and CSS.
# In[4]:
html="""
**OpenLayers = window.OpenLayers**
. We will use the **new**
method injected by Brython to the Javascript object that would behave similarly as if we were using Javascript constructors, (ie functions used with the Javascript keyword **new**
).
#
# The code is as follows:
# In[5]:
get_ipython().run_cell_magic('brython', '-h html -p', "from browser import window\n\nOpenLayers = window.OpenLayers\n\nmymap = OpenLayers.Map.new('map_ex1')\n\nlayer1 = OpenLayers.Layer.OSM.new()\n\nmymap.addLayer(layer1)\n\nmymap.zoomToMaxExtent()\n")
# Pretty simple!!
#
# Ok, let's dissect the code in the Brython cell above.
#
# **%%brython -h html -p**
:
#
# * Indicates that the code cell is written using Brython and we use some options for the Brython code cell, `-h` to use the HTML code defined in the `html` variable and `-p` to print the final HTML code generated below the generated map. In this example, the generated code should be something like the following:
#
# ```html
#
# **from browser import window**
#
# * **browser**
is a special module available only in Brython that allows to interact with the browser, the document, the window,...
#
# **OpenLayers = window.OpenLayers**
#
# * This way we are making the **OpenLayers**
object/namespace (JS lib) accesible to the Brython namespace.
#
# **mymap = OpenLayers.Map.new('map_ex1')**
#
# * Here we are instantiating the map. It is similar to this javascript code: **var mymap = new OpenLayers.Map('map_ex1');**
#
# **layer1 = OpenLayers.Layer.OSM.new()**
#
# * Here we create a simple [raster layer using OSM (OpenStreetMaps) tiles](http://dev.openlayers.org/docs/files/OpenLayers/Layer/OSM-js.html). It is similar to this javascript code: **var layer1 = new OpenLayers.Layer.OSM();**
#
# **mymap.addLayer(layer1)**
#
# * We add the created layer to the created map.
# * In other cells we will use **mymap.addLayers([layer1, layer2,...])**
. We can add several layers once using the method `addLayers` or we can add several layers using several times the method `addLayer`. The following:
#
# ````
# map.addLayer(layer1)
# map.addLayer(layer2)
# ````
# would be equivalent to:
# ````
# map.addLayers([layer1, layer2])
# ````
#
# **mymap.zoomToMaxExtent()**
#
# * And, finally, we set the zoom to the map. In this case we set the zoom to be the maximum extent.
# # The map, the fundamental piece!!
# As commented before, an instance of [`OpenLayers.Map`](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.OpenLayers.Map) will be one of the main parts of our mapping application. It accepts the following inputs:
#
#
# * the name of the DOM element where the map will be rendered, as we saw in our first example, and
#
#
# * a Python dictionary (a javascript object) with the options to be considered (projection to be used, units, projection to display data,...)
# ## Main map options
# * [projection](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.projection): indicates the projection to use to render the map. It uses [codes](http://spatialreference.org/) from the European Petroleum Survey Group (EPSG). Map projections are beyond the scope of this tutorial but you can find excellent information on the internet. You have to take into account that the WMS layers will be requested using the defined projection for the map if you don't define a map projection for the layer. Owing to this you must be sure the WMS server accepts the projection defined for the map. During the present tutorial we are using the projection [EPSG:4326](http://spatialreference.org/ref/epsg/wgs-84/). Other popular projection for interactive maps is [EPSG:3857](http://wiki.openstreetmap.org/wiki/EPSG:3857) that is the one used by Google and [OpenStreetMap](www.openstreetmap.org). The [EPSG:3857](http://wiki.openstreetmap.org/wiki/EPSG:3857) is also known as 900913 (900913 is GOOGLE written using numbers...).
#
#
# * [units](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.units): a string indicating if the units are in decimal degrees, meters, inches,... It is only mandatory if both map and layers do not define a projection.
#
#
# * [maxExtent and minExtent](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.maxExtent): If provided as an array, the array should consist of four values (left, bottom, right, top). The maximum and the minimum extent for the map, respectively.
#
#
# * [numZoomLevels](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.numZoomLevels): The default value is set to 16 and normally is a good value.
#
#
# * ...
#
# You can set and/or change the options value at any moment using the **OpenLayers.Map**
[methods](http://dev.openlayers.org/apidocs/files/OpenLayers/Map-js.html#OpenLayers.Map.Functions). Some of these *options will not work with Brython* (I have to analyse why) and it is better to use methods to modify a default parameter.
# ## Let's see a new example using some options in the map (using methods)
# First we define the HTML code where the map will be rendered (div element with `id="map_ex2"`) and another div (div element with `id="data"`) where we will print some data of the actual map. The information from the map is included in the function **get_data**
.
#
# **[NEW CODE]** In the example below there is some Brython specific code like the operator `<=`. This operator is a little bit controversial. You can read the last question in the [FAQ of the Brython docs to learn more](http://brython.info/doc/en/static_index.html?page=faq). If you don't like this operator or you think it is confusing you can use an alternative syntax. You can change:
#
# `element <= element_child`
#
# with
#
# `element.appendChild(element_child)`
# In[6]:
html="""**OpenLayers.LonLat.new(0, 40)**
(**new OpenLayers.LonLat(0, 40)**
in javascript). It is an [object that represents a pair of longitude and latitude coordinates](http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/BaseTypes/LonLat-js.html).
# # What is a layer?
# A layer is basically a way to show multiple levels of information and each level is independent of each other. As we saw in the previous example, when we want to actually create a layer, we create an object from an OpenLayers Layer class.
#
# OpenLayers has many different Layer classes, each allowing you to connect to a different type of map server 'back end.' For example, if you want to connect to a WMS map server, you would use the `Layer.WMS` class, and if you want to use Google Maps you'd use the `Layer.Google` class. Each layer object is independent of other layer objects, so doing things to one layer won't necessarily affect the other.
#
# Whatever the purpose of your web map application is, you will need at least one layer to have a usable map, at least one Base layer. All other layers that 'sit above' the base layer are called Overlay layers. These are the two 'types' of layers in OpenLayers.
# ## Base layers
# A **base layer** is at the very bottom of the layer list, and all other layers are on top of it. The base layer is always visible and determines some map properties such as projection and zoom levels.
#
# The order of the other layers can change, but the base layer is always below the overlay layers. By default, the first layer that you add to your map acts as the base layer. You can, however, change the property of any layer on your map to act as the base layer (by setting the `isBaseLayer` property to `True`). As said before, a map can have more than one base layer but only one of them can be active at a time. When one base layer is turned on, all the other base layers are turned off. In addition, if you add more than one flagged base layer to the map, the first base layer added will be used as the active base layer of the map.
# ### An example including several potential base layers
# First, we define the container where the map will be included:
# In[8]:
html=""""""
# And now, we define two layers and both of them will be treated as base layers but the first one defined would be the base layer as we do not define explicitly which one should be used. The default layer will be a basic map while the other base layer will be the blue marble imagery. We will add a control to choose the base layer to be shown (later we will talk more about predefined controls in OpenLayers).
# In[9]:
get_ipython().run_cell_magic('brython', '-h html', 'from browser import window\n\nOpenLayers = window.OpenLayers\n\nmymap = OpenLayers.Map.new(\'map_ex3\')\n\nlayer1 = OpenLayers.Layer.WMS.new("Base layer (default)",\n "http://vmap0.tiles.osgeo.org/wms/vmap0",\n {"layers": "basic"},\n {"wrapDateLine": True})\n\nlayer2 = OpenLayers.Layer.WMS.new("Other base layer, Blue Marble",\n "http://maps.opengeo.org/geowebcache/service/wms",\n {"layers":"bluemarble"},\n {"wrapDateLine": True})\n\nmymap.addLayers([layer1, layer2])\n\n# We will define this later.\ncontrol = OpenLayers.Control.LayerSwitcher.new()\nmymap.addControl(control)\n\nlocation = OpenLayers.LonLat.new(20, 50)\nmymap.setCenter(location, 3)\n')
# **[NEW CODE]** In the definition of the layers in the Brython code cell above we included the option **wrapDateLine**
and was set to **True**
. There might be situations where you do not want your map ends at -180 or +180 longitude degrees as you are working in that area and need a continuous map. This option helps you to achieve this.
# ## Arguments of the [`OpenLayers.Layer.WMS`](http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/Layer/WMS-js.html) layer used in the example above.
# In the previous example we have seen that we used several arguments in the definition of **layer1**
or **layer2**
. See, for instance, **layer1**
definition below:
# ```python
# layer1 = OpenLayers.Layer.WMS.new("Base layer (default)",
# "http://vmap0.tiles.osgeo.org/wms/vmap0",
# {"layers": "basic"},
# {"wrapDateLine": True})
# ```
#
# The arguments used are as follows:
#
#
# * `Name`, the first parameter, **"Base layer (default)"**
, defines the name of the layer.
#
#
# * `Url`, the second parameter, **"http://vmap0.tiles.osgeo.org/wms/vmap0"**
in this case, is the url of the map server.
#
#
# * `Params`, the third parameter, **{"layers": "basic"}**
, is a Python dictionary (javascript anonymous object). This parameter specifies server side settings that affect the map image, which the WMS server returns. The key:value pairs you pass in here will be appended (more or less) to the URL that OpenLayers generates when it makes requests to the map server. An example of one of the urls generated to get a tile in this example is the following:
#
# http://vmap0.tiles.osgeo.org/wms/vmap0?LAYERS=basic&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&FORMAT=image%2Fjpeg&SRS=EPSG%3A4326&BBOX=0,45,22.5,67.5&WIDTH=256&HEIGHT=256.
#
# The params parameter depends on the map server and it is out of the scope of this tutorial to explain all the possibilities.
#
#
# * `Options`, the fourth parameter, **{"wrapDateLine": True}**
in this case, is a Python dictionary (javascript anonymous object). The options dictionary/object contains [properties for the client side OpenLayers Layer object](http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/Layer-js.html#OpenLayers.Layer.Properties). These are the settings for the layer object itself, so all Layer classes have this parameter.
# ## Overlay layers
# **Overlay layers** (non base layers), however, do not behave the way the base layers work, turning on or off overlay layers will not affect other overlay layers. Base layers are similar to radio buttons, only one can be active at a time. Overlay layers are similar to check boxes, you can have as many on or off as you'd like. Any layer that is not a base layer is called an overlay layer. Like we talked about, the order that you add layers to your map is important. Every time you add a layer to the map, it is placed above the previous one, take this into account!!!!
# ## An example with a Base layer and an Overlay layer
# As usual, first the HTML code where the map will be rendered.
# In[10]:
html = """"""
# And now the script code. In this case we will use the **OpenLayers.Layer.Image**
of the **OpenLayers.Layer**
class. More about this subclass [here](http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/Layer/Image-js.html).
# In[12]:
get_ipython().run_cell_magic('brython', '-h html', 'from browser import window\n\nOpenLayers = window.OpenLayers\n\nmymap = OpenLayers.Map.new(\'map_ex4\')\n\nlayer1 = OpenLayers.Layer.Image.new(\'Python\',\n \'https://farm6.staticflickr.com/5257/5513104816_d73f41eddb_o.jpg\',\n OpenLayers.Bounds.new(-180,-112.5,180,112.5),\n OpenLayers.Size.new(3264,2448),\n {"numZoomLevels": 7, \n "maxResolution":.750})\n\nlayer2 = OpenLayers.Layer.Image.new(\'My Python\',\n \'https://www.python.org/static/community_logos/python-logo-master-v3-TM.png\',\n OpenLayers.Bounds.new(-100,-45,100,45),\n OpenLayers.Size.new(601,203),\n {"numZoomLevels": 7, \n "maxResolution":.750,\n "isBaseLayer": False,\n "opacity": 0.25})\n\nmymap.addLayers([layer1, layer2])\n\n# We will define this later.\ncontrol = OpenLayers.Control.LayerSwitcher.new()\nmymap.addControl(control)\n\nlocation = OpenLayers.LonLat.new(0, 0)\nmymap.setCenter(location, 2)\n')
# In the example above we have used one image as a base layer and a new one as an overlay layer. Some arguments used are the same defined above, `name, url` and `options`.
#
# **[NEW CODE]** We have used two new parameters,
#
#
# * `extent`, i.e. the following in the script **OpenLayers.Bounds.new(-100,-45,100,45)**
(equivalent javascript code would be **new OpenLayers.Bounds(-100,-45,100,45)**
). This [parameter establish the bounding box](http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/BaseTypes/Bounds-js.html#OpenLayers.Bounds.OpenLayers.Bounds) for the image, and
#
#
# * `size`, i.e. the following in the script **OpenLayers.Size.new(601, 203)**
(equivalent javascript code would be **new OpenLayers.Size(601, 203)**
). This [parameter is the width and height](http://dev.openlayers.org/releases/OpenLayers-2.13.1/doc/apidocs/files/OpenLayers/BaseTypes/Size-js.html#OpenLayers.Size) of the image.
#
# In the options parameter we have included **"opacity": 0.25**
that tells the layer to have an opacity of 0.25 (values between 0 to make the layer transparent or 1 to be opaque)
# ## Other type of layers
# It is out of the scope of this tutorial talk about all the available subclasses of the **OpenLayers.Layer**
class but at least you should know there are other available as `Google`, `Bing`, `Yahoo`, `Mapguide`, `Vector`,...
#
# Until now we have seen raster layers (`WMS, Image, Google, Bing,`...) The `Vector` layer will be explained below as it is quite usual and helpful and it is a way to data interesting data in a geographical context.
# # Vector layers
# First of all, what is a vector in this context? A vector uses geometrical shapes based on math equations to form an
# image. As main characteristics, the quality is preserved when you zoom in and vector graphics are not constrained to a grid, so they preserve shape at all scales.
#
# The vector data must be rendered to be seen. OpenLayers supports three ways to render the vector layer: SVG (`