<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>stplanr | Robin Lovelace</title><link>https://robinlovelace.net/old-site/tag/stplanr/</link><atom:link href="https://robinlovelace.net/old-site/tag/stplanr/index.xml" rel="self" type="application/rss+xml"/><description>stplanr</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Thu, 14 Feb 2019 00:00:00 +0000</lastBuildDate><image><url>https://robinlovelace.net/old-site/media/icon_hu93dbabadc2a9bdd4930d1377c0b338b2_5137_512x512_fill_lanczos_center_3.png</url><title>stplanr</title><link>https://robinlovelace.net/old-site/tag/stplanr/</link></image><item><title>Aggregating lines, part II</title><link>https://robinlovelace.net/old-site/post/aggregating-lines-part-ii/</link><pubDate>Thu, 14 Feb 2019 00:00:00 +0000</pubDate><guid>https://robinlovelace.net/old-site/post/aggregating-lines-part-ii/</guid><description>&lt;p>The &lt;a href="https://www.robinlovelace.net/2018/10/27/aggregating-lines/" target="_blank" rel="noopener">previous post&lt;/a> demonstrated a new method to aggregate overlapping lines.
It showed how to combine 2 lines that have an area of overlap.
More excitingly, it led to the creation of a new function in &lt;strong>stplanr&lt;/strong>, &lt;code>overline_sf()&lt;/code>, that lives in the development version of the package.
The purpose of this post is to provide an update on the status of the work to refactor the &lt;code>overline()&lt;/code> function, in a human friendly alternative to discussion in the relevant GitHub issue: &lt;a href="https://github.com/ropensci/stplanr/issues/273" target="_blank" rel="noopener">https://github.com/ropensci/stplanr/issues/273&lt;/a>&lt;/p>
&lt;h2 id="set-up">Set-up&lt;/h2>
&lt;p>To re-cap, these are the package versions we&amp;rsquo;ll be using in this post (run this line to get the development version of stplanr if you want to reproduce the results):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">remotes&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">install_github&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ropensci/stplanr&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ref&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;refactor-overline&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The library is loaded and the input data from the previous post was loaded as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stplanr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">routes_fast_sf&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">routes_fast_sf[2&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">overline_sf2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attrib1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">paste0&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrib&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;.1&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_intersection&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_intersection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl[2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_intersection[[attrib]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sl_intersection[[attrib]]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">sl_intersection[[attrib1]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_intersection[[attrib1]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">NULL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_seg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_difference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[attrib]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_geometry&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_seg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">return&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This was used as the basis of a demonstration of how overline works, demonstrated in the following code chunk and resulting plot:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet[&lt;/span>&lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[&lt;/span>&lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sl&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">sf.colors&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">0.5&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dealing-with-many-lines">Dealing with many lines&lt;/h2>
&lt;p>The above method worked with 2 lines but how can it be used to process many lines?
Clearly the same function could be implemented on another line, but it would need to work from the 3 lines of the newly created &lt;code>rnet&lt;/code> object rather than the original 2 routes.
Let&amp;rsquo;s introduce a 3^rd^ route into the equation, that does not intersect with this newly created &lt;strong>rnet&lt;/strong> object:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">routes_fast_sf[4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline_sf2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;red&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this case the method of adding to rnet is simple: just add the entirety of the line to the &lt;code>rnet&lt;/code> object:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl3[attrib]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet3&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet3&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This works fine.
In fact it works better than the original &lt;code>overline&lt;/code> function because it does not add the value of the existing thickest line in the previous figure onto the new line, a problem associated with &lt;code>overline.sp()&lt;/code> that is illustrated in the following code chunk and resulting figure:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl1_3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">as&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">routes_fast_sf[2&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="m">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Spatial&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet3_sp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl1_3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet3_sp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet3_sp&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A question that arises from the previous example is this: what if the next line intersects with the route network?
It is no longer possible to simply add together two values.
This can be illustrated by introducing 2 more lines:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl4_5&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">routes_fast_sf[5&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="m">6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet3&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet3&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl4_5&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;red&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Both the new lines intersect with the newest part of the route network.
This means that we cannot simply &lt;code>rbind()&lt;/code> them to it as we did for &lt;code>sl3&lt;/code>.
They need to be dealt with separately.&lt;/p>
&lt;p>Before we deal with them, it&amp;rsquo;s worth taking some time to consider what we mean by &amp;lsquo;intersect&amp;rsquo;.
Intersection is actuall a specific type of geometric relation between 2 sets of features.
We can see the type of relation by using the function &lt;code>st_relate()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">relations&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_relate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl4_5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rnet3&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">relations&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">unique&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">as.vector&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relations&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This shows us something important: although 2 elements (1 and 4) of &lt;code>rnet&lt;/code> relate &lt;em>in some way&lt;/em> to the new lines, only the 4^th^ feature has a linear, overlapping relation with it.
That relation is &lt;code>1F1F00102&lt;/code> which, as far as I can tell, is not a &lt;a href="https://en.wikipedia.org/wiki/DE-9IM#Spatial_predicates" target="_blank" rel="noopener">named spatial predicate&lt;/a> (&lt;code>FF1F00102&lt;/code> means &amp;lsquo;intersects and touches&amp;rsquo; but does not have a linear overlap).
This relation is what we need to decide whether or not to simply bind a new feature to the growing &lt;code>rnet&lt;/code>, whether we need to break it up (or at least part of it) into smaller lines before doing so (it also raises the wider question of which order should we do things).&lt;/p>
&lt;p>In the simple case of &lt;em>whether&lt;/em> to simply bind the next line (4) onto &lt;code>rnet3&lt;/code> the answer is simple now we know the string code associated with linear overlaps.
First we&amp;rsquo;ll test it on the previous example of &lt;code>sl3&lt;/code> and the original &lt;code>rnet&lt;/code> composed of 3 features:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">relate_rnet_3&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_relate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pattern&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;1F1F00102&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">relate_rnet_3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">any&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">lengths&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relate_rnet_3&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>FALSE&lt;/code> meant there was no linear overlaps. So we simply used &lt;code>rbind()&lt;/code>.
When we ask the same question of &lt;code>rnet3&lt;/code> and &lt;code>sl4&lt;/code>, however, the answer is &lt;code>TRUE&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl4&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sl4_5[1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">relate_rnet_4&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_relate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pattern&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;1F1F00102&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">relate_rnet_4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">any&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">lengths&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relate_rnet_4&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>How to proceed? We need to avoid &lt;code>rnet&lt;/code> objects containing any overlapping lines.
Because &lt;code>sl4&lt;/code> overlaps with part of &lt;code>rnet3&lt;/code> we will need to remove the overlapping line, run the &lt;code>overline_sf2()&lt;/code> function, and then re-combine the result with the pre-existing route network object.
We can split-up the &lt;code>rnet3&lt;/code> object into overlapping and non-overlapping features as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sel_overlaps&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">lengths&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">relate_rnet_4&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="m">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet_overlaps&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet3[sel_overlaps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet3_tmp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet3[&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">sel_overlaps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can check that there is only one overlapping feature as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">nrow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet_overlaps&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And we can proceed to join the two features together using our new function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet_overlaps4&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline_sf2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet_overlaps&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl4[attrib]&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Adding this back to the &lt;code>rnet3&lt;/code> object results in an larger &lt;code>rnet&lt;/code> object incorporating all the &lt;code>value&lt;/code> and &lt;code>geometry&lt;/code> column data we have so far:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet3_tmp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">rnet_overlaps4&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The information provided so far informed the creation of the following function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">overline_sf&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This function made use of the slightly simpler individual route-joining function, and associated helper functions:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">overline_sf2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">overlaps&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">to_linestring&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Aside from being clunky, this approach has the additional flaw of not working.
It works fine for lines 2 to 6 in &lt;code>routes_fast_sf&lt;/code>, as illustrated below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">r6&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">routes_fast_sf[2&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="m">6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet6&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet6&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A problem arises when we try to run the same command on the same data, but with one more line added.
That will be the topic of the next post on aggregating lines to form route networks, which will also introduce a new function &lt;code>overline2()&lt;/code>, developed by my colleague &lt;a href="https://environment.leeds.ac.uk/transport/staff/964/dr-malcolm-morgan" target="_blank" rel="noopener">Malcolm Morgan&lt;/a>.&lt;/p>
&lt;h2 id="another-benchmark">Another benchmark&lt;/h2>
&lt;p>To provide a taster of the new function, let&amp;rsquo;s see it in action in a benchmark on this latest route network consisting of the five lines illustrated in the previous plot.
Again, we use &lt;code>bench::mark()&lt;/code> to benchmark each approach:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">bench&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">mark&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">check&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">FALSE&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">overline&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">overline&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">overline_sf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">overline_sf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">overline2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">stplanr&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">overline2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r6&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Again the results are not Earth-shattering and should be taken with a large pinch of salt: performance should not be evaluated against these relatively tiny datasets, but against the scale of datasets that the functions were designed to handle: city scale route networks involving thousands of overlapping lines.
The same principle is used in the benchmarking section of the &lt;a href="https://h2oai.github.io/db-benchmark/" target="_blank" rel="noopener">h2oai website&lt;/a>: focus on benchmarks that apply to the datasets you will actually be using.
In this case, my hypothesis is that the geometric functions &lt;code>overline()&lt;/code> and &lt;code>overline_sf()&lt;/code> will perform disproportionately badly as the size of the input dataset grows (to be tested).
Still, it is interesting to note that the new &lt;code>overline2()&lt;/code> function uses way more memory than the other functions.
More next time!&lt;/p></description></item><item><title>Aggregating lines, Part I</title><link>https://robinlovelace.net/old-site/post/aggregating-lines/</link><pubDate>Sat, 27 Oct 2018 00:00:00 +0000</pubDate><guid>https://robinlovelace.net/old-site/post/aggregating-lines/</guid><description>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>It’s been a busy 12 months but with the &lt;a href="https://geocompr.robinlovelace.net/" target="_blank" rel="noopener">Geocomputation with R&lt;/a> book nearing completion&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>
I’ve finally found some time to update my blog and do a bit of thinking, about the tangled topic of line aggregation.&lt;/p>
&lt;h2 id="why-aggregate-lines">Why aggregate lines?&lt;/h2>
&lt;p>The transport ‘flow’ on any particular segment of the transport networks is the aggregate (sum) of trips that pass through it (Hollander 2016).
Finding the flow across a transport network based on input data composed of individual routes, is therefore an aggregation problem.
It requires a more complex solution than that provided by the &lt;code>aggregate()&lt;/code> function in the base R (R Core Team 2018) package &lt;strong>stats&lt;/strong>, however, because the geometry of the output &lt;code>LINESTRING&lt;/code>s will be fundamentally different than the input &lt;code>LINESTRINGS&lt;/code> of the routes: a route network is composed of many small way segments, but a route is a single long &lt;code>LINESTRING&lt;/code>.&lt;/p>
&lt;h2 id="aggregating-lines-with-overline-mki">Aggregating lines with overline() MKI&lt;/h2>
&lt;p>Creating such a route network, with aggregated values per segment, is the problem that the &lt;a href="https://ropensci.github.io/stplanr/reference/overline.html" target="_blank" rel="noopener">&lt;code>overline()&lt;/code>&lt;/a> function in the R package &lt;a href="https://github.com/ropensci/stplanr" target="_blank" rel="noopener">&lt;strong>stplanr&lt;/strong>&lt;/a> was designed to solve (see a 3.5 yr-old question on &lt;a href="https://gis.stackexchange.com/questions/139681/overlaying-lines-and-aggregating-their-values-for-overlapping-segments" target="_blank" rel="noopener">gis.stackexchange.com&lt;/a> for more context).&lt;/p>
&lt;p>The function works well and is the basis of the Route Network layer (MSOA) in the Propensity to Cycle Tool (&lt;a href="http://www.pct.bike/" target="_blank" rel="noopener">PCT&lt;/a>).
In fact it was developed precisely for this purpose, as illustrated in the image below, which shows a common visualization/analysis problem encountered by transport researchers when working with multiple routes: overlapping routes are not easy to identify from non-overlapping routes:
notice the red lines in the centre of Leeds in the image look the same as the red lines on the outskirts, despite representing much more movement.&lt;/p>
&lt;p>To overcome the problem &lt;code>overline()&lt;/code> was born (credit to Barry Rowlingson who wrote the foundations of the function), and it works (on a tiny dataset of 2 lines) as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stplanr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_sp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">routes_fast[2&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_sp&lt;/span>&lt;span class="o">@&lt;/span>&lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">data.frame&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">5&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_sp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;yellow&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet_sp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_sp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet_sp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet_sp&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The utility of such a function is illustrated in the figure below, which shows the original route network layer of the PCT in action over a similar area of Leeds:&lt;/p>
&lt;p>This works great and the resulting network is used for strategic network planning: you can download route network data in the ‘Region data’ tab of the PCT (Lovelace et al. 2017). The route network data for Leeds can, for example, be downloaded as a &lt;code>.geojson&lt;/code> file from &lt;a href="https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/west-yorkshire/rnet.geojson" target="_blank" rel="noopener">here&lt;/a>.&lt;/p>
&lt;p>But there are some issues: the function works on the older &lt;code>SpatialLinesDataFrame&lt;/code> class defined in the &lt;strong>sp&lt;/strong> package (Pebesma 2018).
This data class has been superseded by the simpler &lt;code>sf&lt;/code> class in the &lt;a href="https://github.com/r-spatial/sf" target="_blank" rel="noopener">&lt;strong>sf&lt;/strong>&lt;/a> package, which is faster than &lt;strong>sp&lt;/strong> for &lt;a href="https://github.com/ATFutures/geobench" target="_blank" rel="noopener">some&lt;/a> (if not many) operations.
Another issue with &lt;code>overline()&lt;/code> is that in some cases when 2 lines meet, the resulting aggregated line can be longer than it should be.
So there are performance and functionality issues to address.
Rather than solve them all here, this post sets-out the issue using reproducible code and suggests next steps for a new &lt;code>overline()&lt;/code> function (or perhaps just an updated &lt;code>overline.sf()&lt;/code> function which currently just wraps &lt;code>overline.sp()&lt;/code>).&lt;/p>
&lt;h2 id="aggregating-lines-with-sf">Aggregating lines with sf&lt;/h2>
&lt;p>Rather that starting from scratch and writing a geographic algorithm from the ground-up, we will start by exploring solutions provided by existing packages, notably &lt;strong>sf&lt;/strong>, which provides an interface to the &lt;code>GEOS&lt;/code> library.&lt;/p>
&lt;p>Let’s start simple, with just 2 lines, which have an associated amount of flow (with illustrative values of 2 and 5 in this case):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">stplanr&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">routes_fast_sf[2&lt;/span>&lt;span class="o">:&lt;/span>&lt;span class="m">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">c&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These lines clearly have a decent amount of overlap, which can be extracted using the function &lt;code>st_intersection()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_intersection&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_intersection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl[2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">sf.colors&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">0.5&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Furthermore, we can find the aggregated value associated with this new segment as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_intersection&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">sum&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_intersection&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value.1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We still do not have a full route network composed of 3 non-overlapping lines, however:
the original lines need to be ‘clipped’ so that they do not overlap with &lt;code>sl_intersection&lt;/code>.
This can be done as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_seg1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_difference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_seg2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_difference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">geometry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">9&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">sf.colors&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">0.5&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_seg1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_seg2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">add&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">TRUE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We now have all the geographic components needed for a route network.
The only remaining task is to combine them, using &lt;code>rbind&lt;/code>, right?
Not quite: the following command fails:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_seg1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_seg2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Lesson: we need to be more careful in isolating the value to aggregate.
We will therefore run the previous stages again, but with &lt;code>attrib&lt;/code> set to the attribute we would like to aggregate over (&lt;code>value&lt;/code> in this case):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">attrib1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">paste0&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrib&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;.1&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_intersection&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_intersection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl[2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_intersection[[attrib]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sl_intersection[[attrib]]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">sl_intersection[[attrib1]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_intersection[[attrib1]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">NULL&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That leaves us with a ‘clean’ object that only has a value (7) for the attribute column name we want (&lt;code>value&lt;/code>).&lt;/p>
&lt;p>On this basis we can proceed to create the other segments, keeping only the column we’re interested in. To save time and typing, we’ll create both segments in a single command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">sl_seg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_difference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[attrib]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_geometry&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_seg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It worked! Now we’re in a position to plot the resulting route network, with ‘width’ proportional to the flow along each segment:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="nf">plot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">rnet&lt;/span>&lt;span class="o">$&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="a-benchmark">A benchmark&lt;/h2>
&lt;p>To test that the method is fast, or is at least not slower than the original &lt;code>overline()&lt;/code> function, at least for this task, we’ll package-up the method in a new function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">overline_sf2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">attrib1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">paste0&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">attrib&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;.1&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_intersection&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_intersection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl[2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib]&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_intersection[[attrib]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sl_intersection[[attrib]]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">sl_intersection[[attrib1]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_intersection[[attrib1]]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">NULL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sl_seg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_difference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl[attrib]&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sf&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">st_geometry&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">rnet&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">rbind&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_intersection&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sl_seg&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">return&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">rnet&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you are new to scripts/algorithms/functions, it may be worth taking a look at the new &lt;a href="https://geocompr.robinlovelace.net/algorithms.html" target="_blank" rel="noopener">Algorithms&lt;/a> chapter in our near-complete book that teaches a range of geographic methods that use R (Lovelace, Nowosad, and Muenchow 2019).
Now the method has been put in a function, we can compare its performance with the per-existing &lt;code>overline()&lt;/code> function for comparison:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-r" data-lang="r">&lt;span class="line">&lt;span class="cl">&lt;span class="n">bench&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="nf">mark&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">check&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">F&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">overline.sp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl_sp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">overline.sf&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">overline_sf2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">overline_sf2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">attrib&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The results are not Earth-shattering: the new function seems to be around the same speed as the original, if a little faster.
This is great news, but remember: the new function only works on 2 lines so is much simpler.
More work needed!&lt;/p>
&lt;h2 id="next-steps">Next steps&lt;/h2>
&lt;p>The next step is to generalist this method so it works for many (potentially thousands) of lines in a way that scales, something that should help on the visualization side, a topic that attracts much interest (see for example this &lt;a href="https://gis.stackexchange.com/questions/778/representation-of-network-flows" target="_blank" rel="noopener">gis.stackexchange post&lt;/a> on the subject and, more broadly, a recent &lt;a href="https://nowosad.github.io/post/maps-distortion/" target="_blank" rel="noopener">article&lt;/a> showing how to make animated maps by Jakub Nowosad).
There is also work to do on performance but, as Donald Knuth said (Knuth 1974):&lt;/p>
&lt;blockquote>
&lt;p>premature optimization is the root of all evil (or at least most of it) in programming&lt;/p>
&lt;/blockquote>
&lt;p>So a more complete &lt;code>overline.sf()&lt;/code> function is needed.
That will (hopefully) be the topic of the next blog post.&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;div id="refs" class="references csl-bib-body hanging-indent">
&lt;div id="ref-hollander_transport_2016" class="csl-entry">
&lt;p>Hollander, Yaron. 2016. &lt;em>Transport Modelling for a Complete Beginner&lt;/em>. CTthink!&lt;/p>
&lt;/div>
&lt;div id="ref-knuth_computer_1974" class="csl-entry">
&lt;p>Knuth, Donald E. 1974. “Computer Programming As an Art.” &lt;em>Commun. ACM&lt;/em> 17 (12): 667–73. &lt;a href="https://doi.org/10.1145/361604.361612" target="_blank" rel="noopener">https://doi.org/10.1145/361604.361612&lt;/a>.&lt;/p>
&lt;/div>
&lt;div id="ref-lovelace_propensity_2017" class="csl-entry">
&lt;p>Lovelace, Robin, Anna Goodman, Rachel Aldred, Nikolai Berkoff, Ali Abbas, and James Woodcock. 2017. “The Propensity to Cycle Tool: An Open Source Online System for Sustainable Transport Planning.” &lt;em>Journal of Transport and Land Use&lt;/em> 10 (1). &lt;a href="https://doi.org/10.5198/jtlu.2016.862" target="_blank" rel="noopener">https://doi.org/10.5198/jtlu.2016.862&lt;/a>.&lt;/p>
&lt;/div>
&lt;div id="ref-lovelace_geocomputation_2019" class="csl-entry">
&lt;p>Lovelace, Robin, Jakub Nowosad, and Jannes Muenchow. 2019. &lt;em>Geocomputation with R&lt;/em>. CRC Press.&lt;/p>
&lt;/div>
&lt;div id="ref-pebesma_simple_2018" class="csl-entry">
&lt;p>Pebesma, Edzer. 2018. “Simple Features for R: Standardized Support for Spatial Vector Data.” &lt;em>The R Journal&lt;/em>.&lt;/p>
&lt;/div>
&lt;div id="ref-rcoreteam_language_2018" class="csl-entry">
&lt;p>R Core Team. 2018. &lt;em>R: A Language and Environment for Statistical Computing&lt;/em>. Vienna, Austria: R Foundation for Statistical Computing.&lt;/p>
&lt;/div>
&lt;/div>
&lt;div class="footnotes" role="doc-endnotes">
&lt;hr>
&lt;ol>
&lt;li id="fn:1">
&lt;p>Any input on that still welcome - see the &lt;a href="https://geocompr.robinlovelace.net/index.html#how-to-contribute" target="_blank" rel="noopener">contributing&lt;/a> section for more.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink">&amp;#x21a9;&amp;#xfe0e;&lt;/a>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;/div></description></item></channel></rss>