7 Dec 2014

QspatiaLite Quicktip: Convert MULTILINESTRING to LINESTRING

One often encounters the problem, that after digitizing or running processing algorithms, the output geometry is MULTILINESTRING, but we rather wished to have the geometrytype LINESTRING. Until know I used a quite cumbersome, multistep workflow for conversion between these geometry-types - however, as we will see, all of this becomes ridicously easy with spatial SQL:

select 
   replace(replace(replace(replace(replace(replace(astext(Collect(t.geometry)), 'MULTILINESTRING((','§'), '))', '%'), '(', ''), ')', ''), '§', 'LINESTRING('), '%', ')'
) as geom
from (
    select MultiLinestringFromText('MULTILINESTRING((-1 -1, 0 0), (1 1, 4 4))') as geometry
) as t

resulting in:
LINESTRING(-1 -1, 0 0, 1 1, 4 4)

However, if your orginal line was something like MULTILINESTRING((-1 -1, 0 0), (0 0, 4 4))
you'd end up with:

LINESTRING(-1 -1, 0 0, 0 0, 4 4)

which contains double vertices, which we certainly don't want!

So be aware, that the ordering / direction of the linestring will be as in the segments of the original layer! And as we saw, gaps between subsequent end-/startnodes will be closed in the new geometry!! It is adviseable to doublecheck before / after conversion!

If you deal with a multilinestring (or a combination of any type of linesstrings) which share end/startnodes nodes things are even easieruse this SQL:

SELECT AsText(Linemerge(MultiLinestringFromText('MULTILINESTRING((-1 -1, 0 0), (0 0, 4 4))')))

resulting in:
LINESTRING(-1 -1, 0 0, 4 4)

6 Dec 2014

QspatiaLite Use Case: Query for Species Richness within Search-Radius

Following up my previous blogpost on using SpatiaLite for the calculation of diversity metrics from spatial data, I'll add this SQL-query which counts unique species-names from the intersection of species polygons and a circle-buffer around centroids of an input grid. The species number within the bufferarea are joined to a newly created grid. I use a subquery which grabs only those cells from the rectangular input grid, for which the condition that the buffer-area around the grid-cell's centroid covers the species unioned polygons at least to 80%.



  • Example data is HERE. You can use the shipped qml-stylefile for the newly generated grid. It labels three grid-cells with the species counts for illustration.

  • Import grid- and Sp_distr-layers with QspatiaLite Plugin

  • Run query and choose option "Create spatial table and load in QGIS", mind to set "geom" as geometry column

    select 
        g1.PKUID as gID,
        count (distinct s.species) as sp_num_inbu, 
        g1.Geometry AS geom
    from (
     select g.*
     from(select Gunion(geometry) as geom
               from Sp_distr) as u, grid as g
     where area(intersection(buffer(centroid(g.geometry), 500), u.geom)) > pow(500, 2)*pi()*0.8
    ) as g1 join Sp_distr as s on intersects(buffer(centroid( g1.Geometry), 500), s.Geometry)
    group by gID
    

  • 5 Dec 2014

    QspatiaLite Use Case: Get Subselection of Grid which Covers Polygon

    Here's another short SQL-query which I used to get a subselect from a rectengular grid. Aim is to keep only the grid-cells that fully cover the area of a second polygon-layer - cells which do not overlap the polygon's area completely will be skipped from the new grid-layer.

    select 
      g.*
    from(select Gunion(geometry) as geom
               from MYPLGN) as u, grid as g
    where area(intersection(g.geometry, u.geom)) = area(g.geometry)
    

    2 Dec 2014

    QspatiaLite Use Case: Connect Points with Same ID with Line Using the QspatiaLite Plugin

    Another short example illustrating the effectiveness of geoprocessing with SpatiaLite, using the great QGIS-plugin QspatialLite.

  • We have a point-layer with an ID column ("Birds"), with each ID occuring twice, each ID representing an individual. The Ids should be used as start- & end-nodes for the connecting lines. Note that this also would apply if there were more than two points - then the same query could be used to connect all bird individual's points to a line by the order in each group!

  • We want each set of points, grouped by ID, to be connected. This is easily achieved by importing the points to a SpatiaLite-DB with the QspatiaLite plugin and running a very simple query:

    SELECT 
        ID,
        makeline(Geometry) AS geom
    FROM Birds
    GROUP BY ID
    

  • Load the result to QGIS and that's it!

  • 1 Dec 2014

    QspatiaLite Use Case: Find Dominant Species and Species Count within Sampling Areas Using the QspatiaLite Plugin

    This blogpost shows how to find the dominant species and species counts within sampling polygons. The Species-layer that I'll use here is comprised of overlapping polygons which represent the distribution of several species. The Regions-layer represents areas of interest over which we would like to calculate some measures like species count, dominant species and area occupied by the dominant species.

    Since QGIS now makes import/export and querying of spatial data easy, we can use the spatiaLite engine to join the intersection of both layers to the region table and then aggregate this intersections by applying max- and count-function on each region. We'll also keep the identity and the area-value of the species with the largest intersecting area.

    For the presented example I'll use
  • Regions, which is a polygon layer with a areas of interest
  • Species, which is a polygon layer with overlapping features, representing species

    Do the calculation in 2 easy steps:
  • Import the layers to a spatiaLite DB with the Import function of the plugin (example data: HERE)
  • Run the query. For later use you can load this table to QGIS or export with the plugin's export button.

    SELECT   
      t.region AS region,
      t.species AS sp_dom,
      count(*) AS sp_number,
      max(t.sp_area) / 10000 AS sp_dom_area
      FROM ( 
          SELECT
              g.region AS region, s.species AS species,
              area(intersection(g.Geometry, s.Geometry)) AS sp_area
              FROM Regions AS g JOIN Sp_Distribution AS s 
              ON INTERSECTS(g.Geometry, s.Geometry)  
      ) AS t
    GROUP BY t.region
    ORDER BY t.region
    

    Addendum:
    If you wish to calculate any other diversity measures, like Diversity- or Heterogenity-Indices, you might just run the below query (which actually is the subquery from above) and feed the resulting table to any statistic-software!

    The output table will contain region's IDs, each intersecting species and the intersection area.
    The intersection area, which is the species' area per polygon, is the metric that would be used for the calculation of diversity / heterogenity measures, etc. of regions!

    SELECT 
      g.region AS regID, 
      s.species AS sp,
      AREA(INTERSECTION(g.geometry, s.geometry)) AS sp_area
    FROM Regions AS g JOIN Sp_Distribution AS s 
    ON INTERSECTS(g.Geometry,s.Geometry)
    ORDER BY regID, sp_area ASC
    



    I tested this on
  • QGIS 2.6 Brighton
  • with the QspatiaLite Plugin installed
  • QspatiaLite Use Case: SpatiaLite Aggregation over Points within Polygons using the QspatiaLite Plugin

    Here's a nice example for aggregation of points per polygon areas, which I grabbed from an Answer on SO, by user @Micha. The polygons could be regions of interest, a sampling grid, etc.
    Say you want to do maximum, minimum, averages, etc. per polygon using the spatial database SpatiaLite.


  • You'll first need to import both of your layers into a spatialite DB, called "sensors" (the point layer) here, with a "pollution" column and "SHAPE1" (the polygons) with a "plgnID" column. You can do this easily with the QspatiaLite-plugin "Import" button (example data is HERE).

  • Now this query will give you various statistics from the sensors for each polygon:

    SELECT g.plgnID AS "plgn_ID",
       AVG(s.pollution) AS "Average Pollution", 
       MAX(s.pollution) AS "Maximum Pollution",
       COUNT(*) AS "Number of Sensors"
    FROM sensors AS s JOIN SHAPE1 AS g 
    ON contains(g.geometry, s.geometry)
    GROUP BY g.plgnID