<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: geojson</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/geojson.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2024-11-20T05:52:38+00:00</updated><author><name>Simon Willison</name></author><entry><title>Foursquare Open Source Places: A new foundational dataset for the geospatial community</title><link href="https://simonwillison.net/2024/Nov/20/foursquare-open-source-places/#atom-tag" rel="alternate"/><published>2024-11-20T05:52:38+00:00</published><updated>2024-11-20T05:52:38+00:00</updated><id>https://simonwillison.net/2024/Nov/20/foursquare-open-source-places/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://location.foursquare.com/resources/blog/products/foursquare-open-source-places-a-new-foundational-dataset-for-the-geospatial-community/"&gt;Foursquare Open Source Places: A new foundational dataset for the geospatial community&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I did not expect this!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[...] we are announcing today the general availability of a foundational open data set, Foursquare Open Source Places ("FSQ OS Places"). This base layer of 100mm+ global places of interest ("POI") includes 22 core attributes (see schema &lt;a href="https://docs.foursquare.com/data-products/docs/places-os-data-schema"&gt;here&lt;/a&gt;) that will be updated monthly and available for commercial use under the Apache 2.0 license framework.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The data is available &lt;a href="https://docs.foursquare.com/data-products/docs/access-fsq-os-places"&gt;as Parquet files&lt;/a&gt; hosted on Amazon S3.&lt;/p&gt;
&lt;p&gt;Here's how to list the available files:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aws s3 ls s3://fsq-os-places-us-east-1/release/dt=2024-11-19/places/parquet/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I got back &lt;code&gt;places-00000.snappy.parquet&lt;/code&gt; through &lt;code&gt;places-00024.snappy.parquet&lt;/code&gt;, each file around 455MB for a total of 10.6GB of data.&lt;/p&gt;
&lt;p&gt;I ran &lt;code&gt;duckdb&lt;/code&gt; and then used DuckDB's ability to remotely query Parquet on S3 to explore the data a bit more without downloading it to my laptop first:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select count(*) from 's3://fsq-os-places-us-east-1/release/dt=2024-11-19/places/parquet/places-00000.snappy.parquet';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This got back 4,180,424 - that number is similar for each file, suggesting around 104,000,000 records total.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; DuckDB can use wildcards in S3 paths (thanks, &lt;a href="https://mas.to/@paulbailey/113520325087085448"&gt;Paul&lt;/a&gt;) so this query provides an exact count:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select count(*) from 's3://fsq-os-places-us-east-1/release/dt=2024-11-19/places/parquet/places-*.snappy.parquet';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That returned 104,511,073 - and Activity Monitor on my Mac confirmed that DuckDB only needed to fetch 1.2MB of data to answer that query.&lt;/p&gt;
&lt;p&gt;I ran this query to retrieve 1,000 places from that first file as newline-delimited JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;copy (
    select * from 's3://fsq-os-places-us-east-1/release/dt=2024-11-19/places/parquet/places-00000.snappy.parquet'
    limit 1000
) to '/tmp/places.json';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's &lt;a href="https://gist.github.com/simonw/53ad57ad42c7efe75e2028d975907180"&gt;that places.json file&lt;/a&gt;, and here it is &lt;a href="https://lite.datasette.io/?json=https://gist.github.com/simonw/53ad57ad42c7efe75e2028d975907180#/data/raw"&gt;imported into Datasette Lite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, I got ChatGPT Code Interpreter to &lt;a href="https://chatgpt.com/share/673d7b92-0b4c-8006-a442-c5e6c2713d9c"&gt;convert that file to GeoJSON&lt;/a&gt; and pasted the result &lt;a href="https://gist.github.com/simonw/1e2a170b7368932ebd3922cb5d234924"&gt;into this Gist&lt;/a&gt;, giving me a map of those thousand places (because Gists automatically render GeoJSON):&lt;/p&gt;
&lt;p&gt;&lt;img alt="A map of the world with 1000 markers on it. A marker in Columbia shows a dialog for Raisbeck, Bogota Dv, Cra 47 A 114 05 Second Floor" src="https://static.simonwillison.net/static/2024/places-geojson.jpg" /&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://waxy.org/2024/11/foursquare-open-sources-its-places-database/"&gt;Andy Baio&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/foursquare"&gt;foursquare&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/parquet"&gt;parquet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/duckdb"&gt;duckdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-lite"&gt;datasette-lite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/code-interpreter"&gt;code-interpreter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="geospatial"/><category term="open-source"/><category term="foursquare"/><category term="geojson"/><category term="parquet"/><category term="duckdb"/><category term="datasette-lite"/><category term="ai-assisted-programming"/><category term="code-interpreter"/><category term="coding-agents"/></entry><entry><title>A POI Database in One Line</title><link href="https://simonwillison.net/2024/Apr/19/a-poi-database-in-one-line/#atom-tag" rel="alternate"/><published>2024-04-19T02:44:58+00:00</published><updated>2024-04-19T02:44:58+00:00</updated><id>https://simonwillison.net/2024/Apr/19/a-poi-database-in-one-line/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.dbreunig.com/2024/04/18/a-poi-database-in-one-line.html"&gt;A POI Database in One Line&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Overture maps offer an extraordinarily useful freely licensed databases of POI (point of interest) listings, principally derived from partners such as Facebook and including restaurants, shops, museums and other locations from all around the world.&lt;/p&gt;
&lt;p&gt;Their new "overturemaps" Python CLI utility makes it easy to quickly pull subsets of their data... but requires you to provide a bounding box to do so.&lt;/p&gt;
&lt;p&gt;Drew Breunig came up with this delightful recipe for fetching data using LLM and gpt-3.5-turbo to fill in those bounding boxes:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;overturemaps download --bbox=$(llm 'Give me a bounding box for Alameda, California expressed as only four numbers delineated by commas, with no spaces, longitude preceding latitude.') -f geojsonseq --type=place | geojson-to-sqlite alameda.db places - --nl --pk=id&lt;/code&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/dbreunig/status/1781133877320523792"&gt;@dbreunig&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cli"&gt;cli&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm"&gt;llm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/drew-breunig"&gt;drew-breunig&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/overture"&gt;overture&lt;/a&gt;&lt;/p&gt;



</summary><category term="cli"/><category term="geospatial"/><category term="geojson"/><category term="llm"/><category term="drew-breunig"/><category term="overture"/></entry><entry><title>mapshaper.org</title><link href="https://simonwillison.net/2024/Mar/23/mapshaperorg/#atom-tag" rel="alternate"/><published>2024-03-23T03:44:22+00:00</published><updated>2024-03-23T03:44:22+00:00</updated><id>https://simonwillison.net/2024/Mar/23/mapshaperorg/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://mapshaper.org/"&gt;mapshaper.org&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
It turns out the mapshaper CLI tool for manipulating geospatial data—including converting shapefiles to GeoJSON and back again—also has a web UI that runs the conversions entirely in your browser. If you need to convert between those (and other) formats it’s hard to imagine a more convenient option.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/adamrpearce/status/1771378836128854097"&gt;@adamrpearce&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cli"&gt;cli&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shapefiles"&gt;shapefiles&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;&lt;/p&gt;



</summary><category term="cli"/><category term="geospatial"/><category term="javascript"/><category term="shapefiles"/><category term="geojson"/></entry><entry><title>Claude and ChatGPT for ad-hoc sidequests</title><link href="https://simonwillison.net/2024/Mar/22/claude-and-chatgpt-case-study/#atom-tag" rel="alternate"/><published>2024-03-22T19:44:12+00:00</published><updated>2024-03-22T19:44:12+00:00</updated><id>https://simonwillison.net/2024/Mar/22/claude-and-chatgpt-case-study/#atom-tag</id><summary type="html">
    &lt;p&gt;Here is a short, illustrative example of one of the ways in which I use Claude and ChatGPT on a daily basis.&lt;/p&gt;
&lt;p&gt;I recently learned that the &lt;a href="https://en.wikipedia.org/wiki/Adirondack_Park"&gt;Adirondack Park&lt;/a&gt; is the single largest park in the contiguous United States, taking up a fifth of the state of New York.&lt;/p&gt;
&lt;p&gt;Naturally, my first thought was that it would be neat to have a GeoJSON file representing the boundary of the park.&lt;/p&gt;
&lt;p&gt;A quick search landed me on the &lt;a href="https://apa.ny.gov/gis/ApaData.html"&gt;Adirondack Park Agency GIS data page&lt;/a&gt;, which offered me a shapefile of the "Outer boundary of the New York State Adirondack Park as described in Section 9-0101 of the New York Environmental Conservation Law". Sounds good!&lt;/p&gt;
&lt;p&gt;I knew there were tools for converting shapefiles to GeoJSON, but I couldn't remember what they were. Since I had a terminal window open already, I typed the following:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre style="white-space: pre-wrap"&gt;llm -m opus -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;give me options on macOS for CLI tools to turn a shapefile into GeoJSON&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here I am using my &lt;a href="https://llm.datasette.io/"&gt;LLM tool&lt;/a&gt; (and &lt;a href="https://github.com/simonw/llm-claude-3"&gt;llm-claude-3&lt;/a&gt; plugin) to run a prompt through the new &lt;a href="https://www.anthropic.com/news/claude-3-family"&gt;Claude 3 Opus&lt;/a&gt;, my current favorite language model.&lt;/p&gt;
&lt;p&gt;It &lt;a href="https://gist.github.com/simonw/331918e46f33e27e997afb4e7c62fc74"&gt;replied with a couple of options&lt;/a&gt;, but the first was this:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;ogr2ogr -f GeoJSON output.geojson input.shp&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So I ran that against the shapefile, and then pasted &lt;a href="https://gist.github.com/simonw/c941f3454cdec7e10f500dc5a752b614"&gt;the resulting GeoJSON&lt;/a&gt; into &lt;a href="https://geojson.io/"&gt;geojson.io&lt;/a&gt; to check if it worked... and nothing displayed. Then I looked at the GeoJSON and spotted this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;"coordinates": [ [ -8358911.527799999341369, 5379193.197800002992153 ] ...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That didn't look right. Those co-ordinates aren't the correct scale for latitude and longitude values.&lt;/p&gt;
&lt;p&gt;So I sent a follow-up prompt to the model (the &lt;code&gt;-c&lt;/code&gt; option means "continue previous conversation"):&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre style="white-space: pre-wrap"&gt;llm -c &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;i tried using ogr2ogr but it gave me back GeoJSON with a weird coordinate system that was not lat/lon that i am used to&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It suggested this new command:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;ogr2ogr -f GeoJSON -t_srs EPSG:4326 output.geojson input.shp&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time &lt;a href="https://gist.github.com/simonw/6c4cf102a8ea532dc365c2773f0eb6ea"&gt;it worked&lt;/a&gt;! The shapefile has now been converted to GeoJSON.&lt;/p&gt;
&lt;p&gt;Time elapsed so far: 2.5 minutes (I can tell from &lt;a href="https://llm.datasette.io/en/stable/logging.html"&gt;my LLM logs&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I pasted it into &lt;a href="https://datasette.io/"&gt;Datasette&lt;/a&gt; (with &lt;a href="https://github.com/datasette/datasette-paste"&gt;datasette-paste&lt;/a&gt; and &lt;a href="https://datasette.io/plugins/datasette-leaflet-geojson"&gt;datasette-leaflet-geojson&lt;/a&gt;) to take a look at it more closely, and got this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/datasette-adirondack-boundaries.jpg" alt="A Datasette table with 106 rows. The first two are shown - both have properties and a geometry, and the geometry is a single line on a map. The first one has a ECL_Text of thence southerly along the westerly line of lots 223, 241, 259, 276, 293, 309, 325 and 340 to the southwesterly corner of lot number 340 in the Brantingham Tract and the second has thence westerly along the northern line of lots 204 and 203 to the midpoint of the northern line of lot 203" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;That's not a single polygon! That's 106 line segments... and they are fascinating. Look at those descriptions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;thence westerly along the northern line of lots 204 and 203 to the midpoint of the northern line of lot 203&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is utterly delightful. The shapefile description did say "as described in Section 9-0101 of the New York Environmental Conservation Law", so I guess this is how you write geographically boundaries into law!&lt;/p&gt;
&lt;p&gt;But it's not what I wanted. I want a single polygon of the whole park, not 106 separate lines.&lt;/p&gt;
&lt;p&gt;I decided to switch models. ChatGPT has access to Code Interpreter, and I happen to know that Code Interpreter is quite effective at processing GeoJSON.&lt;/p&gt;
&lt;p&gt;I opened a new ChatGPT (with GPT-4) browser tab, uploaded my GeoJSON file and prompted it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This GeoJSON file is full of line segments. Use them to create me a single shape that is a Polygon&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-1.jpg" alt="ChatGPT screenshot - it shows some Python code with a result of &amp;lt;shapely.geometry.polygon.Polygon at 0x7eba83f9fca0 /&amp;gt;, then says: I've created a polygon from the line segments in the GeoJSON file. You can now use this polygon for further analysis or visualization. If you have specific requirements for the polygon or need it in a particular format, please let me know! ​​" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;OK, so it wrote some Python code and ran it. But did it work?&lt;/p&gt;
&lt;p&gt;I happen to know that Code Interpreter can save files to disk and provide links to download them, so I told it to do that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Save it to a GeoJSON file for me to download&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-2.jpg" alt="ChatGPT screenshot - this time it writes more Python code to define a GeoJSON polygon, then saves that to a file called /mnt/data/polygon.geojson and gives me a link to download it.​​" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;I pasted &lt;a href="https://gist.github.com/simonw/c1002dbf5249de7addd0b65cb774d3e9"&gt;that&lt;/a&gt; into &lt;a href="https://geojson.io/"&gt;geojson.io&lt;/a&gt;, and it was clearly wrong:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-bad-map.jpg" alt="geojson.io screenshot - a triangle shape sits on top of an area of upstate New York, clearly not in the shape of the park" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;So I told it to try again. I didn't think very hard about this prompt, I basically went with a version of "do better":&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;that doesn't look right to me, check that it has all of the lines in it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-3.jpg" alt="ChatGPT screenshot - it writes more Python code and outputs a link to complete_polygon.geojson​​" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;It gave me a new file, optimistically named &lt;code&gt;complete_polygon.geojson&lt;/code&gt;. Here's what that one looked like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-almost.jpg" alt="ChatGPT screenshot - it writes more Python code and outputs a link to complete_polygon.geojson​​" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;This is getting a lot closer! Note how the right hand boundary of the park looks correct, but the rest of the image is scrambled.&lt;/p&gt;
&lt;p&gt;I had a hunch about the fix. I pasted in a screenshot of where we were so far and added my hunch about the solution:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That almost works but you need to sort the line segments first, it looked like this:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Honestly, pasting in the screenshot probably wasn't necessary here, but it amused me.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-4.jpg" alt="That almost works but you need to sort the line segments first, it looked like this: an a screenshot of a map" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;... and ChatGPT churned away again ...&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-chatgpt-5.jpg" alt="More Python code - link to the full transcript is below" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/simonw/b9e4325b76e4a3813ff5482aa278c342"&gt;sorted_polygon.geojson&lt;/a&gt; is spot on! Here's what it looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/shapely-good-map.jpg" alt="A shaded polygon showing the exact shape of the boundary of Adirondack Park, overlayed on a map of the area" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Total time spent in ChatGPT: 3 minutes and 35 seconds. Plus 2.5 minutes with Claude 3 earlier, so an overall total of just over 6 minutes.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://gist.github.com/simonw/0343cdd3568bbe28cad15d1097b1b1c7"&gt;the full Claude transcript&lt;/a&gt; and the &lt;a href="https://gist.github.com/simonw/3eb845823c5ad4c48d2b4eb7586f1533"&gt;full transcript from ChatGPT&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="not-notable"&gt;This isn't notable&lt;/h4&gt;
&lt;p&gt;The most notable thing about this example is how completely &lt;em&gt;not&lt;/em&gt; notable it is.&lt;/p&gt;
&lt;p&gt;I get results like this from these tools several times a day. I'm not at all surprised that this worked, in fact, I would've been mildly surprised if it had not.&lt;/p&gt;
&lt;p&gt;Could I have done this without LLM assistance? Yes, but not nearly as quickly. And this was not a task on my critical path for the day - it was a sidequest at best and honestly more of a distraction.&lt;/p&gt;
&lt;p&gt;So, without LLM tools, I would likely have given this one up at the first hurdle.&lt;/p&gt;
&lt;p&gt;A year ago I wrote about how &lt;a href="https://simonwillison.net/2023/Mar/27/ai-enhanced-development/"&gt;AI-enhanced development makes me more ambitious with my projects&lt;/a&gt;. They are now so firmly baked into my daily work that they influence not just side projects but tiny sidequests like this one as well.&lt;/p&gt;
&lt;h4 id="not-simple"&gt;This certainly wasn't simple&lt;/h4&gt;
&lt;p&gt;Something else I like about this example is that it illustrates quite how much depth there is to getting great results out of these systems.&lt;/p&gt;
&lt;p&gt;In those few minutes I used two different interfaces to call two different models. I sent multiple follow-up prompts. I triggered Code Interpreter, took advantage of GPT-4 Vision and mixed in external tools like &lt;a href="https://geojson.io/"&gt;geojson.io&lt;/a&gt; and Datasette as well.&lt;/p&gt;
&lt;p&gt;I leaned a lot on my existing knowledge and experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I knew that tools existed for commandline processing of shapefiles and GeoJSON&lt;/li&gt;
&lt;li&gt;I instinctively knew that Claude 3 Opus was likely to correctly answer my initial prompt&lt;/li&gt;
&lt;li&gt;I knew the capabilities of Code Interpreter, including that it has libraries that can process geometries, what to say to get it to kick into action and how to get it to give me files to download&lt;/li&gt;
&lt;li&gt;My limited GIS knowledge was strong enough to spot a likely coordinate system problem, and I guessed the fix for the jumbled lines&lt;/li&gt;
&lt;li&gt;My prompting intuition is developed to the point that I didn't have to think very hard about what to say to get the best results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have the right combination of domain knowledge and hard-won experience driving LLMs, you can &lt;em&gt;fly&lt;/em&gt; with these things.&lt;/p&gt;
&lt;h4 id="a-bit-trivial"&gt;Isn't this a bit trivial?&lt;/h4&gt;
&lt;p&gt;Yes it is, and that's the point. This was a five minute sidequest. Writing about it here took ten times longer than the exercise itself.&lt;/p&gt;
&lt;p&gt;I take on LLM-assisted sidequests like this one dozens of times a week. Many of them are substantially larger and more useful. They are having a very material impact on my work: I can get more done and solve much more interesting problems, because I'm not wasting valuable cycles figuring out &lt;code&gt;ogr2ogr&lt;/code&gt; invocations or mucking around with polygon libraries.&lt;/p&gt;
&lt;p&gt;Not to mention that I find working this way &lt;em&gt;fun&lt;/em&gt;! It feels like science fiction every time I do it. Our AI-assisted future is here right now and I'm still finding it weird, fascinating and deeply entertaining.&lt;/p&gt;
&lt;h4 id="llms-are-useful"&gt;LLMs are useful&lt;/h4&gt;
&lt;p&gt;There are many legitimate criticisms of LLMs. The copyright issues involved in their training, their enormous power consumption and the risks of people trusting them when they shouldn't (considering both accuracy and bias) are three that I think about a lot.&lt;/p&gt;
&lt;p&gt;The one criticism I wont accept is that they aren't &lt;em&gt;useful&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;One of the greatest misconceptions concerning LLMs is the idea that they are easy to use. They really aren't: getting great results out of them requires a great deal of experience and hard-fought intuition, combined with deep domain knowledge of the problem you are applying them to.&lt;/p&gt;
&lt;p&gt;I use these things every day. They help me take on much more interesting and ambitious problems than I could otherwise. I would miss them terribly if they were no longer available to me.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shapefiles"&gt;shapefiles&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openai"&gt;openai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chatgpt"&gt;chatgpt&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anthropic"&gt;anthropic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/code-interpreter"&gt;code-interpreter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/leaflet"&gt;leaflet&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="geospatial"/><category term="shapefiles"/><category term="geojson"/><category term="ai"/><category term="openai"/><category term="generative-ai"/><category term="chatgpt"/><category term="llms"/><category term="ai-assisted-programming"/><category term="anthropic"/><category term="claude"/><category term="code-interpreter"/><category term="coding-agents"/><category term="leaflet"/></entry><entry><title>Geospatial SQL queries in SQLite using TG, sqlite-tg and datasette-sqlite-tg</title><link href="https://simonwillison.net/2023/Sep/25/tg-sqlite/#atom-tag" rel="alternate"/><published>2023-09-25T19:45:03+00:00</published><updated>2023-09-25T19:45:03+00:00</updated><id>https://simonwillison.net/2023/Sep/25/tg-sqlite/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://til.simonwillison.net/sqlite/sqlite-tg"&gt;Geospatial SQL queries in SQLite using TG, sqlite-tg and datasette-sqlite-tg&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Alex Garcia built sqlite-tg—a SQLite extension that uses the brand new TG geospatial library to provide a whole suite of custom SQL functions for working with geospatial data.&lt;/p&gt;

&lt;p&gt;Here are my notes on trying out his initial alpha releases. The extension already provides tools for converting between GeoJSON, WKT and WKB, plus the all important tg_intersects() function for testing if a polygon or point overlap each other.&lt;/p&gt;

&lt;p&gt;It’s pretty useful already. Without any geospatial indexing at all I was still able to get 700ms replies to a brute-force point-in-polygon query against 150MB of GeoJSON timezone boundaries stored as JSON text in a table.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/alex-garcia"&gt;alex-garcia&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tg"&gt;tg&lt;/a&gt;&lt;/p&gt;



</summary><category term="geospatial"/><category term="sqlite"/><category term="geojson"/><category term="datasette"/><category term="alex-garcia"/><category term="tg"/></entry><entry><title>TG: Polygon indexing</title><link href="https://simonwillison.net/2023/Sep/23/tg-polygon-indexing/#atom-tag" rel="alternate"/><published>2023-09-23T04:32:14+00:00</published><updated>2023-09-23T04:32:14+00:00</updated><id>https://simonwillison.net/2023/Sep/23/tg-polygon-indexing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/tidwall/tg/blob/v0.1.0/docs/POLYGON_INDEXING.md"&gt;TG: Polygon indexing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
TG is a brand new geospatial library by Josh Baker, author of the Tile38 in-memory spatial server (kind of a geospatial Redis). TG is written in pure C and delivered as a single C file, reminiscent of the SQLite amalgamation.&lt;/p&gt;

&lt;p&gt;TG looks really interesting. It implements almost the exact subset of geospatial functionality that I find most useful: point-in-polygon, intersect, WKT, WKB, and GeoJSON—all with no additional dependencies.&lt;/p&gt;

&lt;p&gt;The most interesting thing about it is the way it handles indexing. In this documentation Josh describes two approaches he uses to speeding up point-in-polygon and intersection using a novel approach that goes beyond the usual RTree implementation.&lt;/p&gt;

&lt;p&gt;I think this could make the basis of a really useful SQLite extension—a lighter-weight alternative to SpatiaLite.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=37619100"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/c"&gt;c&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/spatialite"&gt;spatialite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tg"&gt;tg&lt;/a&gt;&lt;/p&gt;



</summary><category term="c"/><category term="geospatial"/><category term="spatialite"/><category term="sqlite"/><category term="geojson"/><category term="tg"/></entry><entry><title>geoBoundaries</title><link href="https://simonwillison.net/2022/Mar/24/geoboundaries/#atom-tag" rel="alternate"/><published>2022-03-24T14:03:56+00:00</published><updated>2022-03-24T14:03:56+00:00</updated><id>https://simonwillison.net/2022/Mar/24/geoboundaries/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.geoboundaries.org/"&gt;geoBoundaries&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This looks useful: “The world’s largest open, free and research-ready database of political administrative boundaries.” Founded by the geoLab at William &amp;amp; Mary university, and released under a Creative Commons Attribution license that includes a requirement for a citation. File formats offered include shapefiles, GeoJSON and TopoJSON.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shapefiles"&gt;shapefiles&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;&lt;/p&gt;



</summary><category term="geospatial"/><category term="shapefiles"/><category term="geojson"/></entry><entry><title>Weeknotes: Velma, more Django SQL Dashboard</title><link href="https://simonwillison.net/2021/May/18/weeknotes-velma/#atom-tag" rel="alternate"/><published>2021-05-18T00:36:20+00:00</published><updated>2021-05-18T00:36:20+00:00</updated><id>https://simonwillison.net/2021/May/18/weeknotes-velma/#atom-tag</id><summary type="html">
    &lt;p&gt;Matching locations for Vaccinate The States, fun with GeoJSON and more improvements to Django SQL Dashboard.&lt;/p&gt;
&lt;h4&gt;Velma&lt;/h4&gt;
&lt;p&gt;I described &lt;a href="https://simonwillison.net/2021/Apr/26/vaccinate-the-states/"&gt;a few weeks ago&lt;/a&gt; part of the process we've been using to build &lt;a href="https://www.vaccinatethestates.com/"&gt;Vaccinate The States&lt;/a&gt; - a map of every COVID vaccine location in the USA (now at just over 70,000 markers and counting).&lt;/p&gt;
&lt;p&gt;Short version: we have scrapers and data ingesters for a whole bunch of different sources (see the &lt;a href="https://github.com/CAVaccineInventory/vaccine-feed-ingest"&gt;vaccine-feed-ingest&lt;/a&gt; repository).&lt;/p&gt;
&lt;p&gt;Part of the challenge here is how to deal with duplicates - with multiple sources of data, chances are high that the same location will show up in more than on of our input feeds.&lt;/p&gt;
&lt;p&gt;So in the past weeks we've been building a new tool code-named Velma to help handle this. It shows our volunteers a freshly scraped location and asks them to either match it to one of our existing locations (based on automated suggestions) or use it to create a brand new location in our database.&lt;/p&gt;

&lt;p&gt;&lt;img style="max-width: 100%" src="https://static.simonwillison.net/static/2021/velma.png" alt="An interface showing a location and a potential match" /&gt;&lt;/p&gt;

&lt;p&gt;I've been working exclusively on the backend APIs for Velma: APIs that return new scraped data and accept and process the human matching decisions from our volunteers.&lt;/p&gt;
&lt;p&gt;This week we've been expanding Velma to also cover merging potential duplicate locations within our existing corpus, so I've been building out the APIs for that effort as well.&lt;/p&gt;
&lt;p&gt;I've also been working on new export code for making our entire set of locations available to partners and interested outside developers. We hope to launch that fully in the next few days.&lt;/p&gt;
&lt;h4&gt;geojson-to-sqlite&lt;/h4&gt;
&lt;p&gt;One of the export formats we are working with is GeoJSON. I have a tool called &lt;a href="https://datasette.io/tools/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt; which I released last year: this week I released an &lt;a href="https://github.com/simonw/geojson-to-sqlite/releases/tag/0.3"&gt;updated version&lt;/a&gt; with the ability to create SpatiaLite indexes and a &lt;code&gt;--nl&lt;/code&gt; option for consuming newline-delimited GeoJSON, &lt;a href="https://github.com/simonw/geojson-to-sqlite/pull/13"&gt;contributed by Chris Amico&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've also been experimenting with SpatiaLite's KNN mechanism using &lt;code&gt;geojson-to-sqlite&lt;/code&gt; to load in data - here's &lt;a href="https://til.simonwillison.net/spatialite/knn"&gt;a TIL&lt;/a&gt; showing how to use those tools together.&lt;/p&gt;
&lt;h4&gt;Django SQL Dashboard&lt;/h4&gt;
&lt;p&gt;I &lt;a href="https://simonwillison.net/2021/May/10/django-sql-dashboard/"&gt;released the first non-alpha version&lt;/a&gt; of this last week and it's started to gain some traction: I've heard from a few people who are trying it out on their projects and it seems to work, so that's good!&lt;/p&gt;
&lt;p&gt;I released &lt;a href="https://github.com/simonw/django-sql-dashboard/releases/tag/0.14"&gt;version 0.14&lt;/a&gt; yesterday with a bunch of fixes based on feedback from users, plus a security fix that closes a hole where users without the &lt;code&gt;execute_sql&lt;/code&gt; permission but with access to the Django Admin could modify the SQL in saved dashboards and hence execute their own custom queries.&lt;/p&gt;
&lt;p&gt;I also made a bunch of improvements to the documentation, including adding screenshots and demo links &lt;a href="https://django-sql-dashboard.datasette.io/en/latest/widgets.html"&gt;to the widgets page&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;TIL this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/wikipedia/page-stats-api"&gt;The Wikipedia page stats API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/vega/bar-chart-ordering"&gt;Vega-Lite bar charts in the same order as the data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/django/enabling-gin-index"&gt;Enabling a gin index for faster LIKE queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/spatialite/knn"&gt;KNN queries with SpatiaLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://til.simonwillison.net/django/migration-using-cte"&gt;Django data migration using a PostgreSQL CTE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Releases this week&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/geojson-to-sqlite/releases/tag/0.3"&gt;0.3&lt;/a&gt; - (&lt;a href="https://github.com/simonw/geojson-to-sqlite/releases"&gt;6 releases total&lt;/a&gt;) - 2021-05-17
&lt;br /&gt;CLI tool for converting GeoJSON files to SQLite (with SpatiaLite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/simonw/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://github.com/simonw/django-sql-dashboard/releases/tag/0.14"&gt;0.14&lt;/a&gt; - (&lt;a href="https://github.com/simonw/django-sql-dashboard/releases"&gt;28 releases total&lt;/a&gt;) - 2021-05-16
&lt;br /&gt;Django app for building dashboards using raw SQL queries&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vaccinate-ca"&gt;vaccinate-ca&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-sql-dashboard"&gt;django-sql-dashboard&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="geojson"/><category term="weeknotes"/><category term="vaccinate-ca"/><category term="django-sql-dashboard"/></entry><entry><title>country-coder</title><link href="https://simonwillison.net/2021/Apr/18/country-coder/#atom-tag" rel="alternate"/><published>2021-04-18T19:37:24+00:00</published><updated>2021-04-18T19:37:24+00:00</updated><id>https://simonwillison.net/2021/Apr/18/country-coder/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/ideditor/country-coder"&gt;country-coder&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Given a latitude and longitude, how can you tell what country that point sits within? One way is to do a point-in-polygon lookup against a set of country polygons, but this can be tricky: some countries such as New Zealand have extremely complex outlines, even though for this use-case you don’t need the exact shape of the coastline. country-coder solves this with a custom designed 595KB GeoJSON file with detailed land borders but loosely defined ocean borders. It also comes with a wrapper JavaScript library that provides an API for resolving points, plus useful properties on each country with details like telepohen calling codes and emoji flags.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/bhousel/status/1383398696117673985"&gt;@bhousel&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;&lt;/p&gt;



</summary><category term="geospatial"/><category term="geojson"/></entry><entry><title>Things I learned about shapefiles building shapefile-to-sqlite</title><link href="https://simonwillison.net/2020/Feb/19/shapefile-to-sqlite/#atom-tag" rel="alternate"/><published>2020-02-19T05:25:58+00:00</published><updated>2020-02-19T05:25:58+00:00</updated><id>https://simonwillison.net/2020/Feb/19/shapefile-to-sqlite/#atom-tag</id><summary type="html">
    &lt;p&gt;The latest in my series of &lt;a href="https://datasette.readthedocs.io/en/latest/ecosystem.html#tools-for-creating-sqlite-databases"&gt;x-to-sqlite tools&lt;/a&gt; is &lt;a href="https://github.com/simonw/shapefile-to-sqlite"&gt;shapefile-to-sqlite&lt;/a&gt;. I learned a whole bunch of things about the ESRI shapefile format while building it.&lt;/p&gt;
&lt;p&gt;Governments really love ESRI shapefiles. There is a huge amount of interesting geospatial data made available in the format - &lt;a href="https://catalog.data.gov/dataset?res_format=SHP"&gt;4,614 on Data.gov&lt;/a&gt;!&lt;/p&gt;
&lt;h3 id="shapefile-to-sqlite"&gt;shapefile-to-sqlite&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;shapefile-to-sqlite&lt;/code&gt; loads the data from these files into a SQLite database, turning geometry properties into database columns and the geometry itself into a blob of GeoJSON. Let&amp;#39;s try it out on a shapefile containing the &lt;a href="https://catalog.data.gov/dataset/national-parks"&gt;boundaries of US national parks&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ wget http:&lt;span class="hljs-comment"&gt;//nrdata.nps.gov/programs/lands/nps_boundary.zip&lt;/span&gt;
...
Saving to: ‘nps_boundary.zip’
nps_boundary.zip                           &lt;span class="hljs-number"&gt;100&lt;/span&gt;%[=====================================================================================&amp;gt;]  &lt;span class="hljs-number"&gt;12.61&lt;/span&gt;M   &lt;span class="hljs-number"&gt;705&lt;/span&gt;KB/s    &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; &lt;span class="hljs-number"&gt;22&lt;/span&gt;s     
&lt;span class="hljs-number"&gt;2020&lt;/span&gt;&lt;span class="hljs-number"&gt;-02&lt;/span&gt;&lt;span class="hljs-number"&gt;-18&lt;/span&gt; &lt;span class="hljs-number"&gt;19&lt;/span&gt;:&lt;span class="hljs-number"&gt;59&lt;/span&gt;:&lt;span class="hljs-number"&gt;22&lt;/span&gt; (&lt;span class="hljs-number"&gt;597&lt;/span&gt; KB/s) - ‘nps_boundary.zip’ saved [&lt;span class="hljs-number"&gt;13227561&lt;/span&gt;/&lt;span class="hljs-number"&gt;13227561&lt;/span&gt;]

$ unzip nps_boundary.zip 
Archive:  nps_boundary.zip
inflating: temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.xml  
inflating: temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.dbf  
inflating: temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.prj  
inflating: temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.shp  
inflating: temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.shx

$ shapefile-to-sqlite nps.db temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.shp
temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.shp
[####################################]  &lt;span class="hljs-number"&gt;100&lt;/span&gt;%

$ datasette nps.db
Serve! files=(&lt;span class="hljs-string"&gt;'nps.db'&lt;/span&gt;,) (immutables=()) on port &lt;span class="hljs-number"&gt;8003&lt;/span&gt;
INFO:     Started server process [&lt;span class="hljs-number"&gt;33534&lt;/span&gt;]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http:&lt;span class="hljs-comment"&gt;//127.0.0.1:8001 (Press CTRL+C to quit)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I recommend installing the &lt;a href="https://github.com/simonw/datasette-leaflet-geojson"&gt;datasette-leaflet-geojson&lt;/a&gt; plugin, which will turn any column containing GeoJSON into a Leaflet map.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2020/nps-boundaries.jpg" alt="Screenshot of National Parks in Datasette" style="max-width: 100%" /&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#39;ve installed SpatiaLite (&lt;a href="https://datasette.readthedocs.io/en/latest/spatialite.html#installation"&gt;installation instructions here&lt;/a&gt;) you can use the &lt;code&gt;--spatialite&lt;/code&gt; option to instead store the geometry in a SpatiaLite column, unlocking &lt;a href="http://www.gaia-gis.it/gaia-sins/spatialite-sql-latest.html"&gt;a bewildering array&lt;/a&gt; of SQL geometry functions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ shapefile-to-sqlite nps.db temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.shp --spatialite --table=nps-spatialite
temp/Current_Shapes/Data_Store/&lt;span class="hljs-number"&gt;06&lt;/span&gt;&lt;span class="hljs-number"&gt;-06&lt;/span&gt;&lt;span class="hljs-number"&gt;-12&lt;/span&gt;_Posting/nps_boundary.shp
[##################################--]   &lt;span class="hljs-number"&gt;94&lt;/span&gt;%  &lt;span class="hljs-number"&gt;00&lt;/span&gt;:&lt;span class="hljs-number"&gt;00&lt;/span&gt;:&lt;span class="hljs-number"&gt;00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I deployed a copy of the resulting database using Cloud Run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ datasette publish cloudrun nps.db \
    -&lt;span class="ruby"&gt;-service national-parks \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-title &lt;span class="hljs-string"&gt;"National Parks"&lt;/span&gt; \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-source_url=&lt;span class="hljs-string"&gt;"https://catalog.data.gov/dataset/national-parks"&lt;/span&gt; \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-source=&lt;span class="hljs-string"&gt;"data.gov"&lt;/span&gt; \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-spatialite \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-install=datasette-leaflet-geojson \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-install=datasette-render-binary \
&lt;/span&gt;    -&lt;span class="ruby"&gt;-extra-options=&lt;span class="hljs-string"&gt;"--config max_returned_rows:5"&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I used &lt;code&gt;max_returned_rows:5&lt;/code&gt; there because these geometrries are pretty big - without it a page with 100 rows on it can return over 90MB of HTML!&lt;/p&gt;
&lt;p&gt;You can browse the GeoJSON version of the table &lt;a href="https://national-parks-j7hipcg4aq-uc.a.run.app/nps/nps_boundary"&gt;here&lt;/a&gt; and the SpatiaLite version &lt;a href="https://national-parks-j7hipcg4aq-uc.a.run.app/nps/nps-spatialite"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The SpatiaLite version defaults to rendering each geometry as an ugly binary blob. You can convert them to GeoJSON for compatibility with &lt;code&gt;datasette-leaflet-geojson&lt;/code&gt; using the SpatiaLite &lt;code&gt;AsGeoJSON()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;select&lt;/span&gt; &lt;span class="hljs-keyword"&gt;id&lt;/span&gt;, UNIT_NAME, AsGeoJSON(geometry)
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; [nps-spatialite]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;#39;s &lt;a href="https://national-parks-j7hipcg4aq-uc.a.run.app/nps?sql=select+id%2C+UNIT_NAME%2C+AsGeoJSON%28geometry%29+from+%5Bnps-spatialite%5D"&gt;the result&lt;/a&gt; of that query running against the demo.&lt;/p&gt;
&lt;h3 id="understanding-shapefiles"&gt;Understanding shapefiles&lt;/h3&gt;
&lt;p&gt;The most confusing thing about shapefiles is that they aren&amp;#39;t a single file. A shapefile comes as a minimum of three files: &lt;code&gt;foo.shp&lt;/code&gt; containing geometries, &lt;code&gt;foo.shx&lt;/code&gt; containing an index into those geometries (really more of an implementation detail) and &lt;code&gt;foo.dbf&lt;/code&gt; contains key/value properties for each geometry.&lt;/p&gt;
&lt;p&gt;They often come bundled with other files too. &lt;code&gt;foo.prj&lt;/code&gt; is a WKT projection for the data for example. Wikipedia lists &lt;a href="https://en.wikipedia.org/wiki/Shapefile#Overview"&gt;a whole bunch&lt;/a&gt; of other possibilities.&lt;/p&gt;
&lt;p&gt;As a result, shapefiles are usually distributed as a zip file. Some shapefile libraries can even read directly from a zip.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://tools.ietf.org/html/rfc7946"&gt;GeoJSON format&lt;/a&gt; was designed as a modern alternative to shapefiles, so understanding GeoJSON really helps in understanding shapefiles. In particular the GeoJSON geometry types: Point, LineString, MultiLineString, Polygon and MultiPolygon match how shapefile geometries work.&lt;/p&gt;
&lt;p&gt;An important detail in shapefiles is that data in the &lt;code&gt;.shp&lt;/code&gt; and &lt;code&gt;.dbf&lt;/code&gt; files is matched by array index - so the first geometry can be considered as having ID=0, the second ID=1 and so on.&lt;/p&gt;
&lt;p&gt;You can read the properties from the &lt;code&gt;.dbf&lt;/code&gt; file using the &lt;a href="https://dbfread.readthedocs.io/en/latest/"&gt;dbfread&lt;/a&gt; Python module like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ipython
&lt;span class="hljs-keyword"&gt;In&lt;/span&gt; [&lt;span class="hljs-number"&gt;1&lt;/span&gt;]: import dbfread
&lt;span class="hljs-keyword"&gt;In&lt;/span&gt; [&lt;span class="hljs-number"&gt;2&lt;/span&gt;]: db = dbfread.DBF(&lt;span class="hljs-string"&gt;"temp/Current_Shapes/Data_Store/06-06-12_Posting/nps_boundary.dbf"&lt;/span&gt;)
&lt;span class="hljs-keyword"&gt;In&lt;/span&gt; [&lt;span class="hljs-number"&gt;3&lt;/span&gt;]: next(iter(db))
&lt;span class="hljs-keyword"&gt;Out&lt;/span&gt;[&lt;span class="hljs-number"&gt;3&lt;/span&gt;]: 
OrderedDict([(&lt;span class="hljs-string"&gt;'UNIT_TYPE'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'Park'&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'STATE'&lt;/span&gt;, &lt;span class="hljs-string"&gt;''&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'REGION'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'NC'&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'UNIT_CODE'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'NACC'&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'UNIT_NAME'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'West Potomac Park'&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'DATE_EDIT'&lt;/span&gt;, &lt;span class="hljs-keyword"&gt;None&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'GIS_NOTES'&lt;/span&gt;, &lt;span class="hljs-string"&gt;''&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'CREATED_BY'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'Legacy'&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'METADATA'&lt;/span&gt;, &lt;span class="hljs-string"&gt;''&lt;/span&gt;),
            (&lt;span class="hljs-string"&gt;'PARKNAME'&lt;/span&gt;, &lt;span class="hljs-string"&gt;''&lt;/span&gt;)])
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="reading-shapefiles-in-python"&gt;Reading shapefiles in Python&lt;/h3&gt;
&lt;p&gt;I&amp;#39;m a big fan of the &lt;a href="https://shapely.readthedocs.io/"&gt;Shapely&lt;/a&gt; Python library, so I was delighted to see that Sean Gillies, creator of Shapely, also created a library for reading and writing shapefiles: &lt;a href="https://fiona.readthedocs.io/"&gt;Fiona&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://macwright.org/2012/10/31/gis-with-python-shapely-fiona.html"&gt;GIS with Python, Shapely, and Fiona&lt;/a&gt; by Tom MacWright was particularly useful for figuring this out. I like how he wrote that post in 2012 but added a note in 2017 that it&amp;#39;s still his recommended way of getting started with GIS in Python.&lt;/p&gt;
&lt;h3 id="projections"&gt;Projections&lt;/h3&gt;
&lt;p&gt;The trickiest part of working with any GIS data is always figuring out how to deal with &lt;a href="https://xkcd.com/977/"&gt;projections&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;GeoJSON &lt;a href="https://tools.ietf.org/html/rfc7946#section-4"&gt;attempts to standardize&lt;/a&gt; on WGS 84, otherwise known as the latitude/longitude model used by GPS. But... shapefiles frequently use something else. The &lt;a href="https://www.sccgov.org/sites/parks/Parks-Maps/Maps-Data/Pages/home.aspx"&gt;Santa Clara county parks&lt;/a&gt; shapefiles for example use &lt;a href="https://epsg.io/2227"&gt;EPSG:2227&lt;/a&gt;, also known as California zone 3.&lt;/p&gt;
&lt;p&gt;(Fun fact: ESPG stands for European Petroleum Survey Group, a now defunct oil industry group that today lives on only as a database of projected coordinate systems.)&lt;/p&gt;
&lt;p&gt;I spent &lt;a href="https://github.com/simonw/shapefile-to-sqlite/issues/6"&gt;quite a while&lt;/a&gt; thinking about how to best handle projections. In the end I decided that I&amp;#39;d follow GeoJSON&amp;#39;s lead and attempt to convert everything to WGS 84, but allow users to skip that behaviour using &lt;code&gt;--crs=keep&lt;/code&gt; or to specify an alternative projection to convert to with &lt;code&gt;--crs=epsg:2227&lt;/code&gt; or similar.&lt;/p&gt;
&lt;p&gt;SpatiaLite creates its geometry columns with a baked in SRID (a code which usually maps to the EPSG identifier). You can see which SRID was used for a specific geometry using the &lt;code&gt;srid()&lt;/code&gt; function:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://national-parks-j7hipcg4aq-uc.a.run.app/nps?sql=select+srid%28geometry%29+from+%22nps-spatialite%22+limit+1"&gt;select srid(geometry) from &amp;quot;nps-spatialite&amp;quot; limit 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SpatiaLite can also convert to another projection using the &lt;code&gt;Transform()&lt;/code&gt; function:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://national-parks-j7hipcg4aq-uc.a.run.app/nps?sql=select+%27%3A%27+%7C%7C+AsGeoJSON%28Transform%28geometry%2C+2227%29%29+from+%22nps-spatialite%22+limit+1"&gt;select &amp;#39;:&amp;#39; || AsGeoJSON(Transform(geometry, 2227)) from &amp;quot;nps-spatialite&amp;quot; limit 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(I&amp;#39;m using &lt;code&gt;&amp;#39;:&amp;#39; || AsGeoJSON(...)&lt;/code&gt; here to disable the &lt;code&gt;datasette-leaflet-geojson&lt;/code&gt; plugin, since it can&amp;#39;t correctly render data that has been transformed to a non-WGS-84 proection.)&lt;/p&gt;
&lt;h3 id="pulling-it-all-together"&gt;Pulling it all together&lt;/h3&gt;
&lt;p&gt;I now have two tools for imorting geospatial data into SQLite (or SpatiaLite) databases: &lt;a href="hhttps://github.com/simonw/shahpefile-to-sqlite"&gt;shapefile-to-sqlite&lt;/a&gt; and &lt;a href="https://github.com/simonw/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m excited about Datasette&amp;#39;s potential as a tool for GIS. I started exploring this back in 2017 when I used it to &lt;a href="https://simonwillison.net/2017/Dec/12/location-time-zone-api/"&gt;build a location to timezone API&lt;/a&gt; - but adding easy shapefile imports to the toolchain should unlock all kinds of interesting new geospatial projects.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/shapefiles"&gt;shapefiles&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/spatialite"&gt;spatialite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tom-macwright"&gt;tom-macwright&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/leaflet"&gt;leaflet&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="geospatial"/><category term="projects"/><category term="shapefiles"/><category term="spatialite"/><category term="sqlite"/><category term="geojson"/><category term="weeknotes"/><category term="tom-macwright"/><category term="leaflet"/></entry><entry><title>Weeknotes: Shaving yaks for Datasette Cloud</title><link href="https://simonwillison.net/2020/Feb/5/weeknotes-shaving-yaks/#atom-tag" rel="alternate"/><published>2020-02-05T07:34:02+00:00</published><updated>2020-02-05T07:34:02+00:00</updated><id>https://simonwillison.net/2020/Feb/5/weeknotes-shaving-yaks/#atom-tag</id><summary type="html">
    &lt;p&gt;I've been &lt;a href="https://en.wiktionary.org/wiki/yak_shaving"&gt;shaving a lot of yaks&lt;/a&gt;, but I'm finally ready to for other people to start kicking the tires on the MVP of Datasette Cloud.&lt;/p&gt;

&lt;p&gt;I've started by inviting a small group of people (from my fellowship program) in to start trying out this new hosted, team-oriented version of Datasette.&lt;/p&gt;

&lt;p&gt;Getting to this point has been a classic example of the last 10% of the project taking 90% of the time.&lt;/p&gt;

&lt;p&gt;Here's just one example. I need my users to be able to upload CSV files directly into Datasette, rather than making them rely on my &lt;a href="https://en.wiktionary.org/wiki/yak_shaving"&gt;growing collection of command-line tools&lt;/a&gt; for data ingestion.&lt;/p&gt;

&lt;p&gt;So I've been trying to knock the very-alpha version of my new &lt;a href="https://github.com/simonw/datasette-upload-csvs"&gt;datasette-upload-csvs&lt;/a&gt; plugin into good enough shape to be usable for this initial round of testing.&lt;/p&gt;

&lt;p&gt;But... that plugin needs to render templates. And Datasette plugins didn't have a pleasant way of rendering templates (if you discount &lt;a href="https://github.com/simonw/datasette-atom/commit/c0e3bd9556d7b31f253a8bf666d42205cd24f4fc#diff-acba9942430bc5e616410567296f92ffR87"&gt;horrifying stack-inpsection hacks&lt;/a&gt;). I've had &lt;a href="https://github.com/simonw/datasette/issues/577"&gt;an issue open about this&lt;/a&gt; since September. I &lt;a href="https://github.com/simonw/datasette/pull/664"&gt;finally closed it&lt;/a&gt; today, and shipped &lt;a href="https://datasette.readthedocs.io/en/stable/changelog.html#v0-35"&gt;Datasette 0.35&lt;/a&gt; to celebrate.&lt;/p&gt;

&lt;p&gt;I want users to only be able to access the Datasette instances for teams that they belong to. Since authentication is handled by &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies"&gt;datasette-auth-existing-cookies&lt;/a&gt; I needed it to grow &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies/issues/9"&gt;some concept of permissions&lt;/a&gt;. I ended up shipping versions 0.3, 0.4, 0.4.1, 0.5 and 0.5.1 just &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies/releases"&gt;in the past two days&lt;/a&gt;, but it finally does what I need it to do. Another thoroughly shaved yak.&lt;/p&gt;

&lt;p&gt;I ran into &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies/issues/8"&gt;an awkward ASGI scope issue&lt;/a&gt;, which I ended up figuring out using a new &lt;a href="https://github.com/simonw/datasette-debug-asgi"&gt;datasette-debug-asgi&lt;/a&gt; plugin based on my older &lt;a href="https://github.com/simonw/asgi-scope"&gt;asgi-scope&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;I've been doing a lot of tinkering with my Docker/Traefik environment too. I can now launch new containers from Python code triggered by a &lt;a href="https://docs.djangoproject.com/en/3.0/ref/contrib/admin/actions/"&gt;Django Admin action&lt;/a&gt;, which is pretty fun.&lt;/p&gt;

&lt;p&gt;My other fun project from this week was &lt;a href="https://github.com/simonw/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt;, a CLI tool for converting GeoJSON files into a SQLite (or optionally a &lt;a href="https://www.gaia-gis.it/fossil/libspatialite/index"&gt;SpatiaLite&lt;/a&gt;) database. Combined with &lt;a href="https://github.com/simonw/datasette-leaflet-geojson"&gt;datasette-leaflet-geojson&lt;/a&gt; this allows for some really fun geospatial nerdery. I'm looking forward to diving deeper into this set of Datasette use-cases in the near future.&lt;/p&gt;

&lt;p&gt;Scrappy weeknotes this week, but I've decided that it's better to keep to the habit and post something untidy than to delay posting and break &lt;a href="https://simonwillison.net/tags/weeknotes/"&gt;my streak&lt;/a&gt;.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-cloud"&gt;datasette-cloud&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="projects"/><category term="geojson"/><category term="datasette"/><category term="weeknotes"/><category term="datasette-cloud"/></entry><entry><title>geojson-to-sqlite</title><link href="https://simonwillison.net/2020/Jan/31/geojson-sqlite/#atom-tag" rel="alternate"/><published>2020-01-31T06:40:53+00:00</published><updated>2020-01-31T06:40:53+00:00</updated><id>https://simonwillison.net/2020/Jan/31/geojson-sqlite/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/geojson-to-sqlite"&gt;geojson-to-sqlite&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I just put out the first release of geojson-to-sqlite—a CLI tool that can convert GeoJSON files (consisting of a Feature or a set of features in a FeatureCollection) into a table in a SQLite database. If you use the --spatialite option it will initalize the table with SpatiaLite and store the geometries in a spacially indexed geometry field—without that option it stores them as GeoJSON.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/simonw/status/1223133971082403841"&gt;@simonw&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cli"&gt;cli&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/spatialite"&gt;spatialite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;&lt;/p&gt;



</summary><category term="cli"/><category term="geospatial"/><category term="projects"/><category term="spatialite"/><category term="sqlite"/><category term="geojson"/></entry><entry><title>togeojson</title><link href="https://simonwillison.net/2019/Jan/18/togeojson/#atom-tag" rel="alternate"/><published>2019-01-18T23:50:00+00:00</published><updated>2019-01-18T23:50:00+00:00</updated><id>https://simonwillison.net/2019/Jan/18/togeojson/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/tmcw/togeojson"&gt;togeojson&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Handy JavaScript library and command-mine tool for converting KML and GPX to GeoJSON, by Tom MacWright

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/tmcw/status/1086407002773803008"&gt;@tmcw&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/kml"&gt;kml&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tom-macwright"&gt;tom-macwright&lt;/a&gt;&lt;/p&gt;



</summary><category term="geospatial"/><category term="kml"/><category term="geojson"/><category term="tom-macwright"/></entry><entry><title>simonepri/geo-maps</title><link href="https://simonwillison.net/2017/Nov/21/geo-maps/#atom-tag" rel="alternate"/><published>2017-11-21T16:06:38+00:00</published><updated>2017-11-21T16:06:38+00:00</updated><id>https://simonwillison.net/2017/Nov/21/geo-maps/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonepri/geo-maps"&gt;simonepri/geo-maps&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat project which publishes GeoJSON maps of the world automatically derived from OpenStreetMap. Three variants are available: country political maritime boundaries, country political coastline boundaries and a general outline of the world’s land territories.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geospatial"&gt;geospatial&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openstreetmap"&gt;openstreetmap&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;&lt;/p&gt;



</summary><category term="geospatial"/><category term="openstreetmap"/><category term="geojson"/></entry><entry><title>Polymaps</title><link href="https://simonwillison.net/2010/Aug/20/polymaps/#atom-tag" rel="alternate"/><published>2010-08-20T18:46:00+00:00</published><updated>2010-08-20T18:46:00+00:00</updated><id>https://simonwillison.net/2010/Aug/20/polymaps/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://polymaps.org/"&gt;Polymaps&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Absurdly classy: “a JavaScript library for image- and vector-tiled maps using SVG”. It can pull in image tiles from sources such as OpenStreetMap, then overlay SVG paths specified using GeoJSON. The demos make use of GeoJSON tiles for US states and counties hosted on AppEngine. The library is developed by Stamen and SimpleGeo, and released under a BSD license. SVG support in the browser is required.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/google-app-engine"&gt;google-app-engine&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mapping"&gt;mapping&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openstreetmap"&gt;openstreetmap&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/stamen-design"&gt;stamen-design&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/svg"&gt;svg&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/geojson"&gt;geojson&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/polymaps"&gt;polymaps&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/simplegeo"&gt;simplegeo&lt;/a&gt;&lt;/p&gt;



</summary><category term="google-app-engine"/><category term="javascript"/><category term="mapping"/><category term="openstreetmap"/><category term="stamen-design"/><category term="svg"/><category term="recovered"/><category term="geojson"/><category term="polymaps"/><category term="simplegeo"/></entry></feed>