Wednesday, April 22, 2015

OpenLayers 3 for Beginners: Part 2

Welcome to OpenLayers 3 for Beginners: Part 2.
If you have not been to and completed OpenLayers 3 Part 1 yet, head over to http://christopherjennison.com/openlayers3-quickstart-tutorial/ and complete that tutorial. If you thought to yourself, “Well, I know how to set up the map I just can’t do anything with it” then you can download the source code here: http://christopherjennison.com/tutorials/hello-map_1_final.zip.
Remember to have your local server running at the same time and navigate to http://localhost:8888/[your project name] to view it.
Let’s begin the next part.

OpenLayers 3 for Beginners: Part 2

Part 2: Layers

When we left off we had just put the map on the screen. Now we are going to actually view some data and make our map really mean something. (Maps of Africa are great and all, but there are tons of those)
We are going to cover these subjects:
  • Kinds of Data Layers
  • Adding/Removing Layers to a Map
  • Changing properties about a Layer
  • Controlling ordering of Layers
  • Introduction to Map Servers (GeoServer)
  • WMS/WFS and GeoJSON
These sections are going to lead into part 3, Interactions (Tracking, Mouse Events, Feature Information). I felt it was necessary to get a clear understanding of the data itself before we go and allow users to manipulate things. If by the end of the tutorial you want to see other things that have to do with layers, make a comment and I can make an amendment tutorial about your request. This is going to be a long tutorial, take breaks between sections and play around in your map.
Let’s begin.

1. What are Layers?

Layers are maps characterized by different attributes that are overlaid onto a basemap to show information. Layers can be, but are not limited to, Images, Vectors, Rasters, and Text. For example, if we take the map of Africa we have and place on top of it a picture of Mickey Mouse, Mickey Mouse is a Layer we are adding to the map.
Mickey Mouse OpenLayers
Mickey Mouse as a Layer on OpenLayers.
We can split Layers up into two categories:

Vectors

A layer that is made up of polygons, lines, and points. Each polygon is identified by a feature ID code. This code is used to find the attributes of a polygon. The exception to this is that GeoJSON layers do not have a feature ID (More on this layer). For example, think of the perimeter of your property, school, or apartment. That area is characterized by a polygon. This polygon has the feature ID of 192832. In the file of all polygons, the row that starts with your ID has some attributes:
fidNumRoomsOwnerNumAnimals
1928324“Christopher”3
While on a map, it may look like a square, complete with four lines and 4 points, but it links itself to the data table corresponding to the feature ID number. There are different kinds of vectors but we are going to focus on a couple of types:
  • Shape Files: (.shp) a Series of files accompanied by a DBF file of each polygon.
  • GeoJSON: (.json) a JSON file structure of a vector layer.
There are many other types, including PostGIS, H2, SQL, and a few others but they may be covered in an amendment tutorial.

Rasters

Rasters are layers that we see and use every day, believe it or not. Raster layers are precisely the same layers used in monitoring weather. If you take a look at this map of road conditions from The Weather Channelhttp://www.weather.com/travel/commute?layers=driving. The information you see here is a Raster Layer.
Rasters are made up of a series of pixels with a identification number much like the vector layers are made up of. However, rasters can only have one (1) attribute, opposed to the unlimited attributes we can have on vectors. A raster layer may have a row that looks like:
idvalue
In a raster, it is up to us to use the value column as needed.
Rasters are often satellite imagery, and is highly accurate information.

Why use Vector Layers if Rasters are so good?

Rasters hold specific, position by position data. There are instances where using a raster would not work. A map of High School Graduates per State would not work with Rasters, but Annual Temperature would. Think of this way, if a satellite can see it from space, it can be a raster.
Types of Common Rasters are:
  • ArcGrid
  • GeoTiff
  • ImageMosaic
  • GTopo30
We will not be using Rasters in this tutorial, but will be used in future lessons.
Now that we know a good deal about layers and what they are, let’s place some on our map!

2. Adding/Removing Layers

Let’s get our project ready and get some data together.
Before we can ultimately add or remove anything, we need data. Luckily, I have prepared some data about New England, Developed vs UnDeveloped land. Should be an interesting map.
First, you need to change your map instantiation. Change your center parameter to these coordinates:
center:[-8015003.33712,4160979.44405],
Good, if you refresh you should be roughly centered over my home, New England. Let’s add that layer:
1
2
3
4
5
6
7
8
9
10
11
12
var vectorLayer = new ol.layer.Tile({
    source: new ol.source.TileWMS({
        preload: Infinity,
        url: 'http://felek.cns.umass.edu:8080/geoserver/wms',
        serverType:'geoserver',
        params:{
            'LAYERS':"Streams:Developed", 'TILED':true
        }
    })
});
    
map.addLayer(vectorLayer);
In English
  • 1: We create the vector layer by instantiating a new ol.layer.Tile class.
  • 2: The one parameter we need is the ‘source‘ of the layer. The source is of the classol.source.TileWMS. The TileWMS is the large factor here because we are using a Web Map Service to pull down a Layer Object onto our map. We will discuss more about WMS later.
  • 3: We set our layer to preload itself. This is to say that the layer will stay in memory, cached, for when the user zooms in and out of the map. This can also attempt to load tile’s before the user zooms in and out, ensuring a smoother transition.
  • 4: The url of the map server we are using. This is my map server.
  • 5: serverType is the type of server, in our case we are using GeoServer.
  • 6: params is an important snippet. This contains the actual layer we want to get from GeoServer. This layer will send a http request in the format of WMS to get back a series of images from the map server to be placed on the map. In the object, ‘LAYERS‘ is the layer name you want to pull, where ‘Streams’ is the workspace of the layer, and ‘Developed’ is the actual layer name. We also check ‘TILED’ to be true. It would be unwise to load the entire layer when the user may not see all parts of it.
OpenLayers Percent Developed
An OpenLayers map of Percent Developed in New England
Now this is a map! We can see the areas that are most developed in New England and take educated guesses at why which areas are less developed than others. Take some guesses, think about it, and now you are really getting into GIS.
Let’s decide, however, that while this data layer is truly very interesting, we would really like to make room for some other layers. We should remove this layer. This is going to take a bit of surgery.
Open up your index.html file and let’s add a button just below the <div> for our map.
<button style='position:absolute; left: 10px; top:30%' onclick='removeTopLayer()'>Remove Top Layer</button>
Now you should see a very ugly HTML button on the left side of your screen. If you are truly bothered by it, go ahead and style it as you wish. At the end of the tutorial set I will improve the layout in the source code.
We created it with the ‘onclick‘ event removeTopLayer() so let’s implement that function in ourmain.js file.
Let’s think for a moment before jumping in. We need to remove the layer, so how can we get to the layer we want to remove? Wouldn’t it be cool if there was a large documentation base for layer in the map?
Well it is your lucky day, there is!
The method getLayers() sounds just like the method we need to call.
getLayers API
The getLayers() method in OpenLayers 3 API
Before we just throw this into our application and hope it does what it says it does, let’s open up the JavaScript Console in your favorite browser. (Command Option J for Mac, Ctrl Shift J for PC)
Type:
map.getLayers()
and see what you get. The object obviously isn’t pretty looking, and I cannot make out what the variable names are, but there is an array in there called a[2] and when you open it, you get a two (2) object array. This is the list of layers. The higher the index, the more forward the layer is on the map. Our target is the top one. So let’s implement that function call.
1
2
3
4
5
function removeTopLayer(){
    var layers = map.getLayers();
    layers.pop();
}
This should look pretty good, but if it is not let me explain. Our method call ‘map.getLayers()‘ returns an object and inside of that object is an array called ‘a’. First, we store the getLayers() object into the variable layers, then layers.pop() to remove the map’s top layer.
There we go, adding and removing layers like it was nobody’s business.
Source Code for this section: http://christopherjennison.com/tutorials/hello-map_2_addremove.zip
Note: There is another way to removing layers from a map. If we move the variable vectorLayer to the top of our file, making it global, we can pass in vectorLayer as an argument tomap.removeLayer(). However, in this example we removed the top layer. Keep this in mind for the end of section exercises.

3. Properties of Layers

You may have looked at our percent developed layer and said, “Wow, Chris, this really is great. But I really can’t remember where the border of Vermont and New Hampshire is.”
Oh No! I can possibly make out the border, because I live in the area, but our users cannot! It is truly a terrible tragedy. This must be fixed.
Fear not, citizens. For things like these can be fixed.
But how? I say we go take a look at the API again under Layers.
There she is, setOpacity(). She’s a beauty. On any layer, we can call setOpacity() with any number between 0 and 1 to change the opacity. We are lucky such a thing exists, some of you code diggers may have found an object in our layer class called ‘T’ or ‘F’ or even ‘E’ that had a bunch of style settings within it, we definitely wanted to avoid those.
Let’s throw in our setOpacity() just under where we add it to the map.
vectorLayer.setOpacity(.3);
Refresh your map, and now your layer should be at .3 opacity! Awesome! You can even see the borders between states, good stuff.
We have no need to change more settings for our layer for now, but let’s take a look at the other get/set methods we could call:
  • Hue: The hue of the layer (Also known as the Tint of Color)
  • Saturation: The saturation (How much color is present)
  • Visibility: Whether or not the layer is visible or not.
  • Contrast: The difference between light and dark values.
  • Brightness: The amount of white in the map.
Before we move on we should discuss Visibility. While Visibility at face value just feels like whether or not the layer is on or off feels similar to setting Opacity to 0 or 1. Visibility also impacts the load of the layers. If a layer is set to be non-Visible it will not load until set visible. Using setVisible() will help us control load times on our site.
OpenLayers 3 loads tiles in queues. If you’ve taken a Data Structures course you may know what that means. If we keep forcing OpenLayers to load tiles all at once, the map may be unusable for some time before all layers are loaded. Users hate loading times, so it would be wise to load the important layers first and less important layers second.
Remember to use Visibility wisely, as it could steal or remove your user’s attention.

4. Ordering of Layers

Remove the following line from your code:
vectorLayer.setOpacity(.3);
Let’s add a second layer on top of this one. Go ahead and insert this snippet under where we added our vector layer. We’re going to take a look at correlations between percent developed and the deposition of nitrate in the ground.
1
2
3
4
5
6
7
8
9
10
11
12
var vectorLayer_2 = new ol.layer.Tile({
    source: new ol.source.TileWMS({
        preload: Infinity,
        url: 'http://felek.cns.umass.edu:8080/geoserver/wms',
        serverType:'geoserver',
        params:{
            'LAYERS':"Streams:Deposition_of_Nitrogen", 'TILED':true
        }
    })
});
    
map.addLayer(vectorLayer_2);
Good, let’s reload and.. hey! Where did our Percent Developed data layer go?! We can’t make correlations if we can’t see it. We have to fix this. We may not be able to go forward unless we can alter the order between layers!
Recall that OpenLayers will order maps by their position in the layers array, I think we have the power to change the ordering between these layers. Let’s give it a shot.
When we add a layer, we immediately push it to the layers array, this array is no different from any other array you have probably worked with, so it should be able to support the implementation of some kind of swap() functionality.
When we call map.getLayers() we are getting back a LayerCollection object, not just some confusing object with two letter variables. Let’s refer to the API.
If you are familiar with Java or any other data collections, this is really just a Collections object with some basis collections commands. Namely, the two we are going to use.
insertAt and removeAt
With some small logic, if we want to take the top layer and switch it with the layer below it we would do it in the following way:
Get the collection of layers
Remove the top layer and store it to an object
Insert the layer at the 2nd highest index
Sounds about right. Let’s go make another button in our html:
 <button style='position:absolute; left: 10px; top:40%' onclick='swapTopLayer()'>Swap Top Layers</button>
Now we have to implement this method in our main.js:
1
2
3
4
5
function swapTopLayer(){
    var layers = map.getLayers();
    var topLayer = layers.removeAt(2);
    layers.insertAt(1, topLayer);
}
We just implement a little logic that says “Get the set of layers, store the top one in an object after removing it, then place that top layer below the now new top layer.”
There we are, we can now swap between layers! I wonder why the New York City area and New York City suburbs have so much Nitrate in their ground.
Download source up to this point: http://christopherjennison.com/tutorials/hello-map_2_swap.zip
At this point it would be wise to take a break. Get a cup of coffee or a snickers bar and come back in about 20 minutes.

Part 5. A Sneak Peek of Map Servers

There won’t be any coding in this section, so if that upsets you then please move to section 6. We are going to discuss what a Map Server is.
You may have wondered how you can pull the maps that I have provided down into your map. This is done by using a Map Server. A Map Server is a special kind of server that is made to distribute maps back to HTTP requests made by a client. The Map Server will calculate the Data Store (or file) it is given into the required format by the client. For example, when we called:
var vectorLayer = new ol.layer.Tile({
 source: new ol.source.TileWMS({
  preload: Infinity,
  url: 'http://felek.cns.umass.edu:8080/geoserver/wms',
  serverType:'geoserver',
  params:{
   'LAYERS':"Streams:Developed", 'TILED':true
  }
 })
});
 
map.addLayer(vectorLayer);
We sent an HTTP request to GeoServer. The request said, in an Italian Mob accent, “Hey GeoServer, buddy. I’m lookin’ for a poor layer called Percent Developed, you know ‘em? Alright, good, good. Now I’m going to need him delivered to me in an image, I don’t want none of those coordinates or nothing, just an image. Oh, yeah, and make sure he’s nice and tiled while you’re at it.” GeoServer sent us back an image, tiled, of our layer in which OpenLayer’s Map class knew how to parse and place on our map.
GeoServer is one of many Map Servers available to us. There is also MapServer and ArcServer. GeoServer, for me, was easiest to use and was also free.
A Map Server takes in a Data Store. The Data Store is the file that contains information about a layer. We will usually create a new Data Store for each new layer, but that isn’t always the case. With a Data Store, we will publish a Layer. Layers exist within a Workspace. In our code snippet, our workspace was the Streams workspace. Here we configure our layer as we need it, but the main things about the Layer are these:
  • Layers must have a CRS (Coordinate Reference System). This is the projection explained in Part 1. There is a Native and Declared CRS. The Native CRS was the original projection the file was developed in. The Declared CRS is the projection we want the file in. In our case, the Percent Developed Native CRS is GCS_North_American_1983 and the Declared was EPSG:WGS84 / Google Mercator (EPSG:900913).
  • Layers have Bounding Boxes. These boxes let OpenLayers and GeoServer know how many tiles will be present on the map if the layer bounding box is smaller than the bounding box of the map’s current view.
  • Each Layer has Feature Types, or Attributes. These are the variables that contain information about each polygon.
  • Layers have styling rules. We will spend a large chunk of time on this later, but Styling rules are defined by a SLD file attached to the layer that will tell the layer how to style its polygons dependent on its attributes. (Ie, High Temp is Red, Low Temp is Blue)
There will be lessons on how to configure and set up GeoServer later in the series, for now, just acknowledge that a Map Server brought you some interesting layers today.

7. WMS vs WFS and GeoJSON

Throughout this tutorial, we have used the abbreviated WMS a lot. This is a type of service that we send to our Map Server to get back information.
WMS stands for Web Map Service. A Web Map Service returns to us an image, usually in PNG format, based on the parameters we have fed it. Easy enough, we’ve already done this twice.
However, the other protocol we should learn about is WFS. A WFS request stands for Web Feature Service. While they sounds similar in english, they are very different. A Web Map Service returns to us a Map, otherwise just an image, but a Web Feature Service returns to us a set of Features.
Features are not images. Features are a series of polygons that make up a map. This is the difference between getting back a 1kb large tile and a 400mb large JSON file of coordinates and feature types, complete with all of their data.
The bonus of Features is the instant access to their properties and dynamic styling. You could, in theory, write a GIS Web Map that had features for each state for Average Number of High School Graduates. A WFS Service would allow us to highlight all polygons with greater than 50% graduation rates. If we used the same WMS Service, we would need to write X number of SLD styles for each graduation rate.
To get a real understanding of WFS, we are going to create a GeoJSON file.
GeoJSON is a type of file that contains polygons, lines, points, and other feature types that can be loaded in as a separate file to a map. It is parsed as a WFS and all attributes are available from the get go. It is common to use GeoJSON on a small scale map to outline the perimeter of your office building, or to mark bus routes. GeoJSON can be written by hand or calculated in a GIS program, such as ArcGIS. Be aware this is only a short introduction to GeoJSON and we will be covering it in an entire section later on.
__________________________________________
Let’s learn how to make a GeoJSON file. In our project file, create a new file called myGeoJson.json. You may or may not have had exposure to JSON files before, so get ready. Every comma counts.
Copy and Paste the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    'type':'FeatureCollection',
    'crs':{
        'type':'name',
        'properties':{
            'name':'EPSG:900913'
        }
    },
    'features': [
    
    ]
}
This is the basis of the GeoJSON file. Let’s understand example before we actually use it.
‘type’ is the type of GeoJSON file we are using. In our case, this is a FeatureCollection, or more simply put, a Collection of Features. In my opinion, this is a very common type. There are other types, but we will focus on this one.
‘crs’ is the Coordinate Reference System we talked about previously in our Sneak Peek of Map Servers. This is just the EPGS code for the GeoJSON file. This let’s the application know what coordinate system is being used.
‘features’ is our array of features. There are many kinds of features in the GeoJSON world. We have:
  • Polygons
  • MultiPolygons
  • LineString
  • Line
  • Point
Pretty simple, right? This is all you’ll need to know about GeoJSON for now. With GeoJSON, we are going to do many things, including interactions, selecting polygons, and parsing data within polygons.
While writing your own GeoJSON is great, as Web Developers, we are mostly not responsible for the coordinates at which a point sits. Whoever supplied your data is the real person responsible for such things, because of this, I am going to supply you with a data file that you will show on a map.Download my GeoJSON file here (Or use your own): http://christopherjennison.com/tutorials/myGeoJSON.geojson.zip
Unzip the file and place it in your project directory, right next to your index file. You may open up the file if you really want, but the data may or may not mean anything to you. I wont dive into details, but this is a basin which is a collection of catchments upstream from any given catchment on a river or stream. Think of catchments as collections of segments streams. This isn’t important for us.
We need to get this on our map. Add this code to the start of your file, just after we declare the Map variable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var styleCache = {};
var geoLayer = new ol.layer.Vector({
    source : new ol.source.GeoJSON({
        projection : 'EPSG:900913',
        url : './myGeoJson.json'
    }),
    style : function(featureresolution) {
        var text = resolution < 5000 ? feature.get('name') : '';
        if (!styleCache[text]) {
            styleCache[text] = [new ol.style.Style({
                fill : new ol.style.Fill({
                    color : 'rgba(255, 255, 255, 0.1)'
                }),
                stroke : new ol.style.Stroke({
                    color : '#319FD3',
                    width : 1
                }),
                text : new ol.style.Text({
                    font : '12px Calibri,sans-serif',
                    text : text,
                    fill : new ol.style.Fill({
                        color : '#000'
                    }),
                    stroke : new ol.style.Stroke({
                        color : '#fff',
                        width : 3
                    })
                }),
                zIndex : 999
            })];
        }
        return styleCache[text];
    }
});
Sit back, kids, this one is a doozy.
What we are seeing here is a WFS service in action. Let’s Pseudo-Code this before we dissect it.
Define a style cache so we may store many styles into one variable.
Create a new layer using the ol.source.GeoJSON class.
  Define the source, feed it our projection and the url to our file.
  Define the style, using a hashtable and hash function for our style cache and set each individual object accordingly. Return the style cache. Also set the zIndex to a higher number than any of the other laters.
Easier said than done, right? Alright folks, line by line:
  • 1: Define the style cache. This is a Hash Table to store styles. While we only use one style in this example, this is good practice.
  • 2: Create the geoLayer variable by instantiating a new ol.source.GeoJSON object.
  • 3 – 6: Define the source. Feed in our projection code and url.
  • 7: Start the style definition. The style definition is actually a function, where we take in the feature, which will be any object we feed it, and the resolution.
  • 8: Define the hash function.
  • 9: As long as the hash function we have defined does not also exist..
  • 10: Create a new style definition in it’s place.
  • 11: Define a Fill. This is the inside of a closed polygon.
  • 14: Define a Stroke. This is the perimeter of a polygon.
  • 18: Define Text. This is the parameters of any text we use in the GeoJSON file.
  • 29: Define the zIndex. This is the ordering property of the GeoJSON layer.
  • 32: Return the hash table with our style.
To clear up why we are using a hash function, this layer will save the style function we created for each time we zoom in our map or render it. If the map has to recreate a style every time, we can lose speed and create lag. We save the style in an outside hash table so we can build it once, and retrieve it quickly every time.
Phew, that code looks rough. Let’s finish it up, add the layer to our map! Add this code to the end of the init functon:
map.addLayer(geoLayer);
Load up the map in your web browser and zoom on in to Massachusetts. You should see a blue outline of some series of polygons. Scroll in a bit more and there it is! A GeoJSON layer!
GeoJSON OpenLayers Image
GeoJSON in OpenLayers 3 with our styling rules.

Congratulations!

We are done with part 2. This was a very, very long section and there is a lot of information we did not cover here. Let’s look at a quick summary of what we did:
  • Learned about Layer Formatting and Types
  • Learned how to Add and Remove a Layer
  • Learned how to order Layers
  • Learned how to change the inner properties of a Layer
  • Got a Basic Understanding of where the Layer is hosted.
  • Created and Implemented a Basic WFS Service.
This is the very tip of the surface of each of these topics. We didn’t add a Raster layer, I never showed you how to actually use a Map Server, we only began to show what a WFS service can do.
Amendments will be made to this part and be available at the bottom of the page, and subsequent parts with more in-depth information but will not be required to create the end product. Re-call that we are going to end this series with a functioning mapping application that can be the beginning of your own application. Other information can be used to extend your application however you want.

Download the source code for the whole project here: http://christopherjennison.com/tutorials/hello-map_2_final.zip 
Before moving onto the next section Interactions, you should experiment with things on your application first!
  • Change the fill or stroke of the GeoJSON style function we created.
  • Make a tool that removes a layer of index N defined by a drop-down.
  • Make your map ‘totally radical‘ and make your layers fade in and out. Use the code: map.render(); to update your map each time you change the opacity.
  • Add some more of my layers! Try ForestAnnual_Temperature_Maximum, or Herbacious_or_Agriculture
  • Our swapTopLayer() still assumes we want to swap layers 2 and 1. Change it so we are always swapping the top layers no matter how many layers we add.

Be sure to Subscribe and Comment requests, fixes, errors, and anything else you want to ask!


When you are ready, head over to Part 3: Interactions.

No comments:

Post a Comment

Please Comment Here!

How to backup and download Database using PHP

< ?php $mysqlUserName = 'databaseusername' ; $mysqlPassword = 'databasepassword' ; $mysqlHostNa...