diff --git a/package-lock.json b/package-lock.json
index f60e354d56c..f40c0371415 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,6 +36,57 @@
         "acorn": "5.3.0"
       }
     },
+    "@mapbox/geojson-area": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz",
+      "integrity": "sha1-GNeBSqNr8j+7zDefjiaiKSfevxA=",
+      "requires": {
+        "wgs84": "0.0.0"
+      }
+    },
+    "@mapbox/gl-matrix": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/@mapbox/gl-matrix/-/gl-matrix-0.0.1.tgz",
+      "integrity": "sha1-5RJqq01kw2uBx6l9CuDd3eV3PSs="
+    },
+    "@mapbox/mapbox-gl-supported": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.3.0.tgz",
+      "integrity": "sha512-ACfuqIMxAzIoKRp3e7J2VjTJFBbrOoXqt4b7vy1x5uz5Od5Drroe2Ei/+R416eKpTXE1L0zHq5D2m3Q+SyM9WQ=="
+    },
+    "@mapbox/point-geometry": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
+      "integrity": "sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI="
+    },
+    "@mapbox/shelf-pack": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/shelf-pack/-/shelf-pack-3.1.0.tgz",
+      "integrity": "sha1-Ht6pwL9nFbIXFxumBkbCAa9SD2o="
+    },
+    "@mapbox/tiny-sdf": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.0.tgz",
+      "integrity": "sha512-dnhyk8X2BkDRWImgHILYAGgo+kuciNYX30CUKj/Qd5eNjh54OWM/mdOS/PWsPeN+3abtN+QDGYM4G220ynVJKA=="
+    },
+    "@mapbox/unitbezier": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz",
+      "integrity": "sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4="
+    },
+    "@mapbox/vector-tile": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.0.tgz",
+      "integrity": "sha1-xJX5clJb78zvzYOPRf+jfvO3D+g=",
+      "requires": {
+        "@mapbox/point-geometry": "0.1.0"
+      }
+    },
+    "@mapbox/whoots-js": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.0.0.tgz",
+      "integrity": "sha1-wd5CkwgUJNo6wwwjr6hQrxAZu1Q="
+    },
     "@plotly/d3-sankey": {
       "version": "0.5.0",
       "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.5.0.tgz",
@@ -127,6 +178,21 @@
         }
       }
     },
+    "acorn-object-spread": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/acorn-object-spread/-/acorn-object-spread-1.0.0.tgz",
+      "integrity": "sha1-SOrQ9KjrFplaF6Dbn/xqyq2kumg=",
+      "requires": {
+        "acorn": "3.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+        }
+      }
+    },
     "acorn5-object-spread": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/acorn5-object-spread/-/acorn5-object-spread-4.0.0.tgz",
@@ -186,6 +252,7 @@
       "version": "5.5.2",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
       "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+      "dev": true,
       "requires": {
         "co": "4.6.0",
         "fast-deep-equal": "1.0.0",
@@ -275,8 +342,7 @@
     "ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-      "dev": true
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
     },
     "ansi-styles": {
       "version": "3.2.0",
@@ -289,7 +355,8 @@
     "any-promise": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+      "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
+      "dev": true
     },
     "anymatch": {
       "version": "1.3.2",
@@ -359,7 +426,6 @@
       "version": "1.0.9",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
       "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
-      "dev": true,
       "requires": {
         "sprintf-js": "1.0.3"
       },
@@ -367,8 +433,7 @@
         "sprintf-js": {
           "version": "1.0.3",
           "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-          "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-          "dev": true
+          "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
         }
       }
     },
@@ -492,7 +557,8 @@
     "asn1": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
-      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
+      "dev": true
     },
     "asn1.js": {
       "version": "4.9.2",
@@ -517,7 +583,8 @@
     "assert-plus": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
     },
     "ast-module-types": {
       "version": "2.3.2",
@@ -576,7 +643,8 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
     },
     "atob-lite": {
       "version": "1.0.0",
@@ -586,12 +654,14 @@
     "aws-sign2": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+      "dev": true
     },
     "aws4": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
-      "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
+      "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
+      "dev": true
     },
     "axios": {
       "version": "0.15.3",
@@ -669,8 +739,7 @@
     "babylon": {
       "version": "6.18.0",
       "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
-      "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
-      "dev": true
+      "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
     },
     "backo2": {
       "version": "1.0.2",
@@ -697,11 +766,6 @@
       "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
       "dev": true
     },
-    "base64-js": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz",
-      "integrity": "sha1-Ak8Pcq+iW3X5wO5zzU9V7Bvtl4Q="
-    },
     "base64id": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
@@ -712,6 +776,7 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
       "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+      "dev": true,
       "optional": true,
       "requires": {
         "tweetnacl": "0.14.5"
@@ -855,19 +920,11 @@
       "version": "4.3.1",
       "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
       "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
+      "dev": true,
       "requires": {
         "hoek": "4.2.0"
       }
     },
-    "bops": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/bops/-/bops-0.0.6.tgz",
-      "integrity": "sha1-CC0dVfoB5g29wuvC26N/ZZVUzzo=",
-      "requires": {
-        "base64-js": "0.0.2",
-        "to-utf8": "0.0.1"
-      }
-    },
     "boundary-cells": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/boundary-cells/-/boundary-cells-2.0.1.tgz",
@@ -1236,6 +1293,11 @@
         "inherits": "2.0.3"
       }
     },
+    "browserify-package-json": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/browserify-package-json/-/browserify-package-json-1.0.1.tgz",
+      "integrity": "sha1-mN3oqlxWH9bT/km7qhArdLOW/eo="
+    },
     "browserify-rsa": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
@@ -1451,7 +1513,8 @@
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
     },
     "cdt2d": {
       "version": "1.0.0",
@@ -1633,7 +1696,8 @@
     "co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+      "dev": true
     },
     "code-point-at": {
       "version": "1.1.0",
@@ -1753,6 +1817,7 @@
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
       "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+      "dev": true,
       "requires": {
         "delayed-stream": "1.0.0"
       }
@@ -1988,6 +2053,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
       "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+      "dev": true,
       "requires": {
         "boom": "5.2.0"
       },
@@ -1996,6 +2062,7 @@
           "version": "5.2.0",
           "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
           "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
+          "dev": true,
           "requires": {
             "hoek": "4.2.0"
           }
@@ -2147,6 +2214,7 @@
       "version": "1.14.1",
       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
       "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
       "requires": {
         "assert-plus": "1.0.0"
       }
@@ -2272,7 +2340,8 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+      "dev": true
     },
     "delegates": {
       "version": "1.0.0",
@@ -2696,6 +2765,7 @@
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
       "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+      "dev": true,
       "optional": true,
       "requires": {
         "jsbn": "0.1.1"
@@ -3188,7 +3258,16 @@
     "extend": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
-      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+      "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
+      "dev": true
+    },
+    "extend-shallow": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+      "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+      "requires": {
+        "is-extendable": "0.1.1"
+      }
     },
     "external-editor": {
       "version": "2.1.0",
@@ -3229,7 +3308,8 @@
     "extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
     },
     "falafel": {
       "version": "2.1.0",
@@ -3245,7 +3325,8 @@
     "fast-deep-equal": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
-      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
+      "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
+      "dev": true
     },
     "fast-isnumeric": {
       "version": "1.1.1",
@@ -3255,18 +3336,14 @@
     "fast-json-stable-stringify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "dev": true
     },
     "fast-levenshtein": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
       "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
     },
-    "feature-filter": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/feature-filter/-/feature-filter-2.2.0.tgz",
-      "integrity": "sha1-PMNWAV6WjDYq+99/8bt0Td9/wuA="
-    },
     "figures": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -3437,6 +3514,15 @@
         "is-typedarray": "1.0.0"
       }
     },
+    "flow-remove-types": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/flow-remove-types/-/flow-remove-types-1.2.3.tgz",
+      "integrity": "sha512-ypq/U3V+t9atYiOuSJd40tekCra03EHKoRsiK/wXGrsZimuum0kdwVY7Yv0HTaoXgHW1WiayomYd+Q3kkvPl9Q==",
+      "requires": {
+        "babylon": "6.18.0",
+        "vlq": "0.2.3"
+      }
+    },
     "follow-redirects": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz",
@@ -3487,12 +3573,14 @@
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
     },
     "form-data": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
       "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
+      "dev": true,
       "requires": {
         "asynckit": "0.4.0",
         "combined-stream": "1.0.5",
@@ -3565,33 +3653,6 @@
         "jsonfile": "2.4.0"
       }
     },
-    "fs-promise": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-0.5.0.tgz",
-      "integrity": "sha1-Q0fWv2JGVacGGkMZITw5MnatPvM=",
-      "dev": true,
-      "requires": {
-        "any-promise": "1.3.0",
-        "fs-extra": "0.26.7",
-        "mz": "2.7.0",
-        "thenify-all": "1.6.0"
-      },
-      "dependencies": {
-        "fs-extra": {
-          "version": "0.26.7",
-          "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
-          "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "4.1.11",
-            "jsonfile": "2.4.0",
-            "klaw": "1.3.1",
-            "path-is-absolute": "1.0.1",
-            "rimraf": "2.6.2"
-          }
-        }
-      }
-    },
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -4611,43 +4672,20 @@
         "is-property": "1.0.2"
       }
     },
-    "geojson-area": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/geojson-area/-/geojson-area-0.1.0.tgz",
-      "integrity": "sha1-1I2AcILPrfSnjfE0m+UPOL8YlK4=",
-      "requires": {
-        "wgs84": "0.0.0"
-      }
-    },
     "geojson-rewind": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/geojson-rewind/-/geojson-rewind-0.1.0.tgz",
-      "integrity": "sha1-VwIqBUsZZmDXVTVP5dJmhNkM0Bk=",
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/geojson-rewind/-/geojson-rewind-0.3.0.tgz",
+      "integrity": "sha512-5dsjiZGk6p///Ju9kh7uGW+I74CZriHsxqBNPbIN4bbInfKmHwwM+f8fZ42fmpV5emeUYLTTC+GWs3EC1TMjNQ==",
       "requires": {
-        "concat-stream": "1.2.1",
-        "geojson-area": "0.1.0",
-        "minimist": "0.0.5"
-      },
-      "dependencies": {
-        "concat-stream": {
-          "version": "1.2.1",
-          "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.2.1.tgz",
-          "integrity": "sha1-81EAtsRjeL+6i2uA+fDQzN8T3GA=",
-          "requires": {
-            "bops": "0.0.6"
-          }
-        },
-        "minimist": {
-          "version": "0.0.5",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
-          "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY="
-        }
+        "@mapbox/geojson-area": "0.2.2",
+        "concat-stream": "1.6.0",
+        "minimist": "1.2.0"
       }
     },
     "geojson-vt": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-2.4.0.tgz",
-      "integrity": "sha1-PBz0RJPzXrTSxwyV2mVQ3mYHLAU="
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.0.0.tgz",
+      "integrity": "sha512-FL7VV56gYBDBh0F7EWyZV5G9/L2EHEHh9SyhEpJz4c8YDPerM6dnP9VbRcsbyg1wH+1oyoHRA9dlJkGs/IXULA=="
     },
     "get-amd-module-type": {
       "version": "2.0.5",
@@ -4730,6 +4768,7 @@
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
       "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
       "requires": {
         "assert-plus": "1.0.0"
       }
@@ -4955,11 +4994,6 @@
       "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.1.4.tgz",
       "integrity": "sha1-HolbVYkuVqiWhnq9g30483oXgIY="
     },
-    "gl-matrix": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-2.4.0.tgz",
-      "integrity": "sha1-IImxMwGinuyCLZ2Z3/wfeO6aPFA="
-    },
     "gl-matrix-invert": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/gl-matrix-invert/-/gl-matrix-invert-1.0.0.tgz",
@@ -5571,6 +5605,24 @@
         "temp": "0.4.0"
       }
     },
+    "gray-matter": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-3.1.1.tgz",
+      "integrity": "sha512-nZ1qjLmayEv0/wt3sHig7I0s3/sJO0dkAaKYQ5YAOApUtYEOonXSFdWvL1khvnZMTvov4UufkqlFsilPnejEXA==",
+      "requires": {
+        "extend-shallow": "2.0.1",
+        "js-yaml": "3.10.0",
+        "kind-of": "5.1.0",
+        "strip-bom-string": "1.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
+        }
+      }
+    },
     "grid-index": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.0.0.tgz",
@@ -5620,12 +5672,14 @@
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true
     },
     "har-validator": {
       "version": "5.0.3",
       "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
       "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+      "dev": true,
       "requires": {
         "ajv": "5.5.2",
         "har-schema": "2.0.0"
@@ -5643,7 +5697,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
       "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "dev": true,
       "requires": {
         "ansi-regex": "2.1.1"
       }
@@ -5738,6 +5791,7 @@
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
       "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
+      "dev": true,
       "requires": {
         "boom": "4.3.1",
         "cryptiles": "3.1.2",
@@ -5776,7 +5830,8 @@
     "hoek": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
-      "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ=="
+      "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==",
+      "dev": true
     },
     "hosted-git-info": {
       "version": "2.5.0",
@@ -5844,6 +5899,7 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
       "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
       "requires": {
         "assert-plus": "1.0.0",
         "jsprim": "1.4.1",
@@ -6236,8 +6292,7 @@
     "is-extendable": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
-      "dev": true
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
     },
     "is-extglob": {
       "version": "1.0.0",
@@ -6446,7 +6501,8 @@
     "isstream": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+      "dev": true
     },
     "istanbul": {
       "version": "0.4.5",
@@ -6575,7 +6631,6 @@
       "version": "3.10.0",
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
       "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
-      "dev": true,
       "requires": {
         "argparse": "1.0.9",
         "esprima": "4.0.0"
@@ -6584,8 +6639,7 @@
         "esprima": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
-          "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
-          "dev": true
+          "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
         }
       }
     },
@@ -6593,6 +6647,7 @@
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
       "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true,
       "optional": true
     },
     "jsdom": {
@@ -6672,12 +6727,14 @@
     "json-schema": {
       "version": "0.2.3",
       "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
     },
     "json-schema-traverse": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+      "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+      "dev": true
     },
     "json-stable-stringify": {
       "version": "0.0.1",
@@ -6697,12 +6754,14 @@
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+      "dev": true
     },
     "jsonfile": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
       "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+      "dev": true,
       "requires": {
         "graceful-fs": "4.1.11"
       }
@@ -6738,6 +6797,7 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
       "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "dev": true,
       "requires": {
         "assert-plus": "1.0.0",
         "extsprintf": "1.3.0",
@@ -7093,14 +7153,6 @@
         "is-buffer": "1.1.6"
       }
     },
-    "klaw": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
-      "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
-      "requires": {
-        "graceful-fs": "4.1.11"
-      }
-    },
     "labeled-stream-splicer": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz",
@@ -7216,26 +7268,6 @@
       "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
       "dev": true
     },
-    "lodash._baseisequal": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz",
-      "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=",
-      "requires": {
-        "lodash.isarray": "3.0.4",
-        "lodash.istypedarray": "3.0.6",
-        "lodash.keys": "3.1.2"
-      }
-    },
-    "lodash._bindcallback": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
-      "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4="
-    },
-    "lodash._getnative": {
-      "version": "3.9.1",
-      "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-      "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
-    },
     "lodash.assign": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
@@ -7248,40 +7280,6 @@
       "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
       "dev": true
     },
-    "lodash.isarguments": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
-      "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
-    },
-    "lodash.isarray": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
-      "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
-    },
-    "lodash.isequal": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz",
-      "integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=",
-      "requires": {
-        "lodash._baseisequal": "3.0.7",
-        "lodash._bindcallback": "3.0.1"
-      }
-    },
-    "lodash.istypedarray": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
-      "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I="
-    },
-    "lodash.keys": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
-      "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
-      "requires": {
-        "lodash._getnative": "3.9.1",
-        "lodash.isarguments": "3.1.0",
-        "lodash.isarray": "3.0.4"
-      }
-    },
     "lodash.memoize": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
@@ -7778,71 +7776,115 @@
       "dev": true
     },
     "mapbox-gl": {
-      "version": "0.22.1",
-      "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.22.1.tgz",
-      "integrity": "sha1-kqllVH1MLyTCLLxIfu2khpTLYno=",
-      "requires": {
+      "version": "0.44.0",
+      "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.44.0.tgz",
+      "integrity": "sha512-vMeZaLXjG1B1BKOD9HB11sb9UIUvbzXWJu0NR38j9Uyp1h5xUXqh1Rqe+EhxQp3jzlHIv/LVhFKCJjQQKA2LoA==",
+      "requires": {
+        "@mapbox/gl-matrix": "0.0.1",
+        "@mapbox/mapbox-gl-supported": "1.3.0",
+        "@mapbox/point-geometry": "0.1.0",
+        "@mapbox/shelf-pack": "3.1.0",
+        "@mapbox/tiny-sdf": "1.1.0",
+        "@mapbox/unitbezier": "0.0.0",
+        "@mapbox/vector-tile": "1.3.0",
+        "@mapbox/whoots-js": "3.0.0",
+        "brfs": "1.4.4",
+        "bubleify": "0.7.0",
         "csscolorparser": "1.0.3",
         "earcut": "2.1.3",
-        "feature-filter": "2.2.0",
-        "geojson-rewind": "0.1.0",
-        "geojson-vt": "2.4.0",
-        "gl-matrix": "2.4.0",
+        "geojson-rewind": "0.3.0",
+        "geojson-vt": "3.0.0",
+        "gray-matter": "3.1.1",
         "grid-index": "1.0.0",
-        "mapbox-gl-function": "1.3.0",
-        "mapbox-gl-shaders": "github:mapbox/mapbox-gl-shaders#de2ab007455aa2587c552694c68583f94c9f2747",
-        "mapbox-gl-style-spec": "github:mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae",
-        "mapbox-gl-supported": "1.2.0",
-        "pbf": "1.3.7",
-        "pngjs": "2.3.1",
-        "point-geometry": "0.0.0",
+        "jsonlint-lines-primitives": "1.6.0",
+        "minimist": "0.0.8",
+        "package-json-versionify": "1.0.4",
+        "pbf": "3.1.0",
         "quickselect": "1.0.1",
-        "request": "2.83.0",
-        "resolve-url": "0.2.1",
-        "shelf-pack": "1.1.0",
+        "rw": "1.3.3",
+        "shuffle-seed": "1.1.6",
+        "sort-object": "0.3.2",
         "supercluster": "2.3.0",
+        "through2": "2.0.3",
+        "tinyqueue": "1.2.3",
         "unassertify": "2.1.0",
-        "unitbezier": "0.0.0",
-        "vector-tile": "1.3.0",
-        "vt-pbf": "2.1.4",
-        "webworkify": "1.5.0",
-        "whoots-js": "2.1.0"
-      }
-    },
-    "mapbox-gl-function": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/mapbox-gl-function/-/mapbox-gl-function-1.3.0.tgz",
-      "integrity": "sha1-zuPZV1DBidReg6tBoKV/wqilCbw="
-    },
-    "mapbox-gl-shaders": {
-      "version": "github:mapbox/mapbox-gl-shaders#de2ab007455aa2587c552694c68583f94c9f2747",
-      "requires": {
-        "brfs": "1.4.4"
-      }
-    },
-    "mapbox-gl-style-spec": {
-      "version": "github:mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae",
-      "requires": {
-        "csscolorparser": "1.0.3",
-        "jsonlint-lines-primitives": "1.6.0",
-        "lodash.isequal": "3.0.4",
-        "minimist": "0.0.8",
-        "rw": "0.1.4",
-        "sort-object": "0.3.2"
+        "unflowify": "1.0.1",
+        "vt-pbf": "3.1.0",
+        "webworkify": "1.5.0"
       },
       "dependencies": {
+        "acorn": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+        },
+        "ansi-styles": {
+          "version": "2.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+        },
+        "buble": {
+          "version": "0.15.2",
+          "resolved": "https://registry.npmjs.org/buble/-/buble-0.15.2.tgz",
+          "integrity": "sha1-VH/EdIP45egXbYKqXrzLGDsC1hM=",
+          "requires": {
+            "acorn": "3.3.0",
+            "acorn-jsx": "3.0.1",
+            "acorn-object-spread": "1.0.0",
+            "chalk": "1.1.3",
+            "magic-string": "0.14.0",
+            "minimist": "1.2.0",
+            "os-homedir": "1.0.2"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "1.2.0",
+              "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+            }
+          }
+        },
+        "bubleify": {
+          "version": "0.7.0",
+          "resolved": "https://registry.npmjs.org/bubleify/-/bubleify-0.7.0.tgz",
+          "integrity": "sha1-0I6mQv/Qhf+HEciEP1cHLw1euPY=",
+          "requires": {
+            "buble": "0.15.2",
+            "object-assign": "4.1.1"
+          }
+        },
+        "chalk": {
+          "version": "1.1.3",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "requires": {
+            "ansi-styles": "2.2.1",
+            "escape-string-regexp": "1.0.5",
+            "has-ansi": "2.0.0",
+            "strip-ansi": "3.0.1",
+            "supports-color": "2.0.0"
+          }
+        },
+        "magic-string": {
+          "version": "0.14.0",
+          "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.14.0.tgz",
+          "integrity": "sha1-VyJK7xcByu7Sc7F6OalW5ysXJGI=",
+          "requires": {
+            "vlq": "0.2.3"
+          }
+        },
         "minimist": {
           "version": "0.0.8",
           "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
           "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+        },
+        "supports-color": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
         }
       }
     },
-    "mapbox-gl-supported": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/mapbox-gl-supported/-/mapbox-gl-supported-1.2.0.tgz",
-      "integrity": "sha1-y9NN+JQgbK3amjPI2aRgnya7GYk="
-    },
     "marching-simplex-table": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/marching-simplex-table/-/marching-simplex-table-1.0.0.tgz",
@@ -8019,12 +8061,14 @@
     "mime-db": {
       "version": "1.30.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
-      "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
+      "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=",
+      "dev": true
     },
     "mime-types": {
       "version": "2.1.17",
       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
       "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
+      "dev": true,
       "requires": {
         "mime-db": "1.30.0"
       }
@@ -8813,6 +8857,11 @@
             "has-color": "0.1.7",
             "strip-ansi": "0.1.1"
           }
+        },
+        "strip-ansi": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+          "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
         }
       }
     },
@@ -8903,7 +8952,8 @@
     "oauth-sign": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
-      "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+      "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+      "dev": true
     },
     "object-assign": {
       "version": "4.1.1",
@@ -9147,6 +9197,14 @@
         }
       }
     },
+    "package-json-versionify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/package-json-versionify/-/package-json-versionify-1.0.4.tgz",
+      "integrity": "sha1-WGBYepRIc6a35tJujlH/siMVvxc=",
+      "requires": {
+        "browserify-package-json": "1.0.1"
+      }
+    },
     "pad-left": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-1.0.2.tgz",
@@ -9318,9 +9376,9 @@
       }
     },
     "pbf": {
-      "version": "1.3.7",
-      "resolved": "https://registry.npmjs.org/pbf/-/pbf-1.3.7.tgz",
-      "integrity": "sha1-Hj0Ee6PL6Ahq6FSiVQOrRTfUM10=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.1.0.tgz",
+      "integrity": "sha512-/hYJmIsTmh7fMkHAWWXJ5b8IKLWdjdlAFb3IHkRBn1XUhIYBChVGfVwmHEAV3UfXTxsP/AKfYTXTS/dCPxJd5w==",
       "requires": {
         "ieee754": "1.1.8",
         "resolve-protobuf-schema": "2.0.0"
@@ -9342,7 +9400,8 @@
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
     },
     "permutation-parity": {
       "version": "1.0.0",
@@ -9431,16 +9490,6 @@
       "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
       "dev": true
     },
-    "pngjs": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-2.3.1.tgz",
-      "integrity": "sha1-EdHhK5y2TWPjDBQ6Mw9MH1Z9qF8="
-    },
-    "point-geometry": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/point-geometry/-/point-geometry-0.0.0.tgz",
-      "integrity": "sha1-b8vK16gDtkGCR91uScKFPFhNr/c="
-    },
     "point-in-big-polygon": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/point-in-big-polygon/-/point-in-big-polygon-2.0.0.tgz",
@@ -9614,7 +9663,8 @@
     "punycode": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+      "dev": true
     },
     "q": {
       "version": "1.4.1",
@@ -9632,7 +9682,8 @@
     "qs": {
       "version": "6.5.1",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
-      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+      "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
+      "dev": true
     },
     "quat-slerp": {
       "version": "1.0.1",
@@ -9804,40 +9855,192 @@
         "fs-promise": "0.5.0"
       },
       "dependencies": {
+        "any-promise": {
+          "version": "1.3.0",
+          "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+          "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
+          "dev": true
+        },
         "balanced-match": {
           "version": "0.4.2",
           "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
-          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
+          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+          "dev": true
+        },
+        "brace-expansion": {
+          "version": "1.1.6",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
+          "integrity": "sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=",
+          "dev": true,
+          "requires": {
+            "balanced-match": "0.4.2",
+            "concat-map": "0.0.1"
+          }
+        },
+        "concat-map": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+          "dev": true
         },
         "fs-extra": {
           "version": "0.26.7",
           "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
           "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+          "dev": true,
           "requires": {
             "graceful-fs": "4.1.11",
             "jsonfile": "2.4.0",
             "klaw": "1.3.1",
             "path-is-absolute": "1.0.1",
-            "rimraf": "2.6.2"
+            "rimraf": "2.6.1"
+          }
+        },
+        "fs-promise": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/fs-promise/-/fs-promise-0.5.0.tgz",
+          "integrity": "sha1-Q0fWv2JGVacGGkMZITw5MnatPvM=",
+          "dev": true,
+          "requires": {
+            "any-promise": "1.3.0",
+            "fs-extra": "0.26.7",
+            "mz": "2.6.0",
+            "thenify-all": "1.6.0"
+          }
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+          "dev": true
+        },
+        "glob": {
+          "version": "7.1.1",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+          "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "1.0.0",
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.3",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.1.11",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+          "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+          "dev": true
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+          "dev": true,
+          "requires": {
+            "once": "1.4.0",
+            "wrappy": "1.0.2"
+          }
+        },
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "jsonfile": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+          "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11"
+          }
+        },
+        "klaw": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+          "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11"
           }
         },
         "minimatch": {
           "version": "3.0.3",
           "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
           "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=",
+          "dev": true,
           "requires": {
-            "brace-expansion": "1.1.8"
+            "brace-expansion": "1.1.6"
           }
         },
         "mz": {
           "version": "2.6.0",
           "resolved": "https://registry.npmjs.org/mz/-/mz-2.6.0.tgz",
           "integrity": "sha1-yLhSHZWN8KTydoAl22nHGe5O8c4=",
+          "dev": true,
           "requires": {
             "any-promise": "1.3.0",
             "object-assign": "4.1.1",
             "thenify-all": "1.6.0"
           }
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+          "dev": true
+        },
+        "once": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+          "dev": true,
+          "requires": {
+            "wrappy": "1.0.2"
+          }
+        },
+        "path-is-absolute": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+          "dev": true
+        },
+        "rimraf": {
+          "version": "2.6.1",
+          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
+          "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
+          "dev": true,
+          "requires": {
+            "glob": "7.1.1"
+          }
+        },
+        "thenify": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.2.1.tgz",
+          "integrity": "sha1-JR/RyAr/blz1fLF5qx/LckJpvRE=",
+          "dev": true,
+          "requires": {
+            "any-promise": "1.3.0"
+          }
+        },
+        "thenify-all": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+          "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+          "dev": true,
+          "requires": {
+            "thenify": "3.2.1"
+          }
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+          "dev": true
         }
       }
     },
@@ -10196,6 +10399,7 @@
       "version": "2.83.0",
       "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
       "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
+      "dev": true,
       "requires": {
         "aws-sign2": "0.7.0",
         "aws4": "1.6.0",
@@ -10365,11 +10569,6 @@
         "protocol-buffers-schema": "2.2.0"
       }
     },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
-    },
     "restore-cursor": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@@ -10405,6 +10604,7 @@
       "version": "2.6.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
       "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+      "dev": true,
       "requires": {
         "glob": "7.1.2"
       }
@@ -10526,9 +10726,9 @@
       "dev": true
     },
     "rw": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/rw/-/rw-0.1.4.tgz",
-      "integrity": "sha1-SQPL2AJIrg7eaFv1j9I2p6mymj4="
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
     },
     "rx-lite": {
       "version": "4.0.8",
@@ -10664,6 +10864,11 @@
         }
       }
     },
+    "seedrandom": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
+      "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw="
+    },
     "semver": {
       "version": "4.3.6",
       "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz",
@@ -10728,11 +10933,6 @@
       "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
-    "shelf-pack": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/shelf-pack/-/shelf-pack-1.1.0.tgz",
-      "integrity": "sha1-tGea/dAK1o39m70rWj6BkpOnTYI="
-    },
     "shell-quote": {
       "version": "1.6.1",
       "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
@@ -10745,6 +10945,14 @@
         "jsonify": "0.0.0"
       }
     },
+    "shuffle-seed": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/shuffle-seed/-/shuffle-seed-1.1.6.tgz",
+      "integrity": "sha1-UzwSaDurO0+j6HUfxOViFGdEJgs=",
+      "requires": {
+        "seedrandom": "2.4.3"
+      }
+    },
     "signal-exit": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@@ -10880,6 +11088,7 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
       "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
+      "dev": true,
       "requires": {
         "hoek": "4.2.0"
       }
@@ -11039,6 +11248,7 @@
       "version": "1.13.1",
       "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
       "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
+      "dev": true,
       "requires": {
         "asn1": "0.2.3",
         "assert-plus": "1.0.0",
@@ -11469,12 +11679,21 @@
     "stringstream": {
       "version": "0.0.5",
       "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
-      "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
+      "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
+      "dev": true
     },
     "strip-ansi": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
-      "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "requires": {
+        "ansi-regex": "2.1.1"
+      }
+    },
+    "strip-bom-string": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+      "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI="
     },
     "strip-indent": {
       "version": "1.0.1",
@@ -11736,6 +11955,7 @@
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
       "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
+      "dev": true,
       "requires": {
         "any-promise": "1.3.0"
       }
@@ -11744,6 +11964,7 @@
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
       "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+      "dev": true,
       "requires": {
         "thenify": "3.3.0"
       }
@@ -11824,6 +12045,11 @@
       "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
       "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
     },
+    "tinyqueue": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-1.2.3.tgz",
+      "integrity": "sha512-Qz9RgWuO9l8lT+Y9xvbzhPT2efIUIFd69N7eF7tJ9lnQl0iLj1M7peK7IoUGZL9DJHw9XftqLreccfxcQgYLxA=="
+    },
     "tmp": {
       "version": "0.0.31",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",
@@ -11858,11 +12084,6 @@
         "parse-unit": "1.0.1"
       }
     },
-    "to-utf8": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz",
-      "integrity": "sha1-0Xrqcv8vujm55DYBvns/9y4ImFI="
-    },
     "topojson-client": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-2.1.0.tgz",
@@ -11875,6 +12096,7 @@
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
       "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
+      "dev": true,
       "requires": {
         "punycode": "1.4.1"
       }
@@ -11967,6 +12189,7 @@
       "version": "0.6.0",
       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
       "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
       "requires": {
         "safe-buffer": "5.1.1"
       }
@@ -11985,6 +12208,7 @@
       "version": "0.14.5",
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
       "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true,
       "optional": true
     },
     "two-product": {
@@ -12172,6 +12396,15 @@
       "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
       "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
     },
+    "unflowify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/unflowify/-/unflowify-1.0.1.tgz",
+      "integrity": "sha1-ouoNJcCv/MRpVeZHNXX3xaH0ppY=",
+      "requires": {
+        "flow-remove-types": "1.2.3",
+        "through": "2.3.8"
+      }
+    },
     "union-find": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz",
@@ -12182,11 +12415,6 @@
       "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
       "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
     },
-    "unitbezier": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/unitbezier/-/unitbezier-0.0.0.tgz",
-      "integrity": "sha1-M79/XXKExTUL/Fx/dw+6dUnFSl4="
-    },
     "unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -12263,7 +12491,8 @@
     "uuid": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
-      "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
+      "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
+      "dev": true
     },
     "uws": {
       "version": "0.14.5",
@@ -12282,14 +12511,6 @@
         "spdx-expression-parse": "1.0.4"
       }
     },
-    "vector-tile": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/vector-tile/-/vector-tile-1.3.0.tgz",
-      "integrity": "sha1-BtUWqDsGPwTILvU5zxuxrr62lrQ=",
-      "requires": {
-        "point-geometry": "0.0.0"
-      }
-    },
     "vectorize-text": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.0.2.tgz",
@@ -12308,6 +12529,7 @@
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
       "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
       "requires": {
         "assert-plus": "1.0.0",
         "core-util-is": "1.0.2",
@@ -12335,13 +12557,13 @@
       "dev": true
     },
     "vt-pbf": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-2.1.4.tgz",
-      "integrity": "sha1-td98P5cGFW4LmIGpncsFY1dAtSI=",
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.0.tgz",
+      "integrity": "sha512-UUCGPkpT1P/bm3R3/HX0SCnRSto44xXx0WuLFVG6C7KspdfQfU+84etoO6cITAGCdq8V5DjuWfDhvk/pyTyt3Q==",
       "requires": {
-        "pbf": "1.3.7",
-        "point-geometry": "0.0.0",
-        "vector-tile": "1.3.0"
+        "@mapbox/point-geometry": "0.1.0",
+        "@mapbox/vector-tile": "1.3.0",
+        "pbf": "3.1.0"
       }
     },
     "w3c-hr-time": {
@@ -12450,11 +12672,6 @@
       "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
       "dev": true
     },
-    "whoots-js": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/whoots-js/-/whoots-js-2.1.0.tgz",
-      "integrity": "sha1-vLIBw04OrzNfzOWuLPh0V5qZxIc="
-    },
     "wide-align": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
diff --git a/package.json b/package.json
index 4c80b933762..2294abb7380 100644
--- a/package.json
+++ b/package.json
@@ -87,7 +87,7 @@
     "has-hover": "^1.0.1",
     "has-passive-events": "^1.0.0",
     "kdgrass": "^1.0.1",
-    "mapbox-gl": "^0.22.0",
+    "mapbox-gl": "^0.44.0",
     "matrix-camera-controller": "^2.1.3",
     "mouse-change": "^1.4.0",
     "mouse-event-offset": "^3.0.2",
diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js
index 98a64b9e334..9904121f5b0 100644
--- a/src/components/drawing/index.js
+++ b/src/components/drawing/index.js
@@ -439,8 +439,8 @@ drawing.pointStyle = function(s, trace, gd) {
     });
 };
 
-drawing.selectedPointStyle = function(s, trace) {
-    if(!s.size() || !trace.selectedpoints) return;
+drawing.makeSelectedPointStyleFns = function(trace) {
+    var out = {};
 
     var selectedAttrs = trace.selected || {};
     var unselectedAttrs = trace.unselected || {};
@@ -455,40 +455,31 @@ drawing.selectedPointStyle = function(s, trace) {
     var smoIsDefined = smo !== undefined;
     var usmoIsDefined = usmo !== undefined;
 
-    s.each(function(d) {
-        var pt = d3.select(this);
+    out.opacityFn = function(d) {
         var dmo = d.mo;
         var dmoIsDefined = dmo !== undefined;
-        var mo2;
 
         if(dmoIsDefined || smoIsDefined || usmoIsDefined) {
             if(d.selected) {
-                if(smoIsDefined) mo2 = smo;
+                if(smoIsDefined) return smo;
             } else {
-                if(usmoIsDefined) mo2 = usmo;
-                else mo2 = DESELECTDIM * (dmoIsDefined ? dmo : mo);
+                if(usmoIsDefined) return usmo;
+                return DESELECTDIM * (dmoIsDefined ? dmo : mo);
             }
         }
-
-        if(mo2 !== undefined) pt.style('opacity', mo2);
-    });
+    };
 
     var smc = selectedMarker.color;
     var usmc = unselectedMarker.color;
 
     if(smc || usmc) {
-        s.each(function(d) {
-            var pt = d3.select(this);
-            var mc2;
-
+        out.colorFn = function(d) {
             if(d.selected) {
-                if(smc) mc2 = smc;
+                if(smc) return smc;
             } else {
-                if(usmc) mc2 = usmc;
+                if(usmc) return usmc;
             }
-
-            if(mc2) Color.fill(pt, mc2);
-        });
+        };
     }
 
     var sms = selectedMarker.size;
@@ -496,18 +487,45 @@ drawing.selectedPointStyle = function(s, trace) {
     var smsIsDefined = sms !== undefined;
     var usmsIsDefined = usms !== undefined;
 
-    if(Registry.traceIs(trace, 'symbols') && (smsIsDefined || usmsIsDefined)) {
-        s.each(function(d) {
-            var pt = d3.select(this);
+    if(smsIsDefined || usmsIsDefined) {
+        out.sizeFn = function(d) {
             var mrc = d.mrc;
-            var mx = d.mx || marker.symbol || 0;
-            var mrc2;
-
             if(d.selected) {
-                mrc2 = (smsIsDefined) ? sms / 2 : mrc;
+                return smsIsDefined ? sms / 2 : mrc;
             } else {
-                mrc2 = (usmsIsDefined) ? usms / 2 : mrc;
+                return usmsIsDefined ? usms / 2 : mrc;
             }
+        };
+    }
+
+    return out;
+};
+
+drawing.selectedPointStyle = function(s, trace) {
+    if(!s.size() || !trace.selectedpoints) return;
+
+    var fns = drawing.makeSelectedPointStyleFns(trace);
+    var marker = trace.marker || {};
+
+    s.each(function(d) {
+        var pt = d3.select(this);
+        var mo2 = fns.opacityFn(d);
+        if(mo2 !== undefined) pt.style('opacity', mo2);
+    });
+
+    if(fns.colorFn) {
+        s.each(function(d) {
+            var pt = d3.select(this);
+            var mc2 = fns.colorFn(d);
+            if(mc2) Color.fill(pt, mc2);
+        });
+    }
+
+    if(Registry.traceIs(trace, 'symbols') && fns.sizeFn) {
+        s.each(function(d) {
+            var pt = d3.select(this);
+            var mx = d.mx || marker.symbol || 0;
+            var mrc2 = fns.sizeFn(d);
 
             pt.attr('d', makePointPath(drawing.symbolNumber(mx), mrc2));
 
diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js
index 3a7bcbea755..3ded8e18b47 100644
--- a/src/plots/cartesian/dragbox.js
+++ b/src/plots/cartesian/dragbox.js
@@ -358,6 +358,10 @@ function makeDragBox(gd, plotinfo, x, y, w, h, ns, ew) {
             return;
         }
 
+        if(redrawTimer === null) {
+            clearSelect(zoomlayer);
+        }
+
         // If a transition is in progress, then disable any behavior:
         if(gd._transitioningWithDuration) {
             e.preventDefault();
diff --git a/src/plots/mapbox/constants.js b/src/plots/mapbox/constants.js
index cde8a55fc86..e724315f471 100644
--- a/src/plots/mapbox/constants.js
+++ b/src/plots/mapbox/constants.js
@@ -9,13 +9,21 @@
 
 'use strict';
 
+var requiredVersion = '0.44.0';
 
 module.exports = {
+    requiredVersion: requiredVersion,
+
     styleUrlPrefix: 'mapbox://styles/mapbox/',
     styleUrlSuffix: 'v9',
 
     controlContainerClassName: 'mapboxgl-control-container',
 
+    wrongVersionErrorMsg: [
+        'Your custom plotly.js bundle is not using the correct mapbox-gl version',
+        'Please install mapbox-gl@' + requiredVersion + '.'
+    ].join('\n'),
+
     noAccessTokenErrorMsg: [
         'Missing Mapbox access token.',
         'Mapbox trace type require a Mapbox access token to be registered.',
@@ -24,5 +32,11 @@ module.exports = {
         'More info here: https://www.mapbox.com/help/define-access-token/'
     ].join('\n'),
 
-    mapOnErrorMsg: 'Mapbox error.'
+    mapOnErrorMsg: 'Mapbox error.',
+
+    // a subset of node_modules/mapbox-gl/dist/mapbox-gl.css
+    styleRules: {
+        map: 'overflow:hidden;position:relative;',
+        'missing-css': 'display:none',
+    }
 };
diff --git a/src/plots/mapbox/index.js b/src/plots/mapbox/index.js
index 0e7b12d57d8..dc19d1c5dca 100644
--- a/src/plots/mapbox/index.js
+++ b/src/plots/mapbox/index.js
@@ -6,7 +6,6 @@
 * LICENSE file in the root directory of this source tree.
 */
 
-
 'use strict';
 
 var mapboxgl = require('mapbox-gl');
@@ -20,6 +19,9 @@ var constants = require('./constants');
 
 var MAPBOX = 'mapbox';
 
+for(var k in constants.styleRules) {
+    Lib.addStyleRule('.mapboxgl-' + k, constants.styleRules[k]);
+}
 
 exports.name = MAPBOX;
 
@@ -53,6 +55,10 @@ exports.plot = function plotMapbox(gd) {
     var calcData = gd.calcdata;
     var mapboxIds = fullLayout._subplots[MAPBOX];
 
+    if(mapboxgl.version !== constants.requiredVersion) {
+        throw new Error(constants.wrongVersionErrorMsg);
+    }
+
     var accessToken = findAccessToken(gd, mapboxIds);
     mapboxgl.accessToken = accessToken;
 
diff --git a/src/plots/mapbox/layers.js b/src/plots/mapbox/layers.js
index f6ed71a412e..cd535b75141 100644
--- a/src/plots/mapbox/layers.js
+++ b/src/plots/mapbox/layers.js
@@ -6,13 +6,11 @@
 * LICENSE file in the root directory of this source tree.
 */
 
-
 'use strict';
 
 var Lib = require('../../lib');
 var convertTextOpts = require('./convert_text_opts');
 
-
 function MapboxLayer(mapbox, index) {
     this.mapbox = mapbox;
     this.map = mapbox.map;
@@ -36,32 +34,26 @@ var proto = MapboxLayer.prototype;
 
 proto.update = function update(opts) {
     if(!this.visible) {
-
         // IMPORTANT: must create source before layer to not cause errors
         this.updateSource(opts);
         this.updateLayer(opts);
-    }
-    else if(this.needsNewSource(opts)) {
-
+    } else if(this.needsNewSource(opts)) {
         // IMPORTANT: must delete layer before source to not cause errors
         this.updateLayer(opts);
         this.updateSource(opts);
-    }
-    else if(this.needsNewLayer(opts)) {
+    } else if(this.needsNewLayer(opts)) {
         this.updateLayer(opts);
+    } else {
+        this.updateStyle(opts);
     }
 
-    this.updateStyle(opts);
-
     this.visible = isVisible(opts);
 };
 
 proto.needsNewSource = function(opts) {
-
     // for some reason changing layer to 'fill' or 'symbol'
     // w/o changing the source throws an exception in mapbox-gl 0.18 ;
     // stay safe and make new source on type changes
-
     return (
         this.sourceType !== opts.sourcetype ||
         this.source !== opts.source ||
@@ -93,29 +85,27 @@ proto.updateSource = function(opts) {
 
 proto.updateLayer = function(opts) {
     var map = this.map;
+    var convertedOpts = convertOpts(opts);
 
     if(map.getLayer(this.idLayer)) map.removeLayer(this.idLayer);
 
     this.layerType = opts.type;
 
-    if(!isVisible(opts)) return;
-
-    map.addLayer({
-        id: this.idLayer,
-        source: this.idSource,
-        'source-layer': opts.sourcelayer || '',
-        type: opts.type
-    }, opts.below);
-
-    // the only way to make a layer invisible is to remove it
-    var layoutOpts = { visibility: 'visible' };
-    this.mapbox.setOptions(this.idLayer, 'setLayoutProperty', layoutOpts);
+    if(isVisible(opts)) {
+        map.addLayer({
+            id: this.idLayer,
+            source: this.idSource,
+            'source-layer': opts.sourcelayer || '',
+            type: opts.type,
+            layout: convertedOpts.layout,
+            paint: convertedOpts.paint
+        }, opts.below);
+    }
 };
 
 proto.updateStyle = function(opts) {
-    var convertedOpts = convertOpts(opts);
-
     if(isVisible(opts)) {
+        var convertedOpts = convertOpts(opts);
         this.mapbox.setOptions(this.idLayer, 'setLayoutProperty', convertedOpts.layout);
         this.mapbox.setOptions(this.idLayer, 'setPaintProperty', convertedOpts.paint);
     }
@@ -123,7 +113,6 @@ proto.updateStyle = function(opts) {
 
 proto.dispose = function dispose() {
     var map = this.map;
-
     map.removeLayer(this.idLayer);
     map.removeSource(this.idSource);
 };
@@ -198,19 +187,18 @@ function convertOpts(opts) {
 }
 
 function convertSourceOpts(opts) {
-    var sourceType = opts.sourcetype,
-        source = opts.source,
-        sourceOpts = { type: sourceType },
-        isSourceAString = (typeof source === 'string'),
-        field;
-
-    if(sourceType === 'geojson') field = 'data';
-    else if(sourceType === 'vector') {
-        field = isSourceAString ? 'url' : 'tiles';
+    var sourceType = opts.sourcetype;
+    var source = opts.source;
+    var sourceOpts = {type: sourceType};
+    var field;
+
+    if(sourceType === 'geojson') {
+        field = 'data';
+    } else if(sourceType === 'vector') {
+        field = typeof source === 'string' ? 'url' : 'tiles';
     }
 
     sourceOpts[field] = source;
-
     return sourceOpts;
 }
 
diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js
index 608ef00ee34..4df6dfd4555 100644
--- a/src/plots/mapbox/mapbox.js
+++ b/src/plots/mapbox/mapbox.js
@@ -50,9 +50,7 @@ function Mapbox(opts) {
 var proto = Mapbox.prototype;
 
 module.exports = function createMapbox(opts) {
-    var mapbox = new Mapbox(opts);
-
-    return mapbox;
+    return new Mapbox(opts);
 };
 
 proto.plot = function(calcData, fullLayout, promises) {
@@ -76,8 +74,7 @@ proto.plot = function(calcData, fullLayout, promises) {
         promise = new Promise(function(resolve, reject) {
             self.createMap(calcData, fullLayout, resolve, reject);
         });
-    }
-    else {
+    } else {
         promise = new Promise(function(resolve, reject) {
             self.updateMap(calcData, fullLayout, resolve, reject);
         });
@@ -120,15 +117,14 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
     self.div.removeChild(controlContainer);
 
     // make sure canvas does not inherit left and top css
-    map._canvas.canvas.style.left = '0px';
-    map._canvas.canvas.style.top = '0px';
+    map._canvas.style.left = '0px';
+    map._canvas.style.top = '0px';
 
     self.rejectOnError(reject);
 
     map.once('load', function() {
         self.updateData(calcData);
         self.updateLayout(fullLayout);
-
         self.resolveOnRender(resolve);
     });
 
@@ -164,7 +160,6 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
         var bb = self.div.getBoundingClientRect();
 
         // some hackery to get Fx.hover to work
-
         evt.clientX = evt.point.x + bb.left;
         evt.clientY = evt.point.y + bb.top;
 
@@ -209,11 +204,17 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
 
         gd.emit('plotly_doubleclick', null);
     });
+
+    // define clear select on map creation, to keep one ref per map,
+    // so that map.on / map.off in updateFx works as expected
+    self.clearSelect = function() {
+        gd._fullLayout._zoomlayer.selectAll('.select-outline').remove();
+    };
 };
 
 proto.updateMap = function(calcData, fullLayout, resolve, reject) {
-    var self = this,
-        map = self.map;
+    var self = this;
+    var map = self.map;
 
     self.rejectOnError(reject);
 
@@ -223,29 +224,23 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
         self.styleObj = styleObj;
         map.setStyle(styleObj.style);
 
-        map.style.once('load', function() {
-
+        map.once('styledata', function() {
             // need to rebuild trace layers on reload
             // to avoid 'lost event' errors
             self.traceHash = {};
-
             self.updateData(calcData);
             self.updateLayout(fullLayout);
-
             self.resolveOnRender(resolve);
         });
-    }
-    else {
+    } else {
         self.updateData(calcData);
         self.updateLayout(fullLayout);
-
         self.resolveOnRender(resolve);
     }
 };
 
 proto.updateData = function(calcData) {
     var traceHash = this.traceHash;
-
     var traceObj, trace, i, j;
 
     // update or create trace objects
@@ -255,8 +250,9 @@ proto.updateData = function(calcData) {
         trace = calcTrace[0].trace;
         traceObj = traceHash[trace.uid];
 
-        if(traceObj) traceObj.update(calcTrace);
-        else if(trace._module) {
+        if(traceObj) {
+            traceObj.update(calcTrace);
+        } else if(trace._module) {
             traceHash[trace.uid] = trace._module.plot(this, calcTrace);
         }
     }
@@ -269,7 +265,6 @@ proto.updateData = function(calcData) {
 
         for(j = 0; j < calcData.length; j++) {
             trace = calcData[j][0].trace;
-
             if(id === trace.uid) continue id_loop;
         }
 
@@ -300,7 +295,8 @@ proto.resolveOnRender = function(resolve) {
     map.on('render', function onRender() {
         if(map.loaded()) {
             map.off('render', onRender);
-            resolve();
+            // resolve at end of render loop
+            setTimeout(resolve, 0);
         }
     });
 };
@@ -323,19 +319,15 @@ proto.createFramework = function(fullLayout) {
     var self = this;
 
     var div = self.div = document.createElement('div');
-
     div.id = self.uid;
     div.style.position = 'absolute';
-
     self.container.appendChild(div);
 
     // create mock x/y axes for hover routine
-
     self.xaxis = {
         _id: 'x',
         c2p: function(v) { return self.project(v).x; }
     };
-
     self.yaxis = {
         _id: 'y',
         c2p: function(v) { return self.project(v).y; }
@@ -376,6 +368,7 @@ proto.updateFx = function(fullLayout) {
 
     if(dragMode === 'select' || dragMode === 'lasso') {
         map.dragPan.disable();
+        map.on('zoomstart', self.clearSelect);
 
         var dragOptions = {
             element: self.div,
@@ -397,18 +390,16 @@ proto.updateFx = function(fullLayout) {
         dragElement.init(dragOptions);
     } else {
         map.dragPan.enable();
+        map.off('zoomstart', self.clearSelect);
         self.div.onmousedown = null;
     }
 };
 
 proto.updateFramework = function(fullLayout) {
-    var domain = fullLayout[this.id].domain,
-        size = fullLayout._size;
+    var domain = fullLayout[this.id].domain;
+    var size = fullLayout._size;
 
     var style = this.div.style;
-
-    // TODO Is this correct? It seems to get the map zoom level wrong?
-
     style.width = size.w * (domain.x[1] - domain.x[0]) + 'px';
     style.height = size.h * (domain.y[1] - domain.y[0]) + 'px';
     style.left = size.l + domain.x[0] * size.w + 'px';
@@ -422,10 +413,10 @@ proto.updateFramework = function(fullLayout) {
 };
 
 proto.updateLayers = function() {
-    var opts = this.opts,
-        layers = opts.layers,
-        layerList = this.layerList,
-        i;
+    var opts = this.opts;
+    var layers = opts.layers;
+    var layerList = this.layerList;
+    var i;
 
     // if the layer arrays don't match,
     // don't try to be smart,
@@ -441,8 +432,7 @@ proto.updateLayers = function() {
         for(i = 0; i < layers.length; i++) {
             layerList.push(createMapboxLayer(this, i, layers[i]));
         }
-    }
-    else {
+    } else {
         for(i = 0; i < layers.length; i++) {
             layerList[i].update(layers[i]);
         }
@@ -458,41 +448,15 @@ proto.destroy = function() {
 };
 
 proto.toImage = function() {
+    this.map.stop();
     return this.map.getCanvas().toDataURL();
 };
 
-// convenience wrapper to create blank GeoJSON sources
-// and avoid 'invalid GeoJSON' errors
-proto.initSource = function(idSource) {
-    var blank = {
-        type: 'geojson',
-        data: {
-            type: 'Feature',
-            geometry: {
-                type: 'Point',
-                coordinates: []
-            }
-        }
-    };
-
-    return this.map.addSource(idSource, blank);
-};
-
-// convenience wrapper to set data of GeoJSON sources
-proto.setSourceData = function(idSource, data) {
-    this.map.getSource(idSource).setData(data);
-};
-
 // convenience wrapper to create set multiple layer
 // 'layout' or 'paint options at once.
 proto.setOptions = function(id, methodName, opts) {
-    var map = this.map,
-        keys = Object.keys(opts);
-
-    for(var i = 0; i < keys.length; i++) {
-        var key = keys[i];
-
-        map[methodName](id, key, opts[key]);
+    for(var k in opts) {
+        this.map[methodName](id, k, opts[k]);
     }
 };
 
@@ -517,25 +481,25 @@ proto.getView = function() {
 };
 
 function getStyleObj(val) {
-    var styleValues = layoutAttributes.style.values,
-        styleDflt = layoutAttributes.style.dflt,
-        styleObj = {};
+    var styleValues = layoutAttributes.style.values;
+    var styleDflt = layoutAttributes.style.dflt;
+    var styleObj = {};
 
     if(Lib.isPlainObject(val)) {
         styleObj.id = val.id;
         styleObj.style = val;
-    }
-    else if(typeof val === 'string') {
+    } else if(typeof val === 'string') {
         styleObj.id = val;
         styleObj.style = (styleValues.indexOf(val) !== -1) ?
              convertStyleVal(val) :
              val;
-    }
-    else {
+    } else {
         styleObj.id = styleDflt;
         styleObj.style = convertStyleVal(styleDflt);
     }
 
+    styleObj.transition = {duration: 0, delay: 0};
+
     return styleObj;
 }
 
diff --git a/src/snapshot/helpers.js b/src/snapshot/helpers.js
index 62057db8af1..ad76c746da3 100644
--- a/src/snapshot/helpers.js
+++ b/src/snapshot/helpers.js
@@ -10,12 +10,13 @@
 'use strict';
 
 exports.getDelay = function(fullLayout) {
-
-    // polar clears fullLayout._has for some reason
     if(!fullLayout._has) return 0;
 
-    // maybe we should add a 'gl' (and 'svg') layoutCategory ??
-    return (fullLayout._has('gl3d') || fullLayout._has('gl2d')) ? 500 : 0;
+    return (
+        fullLayout._has('gl3d') ||
+        fullLayout._has('gl2d') ||
+        fullLayout._has('mapbox')
+    ) ? 500 : 0;
 };
 
 exports.getRedrawFunc = function(gd) {
diff --git a/src/traces/scattermapbox/attributes.js b/src/traces/scattermapbox/attributes.js
index 1c9005ec2ac..49a20454a0e 100644
--- a/src/traces/scattermapbox/attributes.js
+++ b/src/traces/scattermapbox/attributes.js
@@ -20,7 +20,6 @@ var overrideAll = require('../../plot_api/edit_types').overrideAll;
 var lineAttrs = scatterGeoAttrs.line;
 var markerAttrs = scatterGeoAttrs.marker;
 
-
 module.exports = overrideAll({
     lon: scatterGeoAttrs.lon,
     lat: scatterGeoAttrs.lat,
@@ -108,14 +107,10 @@ module.exports = overrideAll({
     textposition: mapboxAttrs.layers.symbol.textposition,
 
     selected: {
-        marker: {
-            opacity: scatterAttrs.selected.marker.opacity
-        }
+        marker: scatterAttrs.selected.marker
     },
     unselected: {
-        marker: {
-            opacity: scatterAttrs.unselected.marker.opacity
-        }
+        marker: scatterAttrs.unselected.marker
     },
 
     hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
diff --git a/src/traces/scattermapbox/convert.js b/src/traces/scattermapbox/convert.js
index e93511580ef..34439328ed2 100644
--- a/src/traces/scattermapbox/convert.js
+++ b/src/traces/scattermapbox/convert.js
@@ -6,7 +6,6 @@
 * LICENSE file in the root directory of this source tree.
 */
 
-
 'use strict';
 
 var isNumeric = require('fast-isnumeric');
@@ -16,30 +15,26 @@ var BADNUM = require('../../constants/numerical').BADNUM;
 var geoJsonUtils = require('../../lib/geojson_utils');
 
 var Colorscale = require('../../components/colorscale');
+var Drawing = require('../../components/drawing');
 var makeBubbleSizeFn = require('../scatter/make_bubble_size_func');
 var subTypes = require('../scatter/subtypes');
 var convertTextOpts = require('../../plots/mapbox/convert_text_opts');
-var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
-
-var COLOR_PROP = 'circle-color';
-var SIZE_PROP = 'circle-radius';
-var OPACITY_PROP = 'circle-opacity';
 
 module.exports = function convert(calcTrace) {
     var trace = calcTrace[0].trace;
 
-    var isVisible = (trace.visible === true),
-        hasFill = (trace.fill !== 'none'),
-        hasLines = subTypes.hasLines(trace),
-        hasMarkers = subTypes.hasMarkers(trace),
-        hasText = subTypes.hasText(trace),
-        hasCircles = (hasMarkers && trace.marker.symbol === 'circle'),
-        hasSymbols = (hasMarkers && trace.marker.symbol !== 'circle');
+    var isVisible = (trace.visible === true);
+    var hasFill = (trace.fill !== 'none');
+    var hasLines = subTypes.hasLines(trace);
+    var hasMarkers = subTypes.hasMarkers(trace);
+    var hasText = subTypes.hasText(trace);
+    var hasCircles = (hasMarkers && trace.marker.symbol === 'circle');
+    var hasSymbols = (hasMarkers && trace.marker.symbol !== 'circle');
 
-    var fill = initContainer(),
-        line = initContainer(),
-        circle = initContainer(),
-        symbol = initContainer();
+    var fill = initContainer();
+    var line = initContainer();
+    var circle = initContainer();
+    var symbol = initContainer();
 
     var opts = {
         fill: fill,
@@ -80,18 +75,14 @@ module.exports = function convert(calcTrace) {
     }
 
     if(hasCircles) {
-        var hash = {};
-        hash[COLOR_PROP] = {};
-        hash[SIZE_PROP] = {};
-        hash[OPACITY_PROP] = {};
-
-        circle.geojson = makeCircleGeoJSON(calcTrace, hash);
+        var circleOpts = makeCircleOpts(calcTrace);
+        circle.geojson = circleOpts.geojson;
         circle.layout.visibility = 'visible';
 
         Lib.extendFlat(circle.paint, {
-            'circle-opacity': calcCircleOpacity(trace, hash),
-            'circle-color': calcCircleColor(trace, hash),
-            'circle-radius': calcCircleRadius(trace, hash)
+            'circle-color': circleOpts.mcc,
+            'circle-radius': circleOpts.mrc,
+            'circle-opacity': circleOpts.mo
         });
     }
 
@@ -121,6 +112,8 @@ module.exports = function convert(calcTrace) {
             var iconSize = (trace.marker || {}).size,
                 textOpts = convertTextOpts(trace.textposition, iconSize);
 
+            // all data-driven below !!
+
             Lib.extendFlat(symbol.layout, {
                 'text-size': trace.textfont.size,
                 'text-anchor': textOpts.anchor,
@@ -148,107 +141,99 @@ function initContainer() {
     };
 }
 
-// N.B. `hash` is mutated here
-//
-// The `hash` object contains mapping between values
-// (e.g. calculated marker.size and marker.color items)
-// and their index in the input arrayOk attributes.
-//
-// GeoJSON features have their 'data-driven' properties set to
-// the index of the first value found in the data.
-//
-// The `hash` object is then converted to mapbox `stops` arrays
-// mapping index to value.
-//
-// The solution prove to be more robust than trying to generate
-// `stops` arrays from scale functions.
-//
-// TODO axe this when we bump mapbox-gl and rewrite this using
-// "identity" property functions.
-// See https://github.com/plotly/plotly.js/pull/1543
-//
-function makeCircleGeoJSON(calcTrace, hash) {
+function makeCircleOpts(calcTrace) {
     var trace = calcTrace[0].trace;
     var marker = trace.marker;
+    var selectedpoints = trace.selectedpoints;
+    var arrayColor = Array.isArray(marker.color);
+    var arraySize = Array.isArray(marker.size);
+    var arrayOpacity = Array.isArray(marker.opacity);
+    var i;
+
+    function addTraceOpacity(o) { return trace.opacity * o; }
+
+    function size2radius(s) { return s / 2; }
 
     var colorFn;
-    if(Colorscale.hasColorscale(trace, 'marker')) {
-        colorFn = Colorscale.makeColorScaleFunc(
-             Colorscale.extractScale(marker.colorscale, marker.cmin, marker.cmax)
-         );
-    } else if(Array.isArray(marker.color)) {
-        colorFn = Lib.identity;
+    if(arrayColor) {
+        if(Colorscale.hasColorscale(trace, 'marker')) {
+            colorFn = Colorscale.makeColorScaleFunc(
+                 Colorscale.extractScale(marker.colorscale, marker.cmin, marker.cmax)
+             );
+        } else {
+            colorFn = Lib.identity;
+        }
     }
 
     var sizeFn;
-    if(subTypes.isBubble(trace)) {
+    if(arraySize) {
         sizeFn = makeBubbleSizeFn(trace);
     }
 
-    var combineOpacities;
-    if(trace.selectedpoints) {
-        combineOpacities = function(d, mo) {
-            return trace.opacity * mo * (d.selected ? 1 : DESELECTDIM);
-        };
-    } else {
-        combineOpacities = function(d, mo) {
-            return trace.opacity * mo;
-        };
-    }
-
     var opacityFn;
-    if(Array.isArray(marker.opacity)) {
-        opacityFn = function(d) {
-            var mo = isNumeric(d.mo) ? +Lib.constrain(d.mo, 0, 1) : 0;
-            return combineOpacities(d, mo);
-        };
-    } else if(trace.selectedpoints) {
-        opacityFn = function(d) {
-            return combineOpacities(d, marker.opacity);
+    if(arrayOpacity) {
+        opacityFn = function(mo) {
+            var mo2 = isNumeric(mo) ? +Lib.constrain(mo, 0, 1) : 0;
+            return addTraceOpacity(mo2);
         };
     }
 
-    // Translate vals in trace arrayOk containers
-    // into a val-to-index hash object
-    function translate(props, key, val, index) {
-        if(hash[key][val] === undefined) hash[key][val] = index;
-
-        props[key] = hash[key][val];
-    }
-
     var features = [];
-
-    for(var i = 0; i < calcTrace.length; i++) {
+    for(i = 0; i < calcTrace.length; i++) {
         var calcPt = calcTrace[i];
         var lonlat = calcPt.lonlat;
 
         if(isBADNUM(lonlat)) continue;
 
         var props = {};
-        if(colorFn) {
-            var mcc = calcPt.mcc = colorFn(calcPt.mc);
-            translate(props, COLOR_PROP, mcc, i);
-        }
-        if(sizeFn) {
-            translate(props, SIZE_PROP, sizeFn(calcPt.ms), i);
-        }
-        if(opacityFn) {
-            translate(props, OPACITY_PROP, opacityFn(calcPt), i);
-        }
+        if(colorFn) props.mcc = calcPt.mcc = colorFn(calcPt.mc);
+        if(sizeFn) props.mrc = calcPt.mrc = sizeFn(calcPt.ms);
+        if(opacityFn) props.mo = opacityFn(calcPt.mo);
+        if(selectedpoints) props.selected = calcPt.selected || 0;
 
         features.push({
             type: 'Feature',
-            geometry: {
-                type: 'Point',
-                coordinates: lonlat
-            },
+            geometry: {type: 'Point', coordinates: lonlat},
             properties: props
         });
     }
 
+    var fns;
+    if(selectedpoints) {
+        fns = Drawing.makeSelectedPointStyleFns(trace);
+
+        for(i = 0; i < features.length; i++) {
+            var d = features[i].properties;
+
+            var mo2 = fns.opacityFn(d);
+            if(mo2 !== undefined) d.mo = addTraceOpacity(mo2);
+            else if(d.mo === undefined) d.mo = addTraceOpacity(marker.opacity);
+
+            if(fns.colorFn) {
+                var mc2 = fns.colorFn(d);
+                if(mc2) d.mcc = mc2;
+                else if(!d.mcc) d.mcc = marker.color;
+            }
+
+            if(fns.sizeFn) {
+                var mrc2 = fns.sizeFn(d);
+                if(mrc2 !== undefined) d.mrc = mrc2;
+                else if(d.mrc === undefined) d.mrc = size2radius(marker.size);
+            }
+        }
+    }
+
     return {
-        type: 'FeatureCollection',
-        features: features
+        geojson: {type: 'FeatureCollection', features: features},
+        mcc: arrayColor || (fns && fns.colorFn) ?
+            {type: 'identity', property: 'mcc'} :
+            marker.color,
+        mrc: arraySize || (fns && fns.sizeFn) ?
+            {type: 'identity', property: 'mrc'} :
+            size2radius(marker.size),
+        mo: arrayOpacity || selectedpoints ?
+            {type: 'identity', property: 'mo'} :
+            addTraceOpacity(marker.opacity)
     };
 }
 
@@ -293,84 +278,6 @@ function makeSymbolGeoJSON(calcTrace) {
     };
 }
 
-function calcCircleColor(trace, hash) {
-    var marker = trace.marker,
-        out;
-
-    if(Array.isArray(marker.color)) {
-        var vals = Object.keys(hash[COLOR_PROP]),
-            stops = [];
-
-        for(var i = 0; i < vals.length; i++) {
-            var val = vals[i];
-
-            stops.push([ hash[COLOR_PROP][val], val ]);
-        }
-
-        out = {
-            property: COLOR_PROP,
-            stops: stops
-        };
-
-    }
-    else {
-        out = marker.color;
-    }
-
-    return out;
-}
-
-function calcCircleRadius(trace, hash) {
-    var marker = trace.marker,
-        out;
-
-    if(Array.isArray(marker.size)) {
-        var vals = Object.keys(hash[SIZE_PROP]),
-            stops = [];
-
-        for(var i = 0; i < vals.length; i++) {
-            var val = vals[i];
-
-            stops.push([ hash[SIZE_PROP][val], +val ]);
-        }
-
-        out = {
-            property: SIZE_PROP,
-            stops: stops.sort(ascending)
-        };
-    }
-    else {
-        out = marker.size / 2;
-    }
-
-    return out;
-}
-
-function calcCircleOpacity(trace, hash) {
-    var marker = trace.marker;
-    var out;
-
-    if(Array.isArray(marker.opacity) || trace.selectedpoints) {
-        var vals = Object.keys(hash[OPACITY_PROP]);
-        var stops = [];
-
-        for(var i = 0; i < vals.length; i++) {
-            var val = vals[i];
-            stops.push([hash[OPACITY_PROP][val], +val]);
-        }
-
-        out = {
-            property: OPACITY_PROP,
-            stops: stops.sort(ascending)
-        };
-    }
-    else {
-        out = trace.opacity * marker.opacity;
-    }
-
-    return out;
-}
-
 function getFillFunc(attr) {
     if(Array.isArray(attr)) {
         return function(v) { return v; };
@@ -385,8 +292,6 @@ function getFillFunc(attr) {
 
 function blankFillFunc() { return ''; }
 
-function ascending(a, b) { return a[0] - b[0]; }
-
 // only need to check lon (OR lat)
 function isBADNUM(lonlat) {
     return lonlat[0] === BADNUM;
diff --git a/src/traces/scattermapbox/defaults.js b/src/traces/scattermapbox/defaults.js
index 210f9e1d47b..c748d804df4 100644
--- a/src/traces/scattermapbox/defaults.js
+++ b/src/traces/scattermapbox/defaults.js
@@ -39,7 +39,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
     }
 
     if(subTypes.hasMarkers(traceOut)) {
-        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true, noSelect: true});
+        handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true});
 
         // array marker.size and marker.color are only supported with circles
 
diff --git a/src/traces/scattermapbox/hover.js b/src/traces/scattermapbox/hover.js
index a1e632cc907..0f21d133aae 100644
--- a/src/traces/scattermapbox/hover.js
+++ b/src/traces/scattermapbox/hover.js
@@ -10,20 +10,22 @@
 'use strict';
 
 var Fx = require('../../components/fx');
+var Lib = require('../../lib');
 var getTraceColor = require('../scatter/get_trace_color');
 var fillHoverText = require('../scatter/fill_hover_text');
 var BADNUM = require('../../constants/numerical').BADNUM;
 
 module.exports = function hoverPoints(pointData, xval, yval) {
-    var cd = pointData.cd,
-        trace = cd[0].trace,
-        xa = pointData.xa,
-        ya = pointData.ya;
+    var cd = pointData.cd;
+    var trace = cd[0].trace;
+    var xa = pointData.xa;
+    var ya = pointData.ya;
+    var subplot = pointData.subplot;
 
     // compute winding number about [-180, 180] globe
     var winding = (xval >= 0) ?
-            Math.floor((xval + 180) / 360) :
-            Math.ceil((xval - 180) / 360);
+        Math.floor((xval + 180) / 360) :
+        Math.ceil((xval - 180) / 360);
 
     // shift longitude to [-180, 180] to determine closest point
     var lonShift = winding * 360;
@@ -31,11 +33,13 @@ module.exports = function hoverPoints(pointData, xval, yval) {
 
     function distFn(d) {
         var lonlat = d.lonlat;
-
         if(lonlat[0] === BADNUM) return Infinity;
 
-        var dx = Math.abs(xa.c2p(lonlat) - xa.c2p([xval2, lonlat[1]]));
-        var dy = Math.abs(ya.c2p(lonlat) - ya.c2p([lonlat[0], yval]));
+        var lon = Lib.wrap180(lonlat[0]);
+        var lat = lonlat[1];
+        var pt = subplot.project([lon, lat]);
+        var dx = pt.x - xa.c2p([xval2, lat]);
+        var dy = pt.y - ya.c2p([lon, yval]);
         var rad = Math.max(3, d.mrc || 0);
 
         return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - 3 / rad);
@@ -46,14 +50,14 @@ module.exports = function hoverPoints(pointData, xval, yval) {
     // skip the rest (for this trace) if we didn't find a close point
     if(pointData.index === false) return;
 
-    var di = cd[pointData.index],
-        lonlat = di.lonlat,
-        lonlatShifted = [lonlat[0] + lonShift, lonlat[1]];
+    var di = cd[pointData.index];
+    var lonlat = di.lonlat;
+    var lonlatShifted = [Lib.wrap180(lonlat[0]) + lonShift, lonlat[1]];
 
     // shift labels back to original winded globe
-    var xc = xa.c2p(lonlatShifted),
-        yc = ya.c2p(lonlatShifted),
-        rad = di.mrc || 1;
+    var xc = xa.c2p(lonlatShifted);
+    var yc = ya.c2p(lonlatShifted);
+    var rad = di.mrc || 1;
 
     pointData.x0 = xc - rad;
     pointData.x1 = xc + rad;
@@ -72,7 +76,6 @@ function getExtraText(trace, di, labels) {
     var isAll = parts.indexOf('all') !== -1;
     var hasLon = parts.indexOf('lon') !== -1;
     var hasLat = parts.indexOf('lat') !== -1;
-
     var lonlat = di.lonlat;
     var text = [];
 
diff --git a/src/traces/scattermapbox/plot.js b/src/traces/scattermapbox/plot.js
index 82fc1439587..cba49fbee18 100644
--- a/src/traces/scattermapbox/plot.js
+++ b/src/traces/scattermapbox/plot.js
@@ -6,56 +6,29 @@
 * LICENSE file in the root directory of this source tree.
 */
 
-
 'use strict';
 
 var convert = require('./convert');
 
-
-function ScatterMapbox(mapbox, uid) {
-    this.mapbox = mapbox;
-    this.map = mapbox.map;
-
+function ScatterMapbox(subplot, uid) {
+    this.subplot = subplot;
     this.uid = uid;
 
-    this.idSourceFill = uid + '-source-fill';
-    this.idSourceLine = uid + '-source-line';
-    this.idSourceCircle = uid + '-source-circle';
-    this.idSourceSymbol = uid + '-source-symbol';
-
-    this.idLayerFill = uid + '-layer-fill';
-    this.idLayerLine = uid + '-layer-line';
-    this.idLayerCircle = uid + '-layer-circle';
-    this.idLayerSymbol = uid + '-layer-symbol';
-
-    this.mapbox.initSource(this.idSourceFill);
-    this.mapbox.initSource(this.idSourceLine);
-    this.mapbox.initSource(this.idSourceCircle);
-    this.mapbox.initSource(this.idSourceSymbol);
-
-    this.map.addLayer({
-        id: this.idLayerFill,
-        source: this.idSourceFill,
-        type: 'fill'
-    });
+    this.sourceIds = {
+        fill: uid + '-source-fill',
+        line: uid + '-source-line',
+        circle: uid + '-source-circle',
+        symbol: uid + '-source-symbol'
+    };
 
-    this.map.addLayer({
-        id: this.idLayerLine,
-        source: this.idSourceLine,
-        type: 'line'
-    });
-
-    this.map.addLayer({
-        id: this.idLayerCircle,
-        source: this.idSourceCircle,
-        type: 'circle'
-    });
+    this.layerIds = {
+        fill: uid + '-layer-fill',
+        line: uid + '-layer-line',
+        circle: uid + '-layer-circle',
+        symbol: uid + '-layer-symbol'
+    };
 
-    this.map.addLayer({
-        id: this.idLayerSymbol,
-        source: this.idSourceSymbol,
-        type: 'symbol'
-    });
+    this.order = ['fill', 'line', 'circle', 'symbol'];
 
     // We could merge the 'fill' source with the 'line' source and
     // the 'circle' source with the 'symbol' source if ever having
@@ -64,33 +37,43 @@ function ScatterMapbox(mapbox, uid) {
 
 var proto = ScatterMapbox.prototype;
 
-proto.update = function update(calcTrace) {
-    var mapbox = this.mapbox;
-    var opts = convert(calcTrace);
+proto.addSource = function(k, opts) {
+    this.subplot.map.addSource(this.sourceIds[k], {
+        type: 'geojson',
+        data: opts.geojson
+    });
+};
 
-    mapbox.setOptions(this.idLayerFill, 'setLayoutProperty', opts.fill.layout);
-    mapbox.setOptions(this.idLayerLine, 'setLayoutProperty', opts.line.layout);
-    mapbox.setOptions(this.idLayerCircle, 'setLayoutProperty', opts.circle.layout);
-    mapbox.setOptions(this.idLayerSymbol, 'setLayoutProperty', opts.symbol.layout);
+proto.setSourceData = function(k, opts) {
+    this.subplot.map
+        .getSource(this.sourceIds[k])
+        .setData(opts.geojson);
+};
 
-    if(isVisible(opts.fill)) {
-        mapbox.setSourceData(this.idSourceFill, opts.fill.geojson);
-        mapbox.setOptions(this.idLayerFill, 'setPaintProperty', opts.fill.paint);
-    }
+proto.addLayer = function(k, opts) {
+    this.subplot.map.addLayer({
+        type: k,
+        id: this.layerIds[k],
+        source: this.sourceIds[k],
+        layout: opts.layout,
+        paint: opts.paint
+    });
+};
 
-    if(isVisible(opts.line)) {
-        mapbox.setSourceData(this.idSourceLine, opts.line.geojson);
-        mapbox.setOptions(this.idLayerLine, 'setPaintProperty', opts.line.paint);
-    }
+proto.update = function update(calcTrace) {
+    var subplot = this.subplot;
+    var optsAll = convert(calcTrace);
 
-    if(isVisible(opts.circle)) {
-        mapbox.setSourceData(this.idSourceCircle, opts.circle.geojson);
-        mapbox.setOptions(this.idLayerCircle, 'setPaintProperty', opts.circle.paint);
-    }
+    for(var i = 0; i < this.order.length; i++) {
+        var k = this.order[i];
+        var opts = optsAll[k];
 
-    if(isVisible(opts.symbol)) {
-        mapbox.setSourceData(this.idSourceSymbol, opts.symbol.geojson);
-        mapbox.setOptions(this.idLayerSymbol, 'setPaintProperty', opts.symbol.paint);
+        subplot.setOptions(this.layerIds[k], 'setLayoutProperty', opts.layout);
+
+        if(opts.layout.visibility === 'visible') {
+            this.setSourceData(k, opts);
+            subplot.setOptions(this.layerIds[k], 'setPaintProperty', opts.paint);
+        }
     }
 
     // link ref for quick update during selections
@@ -98,28 +81,30 @@ proto.update = function update(calcTrace) {
 };
 
 proto.dispose = function dispose() {
-    var map = this.map;
-
-    map.removeLayer(this.idLayerFill);
-    map.removeLayer(this.idLayerLine);
-    map.removeLayer(this.idLayerCircle);
-    map.removeLayer(this.idLayerSymbol);
+    var map = this.subplot.map;
 
-    map.removeSource(this.idSourceFill);
-    map.removeSource(this.idSourceLine);
-    map.removeSource(this.idSourceCircle);
-    map.removeSource(this.idSourceSymbol);
+    for(var i = 0; i < this.order.length; i++) {
+        var k = this.order[i];
+        map.removeLayer(this.layerIds[k]);
+        map.removeSource(this.sourceIds[k]);
+    }
 };
 
-function isVisible(layerOpts) {
-    return layerOpts.layout.visibility === 'visible';
-}
-
-module.exports = function createScatterMapbox(mapbox, calcTrace) {
+module.exports = function createScatterMapbox(subplot, calcTrace) {
     var trace = calcTrace[0].trace;
+    var scatterMapbox = new ScatterMapbox(subplot, trace.uid);
+    var optsAll = convert(calcTrace);
+
+    for(var i = 0; i < scatterMapbox.order.length; i++) {
+        var k = scatterMapbox.order[i];
+        var opts = optsAll[k];
 
-    var scatterMapbox = new ScatterMapbox(mapbox, trace.uid);
-    scatterMapbox.update(calcTrace);
+        scatterMapbox.addSource(k, opts);
+        scatterMapbox.addLayer(k, opts);
+    }
+
+    // link ref for quick update during selections
+    calcTrace[0].trace._glTrace = scatterMapbox;
 
     return scatterMapbox;
 };
diff --git a/src/traces/scattermapbox/select.js b/src/traces/scattermapbox/select.js
index cce4d709c8c..4f6e151d6a9 100644
--- a/src/traces/scattermapbox/select.js
+++ b/src/traces/scattermapbox/select.js
@@ -6,9 +6,9 @@
 * LICENSE file in the root directory of this source tree.
 */
 
-
 'use strict';
 
+var Lib = require('../../lib');
 var subtypes = require('../scatter/subtypes');
 
 module.exports = function selectPoints(searchInfo, polygon) {
@@ -17,8 +17,7 @@ module.exports = function selectPoints(searchInfo, polygon) {
     var ya = searchInfo.yaxis;
     var selection = [];
     var trace = cd[0].trace;
-
-    var di, lonlat, x, y, i;
+    var i;
 
     if(!subtypes.hasMarkers(trace)) return [];
 
@@ -28,12 +27,12 @@ module.exports = function selectPoints(searchInfo, polygon) {
         }
     } else {
         for(i = 0; i < cd.length; i++) {
-            di = cd[i];
-            lonlat = di.lonlat;
-            x = xa.c2p(lonlat);
-            y = ya.c2p(lonlat);
+            var di = cd[i];
+            var lonlat = di.lonlat;
+            var lonlat2 = [Lib.wrap180(lonlat[0]), lonlat[1]];
+            var xy = [xa.c2p(lonlat2), ya.c2p(lonlat2)];
 
-            if(polygon.contains([x, y])) {
+            if(polygon.contains(xy)) {
                 selection.push({
                     pointNumber: i,
                     lon: lonlat[0],
diff --git a/tasks/noci_test.sh b/tasks/noci_test.sh
index c92ea7dd62c..70e53415410 100755
--- a/tasks/noci_test.sh
+++ b/tasks/noci_test.sh
@@ -1,13 +1,22 @@
 #! /bin/bash
 
 EXIT_STATE=0
+root=$(dirname $0)/..
 
-# tests that aren't run on CI
+# tests that aren't run on CI (yet)
 
 # jasmine specs with @noCI tag
 npm run test-jasmine -- --tags=noCI --nowatch || EXIT_STATE=$?
 
 # mapbox image tests take too much resources on CI
-npm run test-image -- mapbox_* --queue || EXIT_STATE=$?
+
+# since the update to mapbox-gl@0.44.0, we must use 'new' image-exporter
+# as mapbox-gl versions >0.22.1 aren't supported on nw.js@0.12 used in the
+# 'old' image server
+$root/../image-exporter/bin/plotly-graph-exporter.js $root/test/image/mocks/mapbox_* \
+    --plotly $root/build/plotly.js \
+    --mapbox-access-token "pk.eyJ1IjoiZXRwaW5hcmQiLCJhIjoiY2luMHIzdHE0MGFxNXVubTRxczZ2YmUxaCJ9.hwWZful0U2CQxit4ItNsiQ" \
+    --output-dir $root/test/image/baselines/ \
+    --verbose
 
 exit $EXIT_STATE
diff --git a/test/image/baselines/mapbox_0.png b/test/image/baselines/mapbox_0.png
index c5378b6cf85..68b8dee4b15 100644
Binary files a/test/image/baselines/mapbox_0.png and b/test/image/baselines/mapbox_0.png differ
diff --git a/test/image/baselines/mapbox_angles.png b/test/image/baselines/mapbox_angles.png
index 54eae19c19f..0fabd18879c 100644
Binary files a/test/image/baselines/mapbox_angles.png and b/test/image/baselines/mapbox_angles.png differ
diff --git a/test/image/baselines/mapbox_bubbles-text.png b/test/image/baselines/mapbox_bubbles-text.png
index 01a663867f0..a36d9aba981 100644
Binary files a/test/image/baselines/mapbox_bubbles-text.png and b/test/image/baselines/mapbox_bubbles-text.png differ
diff --git a/test/image/baselines/mapbox_bubbles.png b/test/image/baselines/mapbox_bubbles.png
index ae38027605e..0fe4978231d 100644
Binary files a/test/image/baselines/mapbox_bubbles.png and b/test/image/baselines/mapbox_bubbles.png differ
diff --git a/test/image/baselines/mapbox_connectgaps.png b/test/image/baselines/mapbox_connectgaps.png
index af554d97a38..cb6e1eec4cc 100644
Binary files a/test/image/baselines/mapbox_connectgaps.png and b/test/image/baselines/mapbox_connectgaps.png differ
diff --git a/test/image/baselines/mapbox_custom-style.png b/test/image/baselines/mapbox_custom-style.png
index 23d2ed05b92..dca5187424e 100644
Binary files a/test/image/baselines/mapbox_custom-style.png and b/test/image/baselines/mapbox_custom-style.png differ
diff --git a/test/image/baselines/mapbox_fill.png b/test/image/baselines/mapbox_fill.png
index 98fc78f86bd..0291fcc60e9 100644
Binary files a/test/image/baselines/mapbox_fill.png and b/test/image/baselines/mapbox_fill.png differ
diff --git a/test/image/baselines/mapbox_layers.png b/test/image/baselines/mapbox_layers.png
index 95da9fd0db4..edfa60da6eb 100644
Binary files a/test/image/baselines/mapbox_layers.png and b/test/image/baselines/mapbox_layers.png differ
diff --git a/test/image/baselines/mapbox_symbol-text.png b/test/image/baselines/mapbox_symbol-text.png
index 1ec7cd40459..d7f3ddf8233 100644
Binary files a/test/image/baselines/mapbox_symbol-text.png and b/test/image/baselines/mapbox_symbol-text.png differ
diff --git a/test/image/mocks/mapbox_bubbles.json b/test/image/mocks/mapbox_bubbles.json
index c35b8eb247f..904de36145d 100644
--- a/test/image/mocks/mapbox_bubbles.json
+++ b/test/image/mocks/mapbox_bubbles.json
@@ -46,6 +46,29 @@
           "opacity": 0.5
         }
       }
+    },
+    {
+      "type": "scattermapbox",
+      "selectedpoints": [0, 2],
+      "mode": "markers",
+      "lon": [-10, -20, -30],
+      "lat": [-10, -20, -30],
+      "marker": {"size": 20},
+      "selected": {
+        "marker": {
+          "size": 40,
+          "color": "green"
+        }
+      },
+      "unselected": {
+        "marker": {
+          "size": 10,
+          "color": "red"
+        }
+      },
+      "hoverlabel": {
+        "bgcolor": "grey"
+      }
     }
   ],
   "layout": {
@@ -56,6 +79,7 @@
     "showlegend": false,
     "height": 450,
     "width": 600,
-    "margin": {"l": 10}
+    "margin": {"l": 10},
+    "dragmode": "select"
   }
 }
diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js
index 3ca613b6b36..60430c94915 100644
--- a/test/jasmine/tests/mapbox_test.js
+++ b/test/jasmine/tests/mapbox_test.js
@@ -265,7 +265,7 @@ describe('mapbox credentials', function() {
         var cnt = 0;
         var msg = [
             'An API access token is required to use Mapbox GL.',
-            'See https://www.mapbox.com/developers/api/#access-tokens'
+            'See https://www.mapbox.com/api-documentation/#access-tokens'
         ].join(' ');
 
         Plotly.plot(gd, [{
@@ -405,10 +405,12 @@ describe('@noCI, mapbox plots', function() {
         function assertMarkerColor(expectations) {
             return new Promise(function(resolve) {
                 setTimeout(function() {
-                    var colors = getStyle(gd, 'circle', 'circle-color');
+                    var objs = getStyle(gd, 'circle', 'circle-color');
 
                     expectations.forEach(function(expected, i) {
-                        expect(colors[i]).toBeCloseToArray(expected);
+                        var obj = objs[i];
+                        var rgba = [obj.r, obj.g, obj.b, obj.a];
+                        expect(rgba).toBeCloseToArray(expected);
                     });
 
                     resolve();
@@ -459,10 +461,9 @@ describe('@noCI, mapbox plots', function() {
             relayoutCnt++;
         });
 
-        function assertLayout(style, center, zoom, dims) {
+        function assertLayout(center, zoom, dims) {
             var mapInfo = getMapInfo(gd);
 
-            expect(mapInfo.style.name).toEqual(style);
             expect([mapInfo.center.lng, mapInfo.center.lat])
                 .toBeCloseToArray(center);
             expect(mapInfo.zoom).toBeCloseTo(zoom);
@@ -473,13 +474,13 @@ describe('@noCI, mapbox plots', function() {
             });
         }
 
-        assertLayout('Mapbox Dark', [-4.710, 19.475], 1.234, [80, 100, 908, 270]);
+        assertLayout([-4.710, 19.475], 1.234, [80, 100, 908, 270]);
 
         Plotly.relayout(gd, 'mapbox.center', { lon: 0, lat: 0 }).then(function() {
             expect(restyleCnt).toEqual(0);
             expect(relayoutCnt).toEqual(1);
 
-            assertLayout('Mapbox Dark', [0, 0], 1.234, [80, 100, 908, 270]);
+            assertLayout([0, 0], 1.234, [80, 100, 908, 270]);
 
             return Plotly.relayout(gd, 'mapbox.zoom', '6');
         })
@@ -487,31 +488,43 @@ describe('@noCI, mapbox plots', function() {
             expect(restyleCnt).toEqual(0);
             expect(relayoutCnt).toEqual(2);
 
-            assertLayout('Mapbox Dark', [0, 0], 6, [80, 100, 908, 270]);
+            assertLayout([0, 0], 6, [80, 100, 908, 270]);
 
-            return Plotly.relayout(gd, 'mapbox.style', 'light');
+            return Plotly.relayout(gd, 'mapbox.domain.x', [0, 0.5]);
         })
         .then(function() {
             expect(restyleCnt).toEqual(0);
             expect(relayoutCnt).toEqual(3);
 
-            assertLayout('Mapbox Light', [0, 0], 6, [80, 100, 908, 270]);
+            assertLayout([0, 0], 6, [80, 100, 454, 270]);
 
-            return Plotly.relayout(gd, 'mapbox.domain.x', [0, 0.5]);
+            return Plotly.relayout(gd, 'mapbox.domain.y[0]', 0.5);
         })
         .then(function() {
             expect(restyleCnt).toEqual(0);
             expect(relayoutCnt).toEqual(4);
 
-            assertLayout('Mapbox Light', [0, 0], 6, [80, 100, 454, 270]);
+            assertLayout([0, 0], 6, [80, 100, 454, 135]);
+        })
+        .catch(failTest)
+        .then(done);
+    }, LONG_TIMEOUT_INTERVAL);
 
-            return Plotly.relayout(gd, 'mapbox.domain.y[0]', 0.5);
+    it('should be able to relayout the map style', function(done) {
+        function assertLayout(style) {
+            var mapInfo = getMapInfo(gd);
+            expect(mapInfo.style.name).toEqual(style);
+        }
+
+        assertLayout('Mapbox Dark');
+
+        Plotly.relayout(gd, 'mapbox.style', 'light').then(function() {
+            assertLayout('Mapbox Light');
+
+            return Plotly.relayout(gd, 'mapbox.style', 'dark');
         })
         .then(function() {
-            expect(restyleCnt).toEqual(0);
-            expect(relayoutCnt).toEqual(5);
-
-            assertLayout('Mapbox Light', [0, 0], 6, [80, 100, 454, 135]);
+            assertLayout('Mapbox Dark');
         })
         .catch(failTest)
         .then(done);
@@ -567,7 +580,12 @@ describe('@noCI, mapbox plots', function() {
             return new Promise(function(resolve) {
                 setTimeout(function() {
                     Object.keys(expectations).forEach(function(k) {
-                        expect(((layer || {}).paint || {})[k]).toEqual(expectations[k]);
+                        try {
+                            var obj = layer.paint._values[k].value.value;
+                            expect(String(obj)).toBe(String(expectations[k]), k);
+                        } catch(e) {
+                            fail('could not find paint values in layer');
+                        }
                     });
                     resolve();
                 }, TRANSITION_DELAY);
@@ -600,8 +618,8 @@ describe('@noCI, mapbox plots', function() {
             expect(countVisibleLayers(gd)).toEqual(2);
 
             return assertLayerStyle(gd, {
-                'fill-color': [1, 0, 0, 1],
-                'fill-outline-color': [0, 0, 1, 1],
+                'fill-color': 'rgba(255,0,0,1)',
+                'fill-outline-color': 'rgba(0,0,255,1)',
                 'fill-opacity': 0.3
             }, 0);
         })
@@ -617,7 +635,7 @@ describe('@noCI, mapbox plots', function() {
 
             return assertLayerStyle(gd, {
                 'line-width': 3,
-                'line-color': [0, 0, 1, 1],
+                'line-color': 'rgba(0,0,255,1)',
                 'line-opacity': 0.6
             }, 1);
         })
@@ -876,7 +894,7 @@ describe('@noCI, mapbox plots', function() {
         var subplot = gd._fullLayout.mapbox._subplot,
             map = subplot.map;
 
-        var sources = map.style.sources,
+        var sources = map.style.sourceCaches,
             layers = map.style._layers,
             uid = subplot.uid;
 
@@ -925,7 +943,7 @@ describe('@noCI, mapbox plots', function() {
                 var info = mapInfo.layers[l];
 
                 if(l.indexOf(mode) === -1) return;
-                if(info.layout.visibility === 'visible') cntPerMode++;
+                if(info.visibility === 'visible') cntPerMode++;
             });
 
             cnts.push(cntPerMode);
@@ -950,7 +968,7 @@ describe('@noCI, mapbox plots', function() {
 
             if(l.indexOf(mode) === -1) return;
 
-            values.push(info.paint[prop]);
+            values.push(info.paint._values[prop].value.value);
         });
 
         return values;
@@ -1012,13 +1030,12 @@ describe('@noCI, mapbox plots', function() {
 
         return promise;
     }
-
 });
 
 describe('@noCI, mapbox toImage', function() {
     // decreased from 1e5 - perhaps chrome got better at encoding these
     // because I get 99330 and the image still looks correct
-    var MINIMUM_LENGTH = 8e4;
+    var MINIMUM_LENGTH = 7e4;
 
     var gd;
 
diff --git a/test/jasmine/tests/scattermapbox_test.js b/test/jasmine/tests/scattermapbox_test.js
index bb4874239fd..dddb8c77a58 100644
--- a/test/jasmine/tests/scattermapbox_test.js
+++ b/test/jasmine/tests/scattermapbox_test.js
@@ -113,7 +113,11 @@ describe('scattermapbox defaults', function() {
 });
 
 describe('scattermapbox convert', function() {
-    'use strict';
+    var base = {
+        type: 'scattermapbox',
+        lon: [10, '20', 30, 20, null, 20, 10],
+        lat: [20, 20, '10', null, 10, 10, 20]
+    };
 
     function _convert(trace) {
         var gd = { data: [trace] };
@@ -126,11 +130,13 @@ describe('scattermapbox convert', function() {
         return convert(calcTrace);
     }
 
-    var base = {
-        type: 'scattermapbox',
-        lon: [10, '20', 30, 20, null, 20, 10],
-        lat: [20, 20, '10', null, 10, 10, 20]
-    };
+    function assertVisibility(opts, expectations) {
+        var actual = ['fill', 'line', 'circle', 'symbol'].map(function(l) {
+            return opts[l].layout.visibility;
+        });
+
+        expect(actual).toEqual(expectations, 'layer visibility');
+    }
 
     it('should generate correct output for markers + circle bubbles traces', function() {
         var opts = _convert(Lib.extendFlat({}, base, {
@@ -145,16 +151,14 @@ describe('scattermapbox convert', function() {
         assertVisibility(opts, ['none', 'none', 'visible', 'none']);
 
         expect(opts.circle.paint['circle-color']).toEqual({
-            property: 'circle-color',
-            stops: [
-                [0, 'rgb(220, 220, 220)'], [1, '#444'], [2, 'rgb(178, 10, 28)']
-            ]
-        }, 'circle-color stops');
+            property: 'mcc',
+            type: 'identity'
+        }, 'circle-color paint');
 
         expect(opts.circle.paint['circle-radius']).toEqual({
-            property: 'circle-radius',
-            stops: [ [0, 5], [1, 10], [2, 0] ]
-        }, 'circle-radius stops');
+            property: 'mrc',
+            type: 'identity'
+        }, 'circle-radius paint');
 
         expect(opts.circle.paint['circle-opacity']).toBe(0.7, 'circle-opacity');
 
@@ -164,11 +168,11 @@ describe('scattermapbox convert', function() {
 
         // N.B repeated values have same geojson props
         expect(circleProps).toEqual([
-            { 'circle-color': 0, 'circle-radius': 0 },
-            { 'circle-color': 1, 'circle-radius': 1 },
-            { 'circle-color': 2, 'circle-radius': 2 },
-            { 'circle-color': 1, 'circle-radius': 2 },
-            { 'circle-color': 1, 'circle-radius': 2 }
+            { 'mcc': 'rgb(220, 220, 220)', 'mrc': 5 },
+            { 'mcc': '#444', 'mrc': 10 },
+            { 'mcc': 'rgb(178, 10, 28)', 'mrc': 0 },
+            { 'mcc': '#444', 'mrc': 0 },
+            { 'mcc': '#444', 'mrc': 0 }
         ], 'geojson feature properties');
     });
 
@@ -189,27 +193,26 @@ describe('scattermapbox convert', function() {
         expect(opts.circle.paint['circle-radius']).toBe(5, 'circle-radius');
 
         expect(opts.circle.paint['circle-opacity']).toEqual({
-            property: 'circle-opacity',
-            stops: [ [0, 0.5], [1, 0], [2, 0.25], [6, 0.4] ]
-        }, 'circle-opacity stops');
+            property: 'mo',
+            type: 'identity'
+        }, 'circle-opacity paint');
 
         var circleProps = opts.circle.geojson.features.map(function(f) {
             return f.properties;
         });
 
-
         expect(circleProps).toEqual([
-            { 'circle-opacity': 0 },
-            { 'circle-opacity': 1 },
-            { 'circle-opacity': 2 },
+            { 'mo': 0.5 },
+            { 'mo': 0 },
+            { 'mo': 0.25 },
             // lat === null
             // lon === null
-            { 'circle-opacity': 1 },
-            { 'circle-opacity': 6 },
+            { 'mo': 0 },
+            { 'mo': 0.4 },
         ], 'geojson feature properties');
     });
 
-    it('should fill circle-opacity correctly during selections', function() {
+    it('should fill circle props correctly during selections', function() {
         var _base = {
             type: 'scattermapbox',
             mode: 'markers',
@@ -219,50 +222,179 @@ describe('scattermapbox convert', function() {
         };
 
         var specs = [{
+            msg: 'base case',
             patch: {
                 selectedpoints: [1, 2]
             },
-            expected: {stops: [[0, 0.2], [1, 1]], props: [0, 1, 1]}
+            expected: {
+                opacity: [0.2, 1, 1]
+            }
         }, {
+            msg: 'with set trace opacity',
             patch: {
                 opacity: 0.5,
                 selectedpoints: [1, 2]
             },
-            expected: {stops: [[0, 0.1], [1, 0.5]], props: [0, 1, 1]}
+            expected: {
+                opacity: [0.1, 0.5, 0.5]
+            }
         }, {
+            msg: 'with set scalar marker.opacity',
             patch: {
                 marker: {opacity: 0.6},
                 selectedpoints: [1, 2]
             },
-            expected: {stops: [[0, 0.12], [1, 0.6]], props: [0, 1, 1]}
+            expected: {
+                opacity: [0.12, 0.6, 0.6]
+            }
         }, {
+            msg: 'width set array marker.opacity',
             patch: {
                 marker: {
                     opacity: [0.5, 1, 0.6],
                 },
                 selectedpoints: [0, 2]
             },
-            expected: {stops: [[0, 0.5], [1, 0.2], [2, 0.6]], props: [0, 1, 2]}
+            expected: {
+                opacity: [0.5, 0.2, 0.6]
+            }
         }, {
+            msg: 'with set array marker.opacity including invalid items',
             patch: {
                 marker: {opacity: [2, null, -0.6]},
                 selectedpoints: [0, 1, 2]
             },
-            expected: {stops: [[0, 1], [1, 0]], props: [0, 1, 1]}
+            expected: {
+                opacity: [1, 0, 0]
+            }
+        }, {
+            msg: 'with set selected & unselected styles',
+            patch: {
+                selected: {
+                    marker: {
+                        opacity: 1,
+                        color: 'green',
+                        size: 20
+                    }
+                },
+                unselected: {
+                    marker: {
+                        opacity: 0,
+                        color: 'red',
+                        size: 5
+                    }
+                },
+                selectedpoints: [0, 2]
+            },
+            expected: {
+                opacity: [1, 0, 1],
+                color: ['green', 'red', 'green'],
+                size: [10, 2.5, 10]
+            }
+        }, {
+            msg: 'with set selected styles only',
+            patch: {
+                selected: {
+                    marker: {
+                        opacity: 1,
+                        color: 'green',
+                        size: 20
+                    }
+                },
+                selectedpoints: [0, 2]
+            },
+            expected: {
+                opacity: [1, 0.2, 1],
+                color: ['green', '#1f77b4', 'green'],
+                size: [10, 3, 10]
+            }
+        }, {
+            msg: 'with set selected styles only + array items',
+            patch: {
+                marker: {
+                    opacity: [0.5, 0.6, 0.7],
+                    color: ['blue', 'yellow', 'cyan'],
+                    size: [50, 60, 70]
+                },
+                selected: {
+                    marker: {
+                        opacity: 1,
+                        color: 'green',
+                        size: 20
+                    }
+                },
+                selectedpoints: [0, 2]
+            },
+            expected: {
+                opacity: [1, 0.12, 1],
+                color: ['green', 'yellow', 'green'],
+                size: [10, 30, 10]
+            }
+        }, {
+            msg: 'with set unselected styles only',
+            patch: {
+                unselected: {
+                    marker: {
+                        opacity: 0,
+                        color: 'red',
+                        size: 5
+                    }
+                },
+                selectedpoints: [0, 2]
+            },
+            expected: {
+                opacity: [1, 0, 1],
+                color: ['#1f77b4', 'red', '#1f77b4'],
+                size: [3, 2.5, 3]
+
+            }
+        }, {
+            msg: 'with set unselected styles only + array items',
+            patch: {
+                marker: {
+                    opacity: [0.5, 0.6, 0.7],
+                    color: ['blue', 'yellow', 'cyan'],
+                    size: [50, 60, 70]
+                },
+                unselected: {
+                    marker: {
+                        opacity: 0,
+                        color: 'red',
+                        size: 5
+                    }
+                },
+                selectedpoints: [0, 2]
+            },
+            expected: {
+                opacity: [0.5, 0, 0.7],
+                color: ['blue', 'red', 'cyan'],
+                size: [25, 2.5, 35]
+            }
         }];
 
         specs.forEach(function(s, i) {
-            var msg0 = '- case ' + i + ' ';
+            var msg0 = s.msg + ' - case ' + i + '- ';
             var opts = _convert(Lib.extendDeep({}, _base, s.patch));
+            var features = opts.circle.geojson.features;
+
+            function _assert(kProp, kExp) {
+                var actual = features.map(function(f) { return f.properties[kProp]; });
+                var expected = s.expected[kExp];
+                var msg = msg0 + ' marker.' + kExp;
+
+                if(Array.isArray(expected)) {
+                    expect(actual).toEqual(expected, msg);
+                } else {
+                    actual.forEach(function(a) {
+                        expect(a).toBe(undefined, msg);
+                    });
+                }
+            }
 
-            expect(opts.circle.paint['circle-opacity'].stops)
-                .toEqual(s.expected.stops, msg0 + 'stops');
-
-            var props = opts.circle.geojson.features.map(function(f) {
-                return f.properties['circle-opacity'];
-            });
-
-            expect(props).toEqual(s.expected.props, msg0 + 'props');
+            _assert('mo', 'opacity');
+            _assert('mcc', 'color');
+            // N.B. sizes in props should be half of the input values
+            _assert('mrc', 'size');
         });
     });
 
@@ -388,14 +520,16 @@ describe('scattermapbox convert', function() {
             marker: { size: ['5', '49', '5', ''] }
         }));
 
-        expect(opts.circle.paint['circle-radius'].stops)
-            .toBeCloseTo2DArray([[0, 2.5], [1, 24.5]], 'no replicate stops');
+        expect(opts.circle.paint['circle-radius']).toEqual({
+            property: 'mrc',
+            type: 'identity'
+        }, 'circle-radius paint');
 
         var radii = opts.circle.geojson.features.map(function(f) {
-            return f.properties['circle-radius'];
+            return f.properties.mrc;
         });
 
-        expect(radii).toBeCloseToArray([0, 1, 0], 'link features to correct stops');
+        expect(radii).toBeCloseToArray([2.5, 24.5, 2.5], 'circle radii');
     });
 
     it('should generate correct output for traces with only blank points', function() {
@@ -413,21 +547,10 @@ describe('scattermapbox convert', function() {
         expect(opts.line.geojson.coordinates).toEqual([], 'line coords');
         expect(opts.fill.geojson.coordinates).toEqual([], 'fill coords');
     });
-
-    function assertVisibility(opts, expectations) {
-        var actual = ['fill', 'line', 'circle', 'symbol'].map(function(l) {
-            return opts[l].layout.visibility;
-        });
-
-        expect(actual).toEqual(expectations, 'layer visibility');
-    }
 });
 
 describe('@noCI scattermapbox hover', function() {
-    'use strict';
-
     var hoverPoints = ScatterMapbox.hoverPoints;
-
     var gd;
 
     beforeAll(function(done) {
@@ -439,9 +562,9 @@ describe('@noCI scattermapbox hover', function() {
 
         var data = [{
             type: 'scattermapbox',
-            lon: [10, 20, 30],
-            lat: [10, 20, 30],
-            text: ['A', 'B', 'C']
+            lon: [10, 20, 30, 300],
+            lat: [10, 20, 30, 10],
+            text: ['A', 'B', 'C', 'D']
         }];
 
         Plotly.plot(gd, data, { autosize: true }).then(done);
@@ -453,16 +576,17 @@ describe('@noCI scattermapbox hover', function() {
     });
 
     function getPointData(gd) {
-        var cd = gd.calcdata,
-            mapbox = gd._fullLayout.mapbox._subplot;
+        var cd = gd.calcdata;
+        var subplot = gd._fullLayout.mapbox._subplot;
 
         return {
             index: false,
             distance: 20,
             cd: cd[0],
             trace: cd[0][0].trace,
-            xa: mapbox.xaxis,
-            ya: mapbox.yaxis
+            subplot: subplot,
+            xa: subplot.xaxis,
+            ya: subplot.yaxis
         };
     }
 
@@ -480,6 +604,19 @@ describe('@noCI scattermapbox hover', function() {
         expect(out.color).toEqual('#1f77b4');
     });
 
+    it('should generate hover label info (lon > 180 case)', function() {
+        var xval = 301;
+        var yval = 11;
+        var out = hoverPoints(getPointData(gd), xval, yval)[0];
+
+        expect(out.index).toEqual(3);
+        expect([out.x0, out.x1, out.y0, out.y1]).toBeCloseToArray([
+            1122.33, 1124.33, 105.41, 107.41
+        ]);
+        expect(out.extraText).toEqual('(300°, 10°)<br>D');
+        expect(out.color).toEqual('#1f77b4');
+    });
+
     it('should skip over blank and non-string text items', function(done) {
         var xval = 11,
             yval = 11,
@@ -632,49 +769,24 @@ describe('@noCI scattermapbox hover', function() {
 
 describe('@noCI Test plotly events on a scattermapbox plot:', function() {
     var mock = require('@mocks/mapbox_0.json');
+    var pointPos = [440, 290];
+    var nearPos = [460, 290];
+    var blankPos = [10, 10];
+    var mockCopy;
+    var gd;
 
-    var mockCopy, gd;
-
-    var blankPos = [10, 10],
-        pointPos,
-        nearPos;
-
-    function getPointData(gd) {
-        var cd = gd.calcdata,
-            mapbox = gd._fullLayout.mapbox._subplot;
-
-        return {
-            index: false,
-            distance: 20,
-            cd: cd[0],
-            trace: cd[0][0].trace,
-            xa: mapbox.xaxis,
-            ya: mapbox.yaxis
-        };
-    }
-
-    beforeAll(function(done) {
+    beforeAll(function() {
         Plotly.setPlotConfig({
             mapboxAccessToken: require('@build/credentials.json').MAPBOX_ACCESS_TOKEN
         });
-
-        gd = createGraphDiv();
-        mockCopy = Lib.extendDeep({}, mock);
-
-        Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
-            var bb = gd._fullLayout.mapbox._subplot.div.getBoundingClientRect(),
-                xval = 10,
-                yval = 10,
-                point = ScatterMapbox.hoverPoints(getPointData(gd), xval, yval)[0];
-            pointPos = [Math.floor(bb.left + (point.x0 + point.x1) / 2),
-                Math.floor(bb.top + (point.y0 + point.y1) / 2)];
-            nearPos = [pointPos[0] - 30, pointPos[1] - 30];
-        }).then(destroyGraphDiv).then(done);
     });
 
-    beforeEach(function() {
+    beforeEach(function(done) {
         gd = createGraphDiv();
         mockCopy = Lib.extendDeep({}, mock);
+        mockCopy.layout.width = 800;
+        mockCopy.layout.height = 500;
+        Plotly.plot(gd, mockCopy).then(done);
     });
 
     afterEach(destroyGraphDiv);
@@ -682,9 +794,7 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() {
     describe('click events', function() {
         var futureData;
 
-        beforeEach(function(done) {
-            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
-
+        beforeEach(function() {
             futureData = undefined;
             gd.on('plotly_click', function(data) {
                 futureData = data;
@@ -720,16 +830,15 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() {
 
     describe('modified click events', function() {
         var clickOpts = {
-                altKey: true,
-                ctrlKey: true,
-                metaKey: true,
-                shiftKey: true
-            },
-            futureData;
+            altKey: true,
+            ctrlKey: true,
+            metaKey: true,
+            shiftKey: true
+        };
 
-        beforeEach(function(done) {
-            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
+        var futureData;
 
+        beforeEach(function() {
             futureData = undefined;
             gd.on('plotly_click', function(data) {
                 futureData = data;
@@ -774,9 +883,7 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() {
     describe('hover events', function() {
         var futureData;
 
-        beforeEach(function(done) {
-            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
-
+        beforeEach(function() {
             gd.on('plotly_hover', function(data) {
                 futureData = data;
             });
@@ -786,8 +893,8 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() {
             mouseEvent('mousemove', blankPos[0], blankPos[1]);
             mouseEvent('mousemove', pointPos[0], pointPos[1]);
 
-            var pt = futureData.points[0],
-                evt = futureData.event;
+            var pt = futureData.points[0];
+            var evt = futureData.event;
 
             expect(Object.keys(pt)).toEqual([
                 'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat'
@@ -808,9 +915,7 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() {
     describe('unhover events', function() {
         var futureData;
 
-        beforeEach(function(done) {
-            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
-
+        beforeEach(function() {
             gd.on('plotly_unhover', function(data) {
                 futureData = data;
             });
@@ -818,8 +923,8 @@ describe('@noCI Test plotly events on a scattermapbox plot:', function() {
 
         it('should contain the correct fields', function(done) {
             move(pointPos[0], pointPos[1], nearPos[0], nearPos[1], HOVERMINTIME + 10).then(function() {
-                var pt = futureData.points[0],
-                    evt = futureData.event;
+                var pt = futureData.points[0];
+                var evt = futureData.event;
 
                 expect(Object.keys(pt)).toEqual([
                     'data', 'fullData', 'curveNumber', 'pointNumber', 'pointIndex', 'lon', 'lat'
diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js
index 49efc4a33cd..7d2af86b3e0 100644
--- a/test/jasmine/tests/select_test.js
+++ b/test/jasmine/tests/select_test.js
@@ -530,6 +530,28 @@ describe('Test select box and lasso in general:', function() {
         .catch(fail)
         .then(done);
     });
+
+    it('scroll zoom should clear selection regions', function(done) {
+        var gd = createGraphDiv();
+        var mockCopy = Lib.extendDeep({}, mock);
+        mockCopy.layout.dragmode = 'select';
+        mockCopy.config = {scrollZoom: true};
+
+        Plotly.plot(gd, mockCopy).then(function() {
+            resetEvents(gd);
+            drag(selectPath);
+            return selectedPromise;
+        })
+        .then(function() {
+            mouseEvent('mousemove', selectPath[0][0], selectPath[0][1]);
+            mouseEvent('scroll', selectPath[0][0], selectPath[0][1], {deltaX: 0, deltaY: -20});
+        })
+        .then(function() {
+            assertSelectionNodes(0, 0);
+        })
+        .catch(fail)
+        .then(done);
+    });
 });
 
 describe('Test select box and lasso per trace:', function() {