When Spatial Search Goes Wrong

2019-02-13

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

2018-01-17

Link to Observable notebook

Planet API Explorer

2018-10-23

Link to Observable notebook

Basemap Selector UIs for Mobile Apps

2018-01-30

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

2016-05-24

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.

//src/index.js
import { componentA } from './components';

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

export default { componentA, componentB };
//webpack.web.js
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'
      }
    ]
  }
};
//webpack.web.js
module.exports = {
  entry: {
    './native/index': '.src/index.js'
  },
  resolve: {
    alias: {
      'componentA': './componentA.native',
      'componentB': './componentB.native',
    }
  },
  externals: {
    'react-native': 'react-native'
  }...
//package.json
"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.

//web.app.js
import crossplatform from 'crossplatform';

//index.ios.js
import crossplatform from 'crossplatform/native';