When Spatial Search Goes Wrong


I want to find some great pizza near my city of Salisbury, Maryland. I do a search and put in 60 miles as my radius. I get a bunch of results:Salisbury is on a peninsula, separated from western Maryland by the Chespeake Bay, with only one way to cross. Take a look at the pizzas on the left. They are across the Bay, and take hours to drive to.

Instead of a simple buffer, we can create an isochrone. An isochrone is a polygon of everywhere one can travel to within a certain time. An isochrone of 60 minutes for my city looks like this:

Now, I can do a query to get all the pizza joints that are contained within that isochrone polygon:

Good! These are more meaningful results.

There are multiple ways to generate isochrones. The free and open source way is to use PostGIS with pgrouting. For this example, I used the Mapbox isochrone API which is fast and easy.

When your product requires these kind of search queries, think about the application before doing a simple buffer.

Often users don't want results that are geographically close, they want results that are chronologically close.

USGS Earthquake API


Link to Observable notebook

Planet API Explorer


Link to Observable notebook

Basemap Selector UIs for Mobile Apps


An ongoing collection of basemap selector UIs for mobile:

Apple Maps (iOS 11.2, Jan 2018)

Tap the "i" button in the upper right to bring up a bottom overlay with a segmented control to select basemap. Dimiss by tapping "x" or tapping the map.

Apple Maps (iPad, Jan 2018)

Tap the "i" button in the upper right to bring up a centered overlay with a segmented control to select basemap. Dimiss by tapping "x".

Google Maps (iOS 11.2, Jan 2018)

Tap the layers button in the upper right to bring up a bottom overlay. Tap a thumbnail of the map to select it. Currently selected basemap is highlighted with a blue border. Dismiss by tapping "x", tapping the map, or swiping down.

Google Maps (Android 6 on Nexus 7, Jan 2018)

Tap the layers button in the upper right to bring up an overlay in the same location. Tap a thumbnail of the map to select it. Currently selected basemap is highlighted with a blue border. Dismiss by tapping "x", or tapping the map.

Google Maps (Safari on iOS, Jan 2018)

Tap the hamburger menu button in the upper left. Tap Satellite to toggle Satellite basemap. When Satellite is selected, it is highlighted in blue. Dismiss by swiping away or tapping on map.

Explorer for ArcGIS (iOS 11.2, Jan 2018)

Tap the three dots "more" button in the upper right navigation bar to bring up a bottom overlay. Tap on "Basemap" to open a second bottom overlay that contains a list of basemap thumbnails. The selected basemap is highlighted in green. Dismiss by tapping "x" or swiping down.

Fulcrum (iOS 11.2, Jan 2018)

Tap the layers button in the bottom right to bring up a full screen modal. Currently selected basemap is highlighted with a checkmark. Tap a basemap name to select. Dismissing by tapping "Close".

Build a cross platform module for React and React Native


For SpatialConnect, we need the ability to have unified interfaces across web and native. The stack we are using is React and React Native, written in ES6 with babel and webpack.

What we want is the ability to install one module in our React web app and our React Native iOS and Android apps and have that module display the correct component based on platform.

The solution we are currently using is webpack's resolve.alias and multiple outputs. The big advantage to this is it allows us to treat the web and native versions of a component as a single component within our module.

import { componentA } from './components';

//do cool things with componentA, we don't care if it's a web or native component
import componentA from 'componentA';
import componentB from 'componentB';

export default { componentA, componentB };
module.exports = {
  entry: {
    './web/index': './src/index.js' //create web/index.js entry
  resolve: {
    alias: {
      'componentA': './componentA.web', //the important part
      'componentB': './componentB.web',
  externals: {
    'react': 'react'
  output: {
    library: 'crossplatformmodules',
    libraryTarget: 'umd',
    filename: '[name].js'
  module: {
    loaders: [
        test: /\.js?$/,
        exclude: /(node_modules)/,
        loader: 'babel'
module.exports = {
  entry: {
    './native/index': '.src/index.js'
  resolve: {
    alias: {
      'componentA': './componentA.native',
      'componentB': './componentB.native',
  externals: {
    'react-native': 'react-native'
"main": "web/index.js",
"scripts": {
  "build": "npm run build-web && npm run build-native",
  "build-web": "webpack --config webpack.web.js",
  "build-native": "webpack --config webpack.native.js"

You can then import the default module in your web code, and the native version in your native app.

import crossplatform from 'crossplatform';

import crossplatform from 'crossplatform/native';

Using React's FixedDataTable as an Attribute Table


I just updated iso to React 0.14.7 after not being able to work on it for a few months. Turns out, React has changed a lot in that time, including FixedDataTable, a popular React component for making better tables.

It is perfect for something like an Attribute Table in GIS, because there are an unknown number of columns/rows when displaying random data. FixedDataTable supports a fixed header row to make scrolling easier, fixed columns (good for an index column in this application), and column resizing. It also adds nice hooks for row clicking, so we can update the data model to select features on click.

Check out the code in iso that created a FixedDataTable from GeoJSON here: https://github.com/frankrowe/iso/blob/gh-pages/src/components/AttributeTable.jsx.

A new take on some QGIS icons


I took a shot at redesigning some QGIS icons, which I am using in the web-based map editor, iso.

I wanted it to be apparent that these buttons were for layer operations, so I added the recognizable three layer stack. I also brightened colors, removed gradients, and replace plus with an up arrow for importing layers. I felt the create new layer (*) and add new layer (+) were ambiguous in the old icons. An up arrow is more recognizable as upload/import.

npm-scripts > grunt


"scripts": {
  "watch-css": "onchange 'style/*.less' -- npm run build-css",
  "watch-js": "watchify -t [ babelify --presets [ es2015 react ] ] js/*.jsx js/*.js -o dist/bundle.js -dv",
  "watch": "npm run watch-css & npm run watch-js",
  "build-css": "lessc --autoprefix='last 2 versions' style/main.less dist/style.css",
  "build-js": "browserify -t [ babelify --presets [ es2015 react ] ] js/*.jsx js/*.js -o dist/bundle.js",
  "build": "npm run build-css && npm run build-js"

Reasons: simpler, less stuff to install, fewer files in your root, simpler, no plugins, same functionality, simpler.

Not only do you not need to install/maintain/update grunt, you don't need any of the grunt plugins. Instead of installing and updating grunt-browserify, grunt-less, etc, you just install the actual browserify package. You've just cut your dependencies in half.

Above is an example of what I use for a project with React, ES2015, and Less. I use browserify with a babelify transform to package my js + jsx modules, and watchify to do the same on file changes. I use less to compile css from my main.less file, from which I import all other less files. onchange enables us to watch for changes in your less files. You can then run npm run build to build your css/js, and npm run watch to recompile on changes during development.

npm-scripts has lots of nice built in scripts and pre and post hooks that you can take advantage of. Bottom line, it's better to take advantage of your already existing package.json than add an entire new dependency to your project.

location-bar: GPS coordinates in your menu bar



Discovered BitBar today and thought it would be useful to display GPS coords in the menu bar. I use them a lot when testing lat/long pairs, plus it's neat to see when you're on the road.

You need to download an executable called whereami to get your location. It's open source and uses the OS X CoreLocation framework.

Once you have that in your home directory, just put this small script in your BitBar plugin directory, chmod +x it, and you're done.