Table of Contents for
QGIS: Becoming a GIS Power User

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition QGIS: Becoming a GIS Power User by Alexander Bruy Published by Packt Publishing, 2017
  1. Cover
  2. Table of Contents
  3. QGIS: Becoming a GIS Power User
  4. QGIS: Becoming a GIS Power User
  5. QGIS: Becoming a GIS Power User
  6. Credits
  7. Preface
  8. What you need for this learning path
  9. Who this learning path is for
  10. Reader feedback
  11. Customer support
  12. 1. Module 1
  13. 1. Getting Started with QGIS
  14. Running QGIS for the first time
  15. Introducing the QGIS user interface
  16. Finding help and reporting issues
  17. Summary
  18. 2. Viewing Spatial Data
  19. Dealing with coordinate reference systems
  20. Loading raster files
  21. Loading data from databases
  22. Loading data from OGC web services
  23. Styling raster layers
  24. Styling vector layers
  25. Loading background maps
  26. Dealing with project files
  27. Summary
  28. 3. Data Creation and Editing
  29. Working with feature selection tools
  30. Editing vector geometries
  31. Using measuring tools
  32. Editing attributes
  33. Reprojecting and converting vector and raster data
  34. Joining tabular data
  35. Using temporary scratch layers
  36. Checking for topological errors and fixing them
  37. Adding data to spatial databases
  38. Summary
  39. 4. Spatial Analysis
  40. Combining raster and vector data
  41. Vector and raster analysis with Processing
  42. Leveraging the power of spatial databases
  43. Summary
  44. 5. Creating Great Maps
  45. Labeling
  46. Designing print maps
  47. Presenting your maps online
  48. Summary
  49. 6. Extending QGIS with Python
  50. Getting to know the Python Console
  51. Creating custom geoprocessing scripts using Python
  52. Developing your first plugin
  53. Summary
  54. 2. Module 2
  55. 1. Exploring Places – from Concept to Interface
  56. Acquiring data for geospatial applications
  57. Visualizing GIS data
  58. The basemap
  59. Summary
  60. 2. Identifying the Best Places
  61. Raster analysis
  62. Publishing the results as a web application
  63. Summary
  64. 3. Discovering Physical Relationships
  65. Spatial join for a performant operational layer interaction
  66. The CartoDB platform
  67. Leaflet and an external API: CartoDB SQL
  68. Summary
  69. 4. Finding the Best Way to Get There
  70. OpenStreetMap data for topology
  71. Database importing and topological relationships
  72. Creating the travel time isochron polygons
  73. Generating the shortest paths for all students
  74. Web applications – creating safe corridors
  75. Summary
  76. 5. Demonstrating Change
  77. TopoJSON
  78. The D3 data visualization library
  79. Summary
  80. 6. Estimating Unknown Values
  81. Interpolated model values
  82. A dynamic web application – OpenLayers AJAX with Python and SpatiaLite
  83. Summary
  84. 7. Mapping for Enterprises and Communities
  85. The cartographic rendering of geospatial data – MBTiles and UTFGrid
  86. Interacting with Mapbox services
  87. Putting it all together
  88. Going further – local MBTiles hosting with TileStream
  89. Summary
  90. 3. Module 3
  91. 1. Data Input and Output
  92. Finding geospatial data on your computer
  93. Describing data sources
  94. Importing data from text files
  95. Importing KML/KMZ files
  96. Importing DXF/DWG files
  97. Opening a NetCDF file
  98. Saving a vector layer
  99. Saving a raster layer
  100. Reprojecting a layer
  101. Batch format conversion
  102. Batch reprojection
  103. Loading vector layers into SpatiaLite
  104. Loading vector layers into PostGIS
  105. 2. Data Management
  106. Joining layer data
  107. Cleaning up the attribute table
  108. Configuring relations
  109. Joining tables in databases
  110. Creating views in SpatiaLite
  111. Creating views in PostGIS
  112. Creating spatial indexes
  113. Georeferencing rasters
  114. Georeferencing vector layers
  115. Creating raster overviews (pyramids)
  116. Building virtual rasters (catalogs)
  117. 3. Common Data Preprocessing Steps
  118. Converting points to lines to polygons and back – QGIS
  119. Converting points to lines to polygons and back – SpatiaLite
  120. Converting points to lines to polygons and back – PostGIS
  121. Cropping rasters
  122. Clipping vectors
  123. Extracting vectors
  124. Converting rasters to vectors
  125. Converting vectors to rasters
  126. Building DateTime strings
  127. Geotagging photos
  128. 4. Data Exploration
  129. Listing unique values in a column
  130. Exploring numeric value distribution in a column
  131. Exploring spatiotemporal vector data using Time Manager
  132. Creating animations using Time Manager
  133. Designing time-dependent styles
  134. Loading BaseMaps with the QuickMapServices plugin
  135. Loading BaseMaps with the OpenLayers plugin
  136. Viewing geotagged photos
  137. 5. Classic Vector Analysis
  138. Selecting optimum sites
  139. Dasymetric mapping
  140. Calculating regional statistics
  141. Estimating density heatmaps
  142. Estimating values based on samples
  143. 6. Network Analysis
  144. Creating a simple routing network
  145. Calculating the shortest paths using the Road graph plugin
  146. Routing with one-way streets in the Road graph plugin
  147. Calculating the shortest paths with the QGIS network analysis library
  148. Routing point sequences
  149. Automating multiple route computation using batch processing
  150. Matching points to the nearest line
  151. Creating a routing network for pgRouting
  152. Visualizing the pgRouting results in QGIS
  153. Using the pgRoutingLayer plugin for convenience
  154. Getting network data from the OSM
  155. 7. Raster Analysis I
  156. Using the raster calculator
  157. Preparing elevation data
  158. Calculating a slope
  159. Calculating a hillshade layer
  160. Analyzing hydrology
  161. Calculating a topographic index
  162. Automating analysis tasks using the graphical modeler
  163. 8. Raster Analysis II
  164. Calculating NDVI
  165. Handling null values
  166. Setting extents with masks
  167. Sampling a raster layer
  168. Visualizing multispectral layers
  169. Modifying and reclassifying values in raster layers
  170. Performing supervised classification of raster layers
  171. 9. QGIS and the Web
  172. Using web services
  173. Using WFS and WFS-T
  174. Searching CSW
  175. Using WMS and WMS Tiles
  176. Using WCS
  177. Using GDAL
  178. Serving web maps with the QGIS server
  179. Scale-dependent rendering
  180. Hooking up web clients
  181. Managing GeoServer from QGIS
  182. 10. Cartography Tips
  183. Using Rule Based Rendering
  184. Handling transparencies
  185. Understanding the feature and layer blending modes
  186. Saving and loading styles
  187. Configuring data-defined labels
  188. Creating custom SVG graphics
  189. Making pretty graticules in any projection
  190. Making useful graticules in printed maps
  191. Creating a map series using Atlas
  192. 11. Extending QGIS
  193. Defining custom projections
  194. Working near the dateline
  195. Working offline
  196. Using the QspatiaLite plugin
  197. Adding plugins with Python dependencies
  198. Using the Python console
  199. Writing Processing algorithms
  200. Writing QGIS plugins
  201. Using external tools
  202. 12. Up and Coming
  203. Preparing LiDAR data
  204. Opening File Geodatabases with the OpenFileGDB driver
  205. Using Geopackages
  206. The PostGIS Topology Editor plugin
  207. The Topology Checker plugin
  208. GRASS Topology tools
  209. Hunting for bugs
  210. Reporting bugs
  211. Bibliography
  212. Index

The D3 data visualization library

D3 is a JavaScript library used for building the visualizations from the Document Object Model (DOM) present in all the modern web browsers.

What is D3?

In more detail, D3 manipulates the DOM into abstract vector visualization components, some of which have been further tailed to certain visualization types, such as maps. It provides us with the ability of parsing from some common data sources and binding, especially to the SVG and canvas elements that are designed to be manipulated for vector graphics.

Some fundamentals

There are a few basic aspects of D3 that are useful for you to understand before we begin. As D3 is not specifically built for geographic data, but rather for general data visualization, it tends to look at geographic data visualization more abstractly. Data must be parsed from its original format into a D3 object and rendered into the graphic space as an SVG or canvas element with a vector shape type. It must then be projected using relative mapping between the graphic space and a geographic coordinate system, scaled in relation to the graphic space and the geographic extent, and bound to a web object. This all must be done in relation to a D3 cursor of sorts, which handles the current scope that D3 is working in with keywords like "begin" and "end".

Parsing

We will be parsing through the d3.json and d3.csv methods. We use the callbacks of these methods to wrap the code that we want to be executed after the external data has been parsed into a JavaScript object.

Graphic elements, SVG, path, and Canvas

D3 makes heavy use of the two vector graphic elements in HTML5: SVG and Canvas. Scaleable Vector Graphics (SVG) is a mature technology for rendering vector graphics in the browser. It has seen some advancement in cross-browser support recently. Canvas is new to HTML5 and may offer better performance than SVG. Both, being DOM elements, are written directly as a subset of the larger HTML document rendered by the browser. Here, we will use SVG.

Projection

D3 is a bit unusual where geographic visualization libraries are concerned, in that it requires very little functionality specific to geographic data. The main geographic method provided is through the path element, projection, as D3 has its own concept of coordinate space, coordinates of the browser window and elements inside it.

Here is an example of projection. In the first line, we set the projection as Mercator. This allows us to center the map in familiar spherical latitude longitude coordinates. The scale property allows us to then zoom closer to the extent that we are interested in.

var projection = d3.geo.mercator()
  .center([-75.166667,40.03])
  .scale(60000);

Shape generator

You must configure a shape generator to bind to the d attribute of an SVG. This will tell the element how to draw the data that has been bound to it.

The main shape generator that we will use with the maps is path. Circle is also used in the following example, though its use is more complicated.

The following code creates a path shape generator, assigns it a projection, and stores it all in variable path:

var path = d3.geo.path()
  .projection(projection);

Scales

Scales allow the mapping of a domain of real data; say you have values of 1 through 100, in a range of possible values, and say you want everything down to numbers from 1 through 5. The most useful purpose of scales in mapping is to associate a range of values with a range of colors. The following code maps a set of values to a range of colors, mapping in-between values to intermediate colors:

var color = d3.scale.linear()
  .domain([-.5, 0, 2.66])
    .range(["#FFEBEB", "#FFFFEB", "#E6FFFF"]);

Binding

After a data object has been parsed into the DOM, it can be bound to a D3 object through its data or datum attribute.

Select, Select All, Enter, Return, Exit, Insert, and Append

In order to select the potentially existing elements, you will use the Select and Select All keywords. Then, based on whether you expect the elements to already be existent, you will use the Enter (if it is not yet existent), Return (if it is already existent), and Exit (if you wish to remove it) keywords to change the interaction with the element.

Here's an example of Select All, which uses the Enter keyword. The data from the house_district JSON, which was previously parsed, is loaded through the d attribute of the path element and assigned the path shape generator. In addition, a function is set on the fill attribute, which returns a color from the linear color scale:

map.selectAll("path")
  .data(topojson.feature(phila, phila.objects.house_district).features)
  .enter()
    .append("path")
    .attr("vector-effect","non-scaling-stroke")
    .style("fill", function(d) { return color(d.properties.d_avg_change); })
    .attr("d", path);

Animated time series map

Through the following steps, we will produce an animated time series map with D3. We will start by moving our data to a filesystem path that we will use:

  1. Move whites.csv to c5/data/web/csv.
  2. Move house_district.json to c5/data/web/json.

The development environment

Start the Python HTTP server using the code from Chapter 1, Exploring Places – from Concept to Interface, (refer to the Parsing the JSON data section from Chapter 7, Mapping for Enterprises and Communities). This is necessary for this example, since the typical cross-site scripting protection on the browsers would block the loading of the JSON files from the local filesystem.

You will find the following files and directory structure under c5/data/web:

./

index.html

./css/

main.css

./csv/

whites.csv (you moved this here)

./images/

Various supporting images

./js/

main.js

./json/

house_district.json (you moved this here)

./lib/

  • d3.slider.js
  • d3.slider.css
  • d3.v3.min.js
  • topojson.v1.min.js

Code

The following code, mostly JavaScript, will provide a time-based animation of our geographic objects through D3. This code is largely based on the one found at TIP Strategies' Geography of Jobs map found at http://tipstrategies.com/geography-of-jobs/. The main code file is at c5/data/web/js/main.js.

Note the reference to the CSV and TopoJSON files that we created earlier: whites.csv and house_district.json.

main.js

All of the following JavaScript code is in ./js/main.js. All our customizations to this code will be done in this file:

var width = 960,
  height = 600;

//sets up the transformation from map coordinates to DOM coordinates
var projection = d3.geo.mercator()
  .center([-75.166667,40.03])
  .scale(60000);

//the shape generator
var path = d3.geo.path()
  .projection(projection);

var svg = d3.select("#map-container").append("svg")
  .attr("width", width)
  .attr("height", height);

var g = svg.append("g");

g.append( "rect" )
  .attr("width",width)
  .attr("height",height)
  .attr("fill","white")
  .attr("opacity",0)
  .on("mouseover",function(){
    hoverData = null;
    if ( probe ) probe.style("display","none");
  })

var map = g.append("g")
  .attr("id","map");

var probe,
  hoverData;

var dateScale, sliderScale, slider;

var format = d3.format(",");

  var months = ["Jan"],
    months_full = ["January"],
    orderedColumns = [],
    currentFrame = 0,
    interval,
    frameLength = 1000,
    isPlaying = false;

var sliderMargin = 65;

function circleSize(d){
  return Math.sqrt( .02 * Math.abs(d) );
};

//color scale
var color = d3.scale.linear()
  .domain([-.5, 0, 2.66])
    .range(["#FFEBEB", "#FFFFEB", "#E6FFFF"]);

//parse house_district.json TopoJSON, reference color scale and other styles
d3.json("json/house_district.json", function(error, phila) {
  map.selectAll("path")
    .data(topojson.feature(phila, phila.objects.house_district).features)
      .enter()
      .append("path")
      .attr("vector-effect","non-scaling-stroke")
      .attr("class","land")
      .style("fill", function(d) { return color(d.properties.d_avg_change); })
      .attr("d", path);

  //add a path element for district outlines
  map.append("path")
    .datum(topojson.mesh(phila, phila.objects.house_district, function(a, b) { return a !== b; }))
      .attr("class", "state-boundary")
      .attr("vector-effect","non-scaling-stroke")
      .attr("d", path);

  //probe is for popups
  probe = d3.select("#map-container").append("div")
    .attr("id","probe");

  d3.select("body")
    .append("div")
    .attr("id","loader")
    .style("top",d3.select("#play").node().offsetTop + "px")
    .style("height",d3.select("#date").node().offsetHeight + d3.select("#map-container").node().offsetHeight + "px");

  //load and parse whites.csv
  d3.csv("csv/whites.csv",function(data){
    var first = data[0];
    // get columns
    for ( var mug in first ){
      if ( mug != "name" && mug != "lat" && mug != "lon" ){
        orderedColumns.push(mug);
    }
  }

  orderedColumns.sort( sortColumns );

  // draw city points 
  for ( var i in data ){
    var projected = projection([ parseFloat(data[i].lon), parseFloat(data[i].lat) ])
    map.append("circle")
      .datum( data[i] )
      .attr("cx",projected[0])
      .attr("cy",projected[1])
      .attr("r",1)
      .attr("vector-effect","non-scaling-stroke")
      .on("mousemove",function(d){
        hoverData = d;
        setProbeContent(d);
        probe
        .style( {
          "display" : "block",
          "top" : (d3.event.pageY - 80) + "px",
          "left" : (d3.event.pageX + 10) + "px"
        })
      })
      .on("mouseout",function(){
        hoverData = null;
        probe.style("display","none");
      })
    }

    createLegend();

    dateScale = createDateScale(orderedColumns).range([0,3]);

    createSlider();

    d3.select("#play")
      .attr("title","Play animation")
      .on("click",function(){
        if ( !isPlaying ){
          isPlaying = true;
          d3.select(this).classed("pause",true).attr("title","Pause animation");
          animate();
        } else {
          isPlaying = false;
          d3.select(this).classed("pause",false).attr("title","Play animation");
          clearInterval( interval );
        }
      });

      drawMonth( orderedColumns[currentFrame] ); // initial map

      window.onresize = resize;
       resize();

      d3.select("#loader").remove();

    }) 

});

Output

The finished product, which you can view by opening index.html in a web browser, is an animated set of points controlled by a timeline showing the change in the white population by the census tract. This data is displayed on top of the House Districts, colored from cool to hot by the change in the white population per year, and averaged over three periods of change (2010-11, 2011-12, and 2012-13). Our map application output, animated with a timeline, will look similar to this:

Output