Sean Gillies (Posts about map)https://sgillies.net/tags/map.atom2023-12-31T01:26:19ZSean GilliesNikolaLinking GeoJSONhttps://sgillies.net/2013/10/08/linking-geojson.html2013-10-08T00:00:00-06:002013-10-08T00:00:00-06:00Sean Gillies<p>I've blogged a few times (
<a class="reference external" href="http://sgillies.net/blog/888/openlayers-constrained-by-hypertext/">here</a>
and <a class="reference external" href="http://sgillies.net/blog/958/geojson-data-uris/">here</a>) about a pattern
I've developed for mapping feature data associated with a web page.
Lyzi Diamond wrote a great post about this pattern a few weeks ago:
<a class="reference external" href="http://lyzidiamond.com/posts/osgeo-august-meeting/">http://lyzidiamond.com/posts/osgeo-august-meeting/</a>. I'm thrilled that it's
catching on a bit. The pattern is super simple, only three short paragraphs are
needed to describe it.</p>
<p>Let's say I have a collection of web pages. Each is about a particular batch of
features. <a class="reference external" href="http://sgillies.github.io/syriaca/">http://sgillies.github.io/syriaca/</a>, for example, is a demo of a page
about Syriac places in antiquity. It bears a map in which the places are
rendered.</p>
<p>The features to be rendered in the map are obtained by making an HTTP request
for a GeoJSON resource. In my case
<a class="reference external" href="http://sgillies.github.io/syriaca/syriaca.json">http://sgillies.github.io/syriaca/syriaca.json</a> is fetched using
jQuery.getJSON().</p>
<div class="code"><pre class="code javascript"><a id="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-1" name="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-1" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-1"></a><span class="kd">var</span><span class="w"> </span><span class="nx">map</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="s1">'map'</span><span class="p">);</span><span class="w"></span>
<a id="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-2" name="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-2" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-2"></a><span class="kd">var</span><span class="w"> </span><span class="nx">geojson</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">L</span><span class="p">.</span><span class="nx">geoJson</span><span class="p">().</span><span class="nx">addTo</span><span class="p">(</span><span class="nx">map</span><span class="p">);</span><span class="w"></span>
<a id="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-3" name="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-3" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-3"></a>
<a id="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-4" name="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-4" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-4"></a><span class="nx">$</span><span class="p">.</span><span class="nx">getJSON</span><span class="p">(</span><span class="nx">geojson_uri</span><span class="p">,</span><span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<a id="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-5" name="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-5" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-5"></a><span class="w"> </span><span class="nx">geojson</span><span class="p">.</span><span class="nx">addData</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span><span class="w"></span>
<a id="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-6" name="rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-6" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_600b65a00e3b4d6ea3aaa0f44c23e564-6"></a><span class="p">});</span><span class="w"></span>
</pre></div>
<p>The feature data is not written into a script in the page as
they are in <a class="reference external" href="http://leafletjs.com/examples/quick-start-example.html">http://leafletjs.com/examples/quick-start-example.html</a>. Yes,
this means an extra HTTP request, but one that can be ansynchronous and
cacheable.</p>
<p>Here's the crux of the pattern: the URI of that GeoJSON resource bound to the
geojson_uri variable is specified not in the map script, but in a link in the
head of the page.</p>
<div class="code"><pre class="code html"><a id="rest_code_74570743e1b34efe99deca20b9ea062b-1" name="rest_code_74570743e1b34efe99deca20b9ea062b-1" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_74570743e1b34efe99deca20b9ea062b-1"></a><span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"location"</span> <span class="na">type</span><span class="o">=</span><span class="s">"application/json"</span> <span class="na">href</span><span class="o">=</span><span class="s">"syriaca.json"</span><span class="p">/></span>
</pre></div>
<p>That URI is found by its "location" relation type.</p>
<div class="code"><pre class="code javascript"><a id="rest_code_84a4e6986b0c4611a8f4a527aeebc8e6-1" name="rest_code_84a4e6986b0c4611a8f4a527aeebc8e6-1" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_84a4e6986b0c4611a8f4a527aeebc8e6-1"></a><span class="kd">var</span><span class="w"> </span><span class="nx">geojson_uri</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">$</span><span class="p">(</span><span class="s1">'link[rel="location"]'</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"href"</span><span class="p">);</span><span class="w"></span>
</pre></div>
<p>What's gained by having my Javascript follow a link to get GeoJSON features?
Why not just write the features into a script? I've developed this pattern
because it decouples the map from the data and lets me generalize the mapping
script so I can reuse it across many pages, and also because I'm not thinking
just about the slippy maps in the page. I'm keeping the bigger web in mind.</p>
<p>If the Javascript in my page was my only consideration I'd just embed the
features in a script and maybe not use GeoJSON at all. But a resource like
<a class="reference external" href="http://sgillies.github.io/syriaca/">http://sgillies.github.io/syriaca/</a> isn't just a page with a slippy map, it's
a starting point for other web map making software. The links are also for web
clients written by people other than me.</p>
<p>Given the page's URL, a Python web client can get the same GeoJSON in the
same way.</p>
<div class="code"><pre class="code python"><a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-1" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-1" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-1"></a><span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-2" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-2" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-2"></a><span class="kn">import</span> <span class="nn">requests</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-3" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-3" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-3"></a>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-4" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-4" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-4"></a><span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'http://sgillies.github.io/syriaca/'</span><span class="p">)</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-5" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-5" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-5"></a><span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">content</span><span class="p">)</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-6" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-6" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-6"></a><span class="n">points</span> <span class="o">=</span> <span class="p">[</span><span class="n">t</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">soup</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="s1">'link'</span><span class="p">)</span> <span class="k">if</span> <span class="s1">'location'</span> <span class="ow">in</span> <span class="n">t</span><span class="p">[</span><span class="s1">'rel'</span><span class="p">]]</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-7" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-7" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-7"></a><span class="nb">print</span> <span class="n">points</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-8" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-8" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-8"></a>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-9" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-9" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-9"></a><span class="c1"># Output:</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-10" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-10" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-10"></a><span class="c1"># [<link href="http://sgillies.github.io/syriaca/syriaca.json" rel="location"/>]</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-11" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-11" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-11"></a>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-12" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-12" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-12"></a><span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">points</span><span class="p">:</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-13" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-13" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-13"></a> <span class="n">s</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">url</span> <span class="o">+</span> <span class="n">p</span><span class="p">[</span><span class="s1">'href'</span><span class="p">])</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-14" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-14" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-14"></a> <span class="n">json</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">json</span><span class="p">()</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-15" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-15" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-15"></a> <span class="nb">print</span> <span class="n">json</span><span class="p">[</span><span class="s1">'type'</span><span class="p">],</span> <span class="nb">len</span><span class="p">(</span><span class="n">json</span><span class="p">[</span><span class="s1">'features'</span><span class="p">])</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-16" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-16" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-16"></a>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-17" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-17" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-17"></a><span class="c1"># Output:</span>
<a id="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-18" name="rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-18" href="https://sgillies.net/2013/10/08/linking-geojson.html#rest_code_fa0c4b50849a43399a7cf1f17cd2be1b-18"></a><span class="c1"># FeatureCollection 955</span>
</pre></div>
<p>The GeoJSON URI is much easier to find in a link by its relation type than
by scraping it from a script (were I to use a literal in my first code
example above instead of a geojson_uri variable). There's a IETF draft
written by James Snell proposing standardization of the "location" relation:
<a class="reference external" href="http://tools.ietf.org/html/draft-snell-more-link-relations-01">http://tools.ietf.org/html/draft-snell-more-link-relations-01</a>. My pattern
becomes much more useful with such a standard link relation.</p>
<p>Does this pattern avoid the browser's same-origin policy as Lyzi suggests in
<a class="reference external" href="http://lyzidiamond.com/posts/external-geojson-and-leaflet-the-other-way/">http://lyzidiamond.com/posts/external-geojson-and-leaflet-the-other-way/</a>? As
far as I know, links with rel="stylesheet" are the only ones that a browser
fetches unprompted. The GeoJSON fetched by XHR is still subject to same-origin
policy.</p>