Bing To Mapbox: Part 2

In Part 1 of this blog post series, I talked about the overall process I went through to migrate from Bing to Mapbox. In this Part 2, I will be digging into technical details of both providers and do a one-to-one class comparison & contrast with code examples. My intention for this blog post is to provide you with a technical guide if you were to take on a similar migration project.

 

bing_logo_header                         mapbox-340

 

Topics to cover

  • Referencing JavaScript and CSS resources
  • Using access key/token
  • Understanding the pixel coordinate systems
  • Handling events
  • Comparable classes with examples (Bing/Mapbox)
    • Map/Map
    • Switching map view types or base layers
    • Location/LatLng
    • LocationRect/LatLngBounds
    • Point/Point
    • Pushpin/Marker
    • PushpinOptions/Icon
    • Polygon/Polygon, MultiPolygon
    • Color/<Path options>
    • Infobox/Popup
    • EntityCollection/LayerGroup, FeatureGroup, GeoJson
    • GeoLocationProvider/map.locate(), CircleMarker
    • TileLayer/TileLayer
  • Concluding remarks

Referencing JavaScript and CSS resources

Bing

Apparently according to this page, in order to draw complex shapes such as a multipolygon, we also need to load its “Microsoft.Maps.AdvancedShapes” API using the “loadModule” method, as shown below, after the above JavaScript reference. You can click here and here to find more info about the “AdvancedShapes” module. In our app we definitely draw lots of complex shapes such as multipolygons, so we needed to include this line of code for sure. That’s it. That’s all we need for loading Bing map resources.

Mapbox

Mapbox only lets you load the resources by https. It also requires you to explicitly load the CSS, as shown below:

But other than that, that’s it. No need to load anything else for complex shapes like we had to do for Bing. Notice that the API version number (v2.1.5) is included in the reference.

Using access key/token

Bing

To create a Bing map app, you will need to create a developer account on the Bing Maps Account Center. When you have an account, you will be able to create a Bing map key that you can use in your map app. This key is required. I won’t go over the instructions for doing that here. You can find the info on how to do that here.

Your Bing map key is embedded in the “credentials” prop of the “MapOptions” object, which is passed into the constructor for a “Map” object, as shown below:

Mapbox

With Mapbox you also will need to create an account with them, and then you will be issued an access token that’s required to create Mapbox apps. You can find more info about that here.

Mapbox account access token is used like below before you can create your first map object:

Understanding the pixel coordinate systems

I want to spend a little bit of time talking about this topic b/c it actually caused me quite a bit of confusion for a couple of days. You might run into this issue too some day if you are not aware of the differences.

We have an algorithm that calculates where to display a popup on mouseover on a certain layer to ensure the popup is always shown at an ideal location on the map. In order to figure out where to display a popup on the map, we basically have to do 5 steps:

  • Get the lat and long of a point on the map
  • Convert the lat and long to pixel location
  • Do some math with the pixel location
  • Convert the new pixel location back to lat and long
  • Display the popup based on the new lat and long

After I thought I had completely migrated the API over for displaying popups, I was expecting everything to just work. But that wasn’t the case. I checked and double-checked the algorithm and the math. All was good. I was scratching my head for a couple of days…and then finally it dawned on me: the centers of the pixel coordinate systems in the 2 APIs were NOT the same! I was not expecting this at all. I just assumed they would all be using the same pixel coordinate system. Man was I wrong. I will definitely check this first next time I work with a different mapping API.

Bing

In Bing Map, apparently the (0,0) of the pixel coordinate system defaults to the center of the map view displayed on the screen, which is the “PixelReference.viewport” enumeration discussed on this page. In this configuration, to the right of the center is positive X values, and below the center is positive Y values. So a point on the map in the NE quadrant would have a positive X value, and a negative Y value.

So when I’m doing conversion using “Map.tryLocationToPixel” or “Map.tryPixelToLocation” and not specifying the “PixelReference” param, it defaults to “PixelReference.viewport”.

BingPixelCoordSystem

Mapbox

On the contrast, in Mapbox, the (0,0) of the pixel coordinate system is at the upper left corner of the map view. I’m not sure if this is a configurable option or not. To the right of the center is positive X values and below the center is positive Y values. So no visible point on a Mapbox map would have negative X or Y values.

So now you understand what caused the confusion. Without changing our algorithm used to calculate the popup position in our app, I just had to tweak the math a little in order to achieve the same result using Mapbox API.

MapboxPixelCoordSystem

Handling events

Very soon into the project I noticed there’s a pretty big difference in the event handling APIs between Bing and Mapbox. I just want to say a few words about it here in a general sense, so that you will become aware of it. Even though they are different, event handling in both APIs is fairly easy to catch on. You should be able to easily identify events in Bing and find an equivalent way to do it in Mapbox for the most part.

Bing

The Bing map API has the “Events” object whose methods you can call directly to add/remove an event handler on pretty much any object, as shown below:

Mapbox

In Mapbox, there are a set of methods shared between event-powered classes such as “Map” or “Marker”. The set of methods include “on”, “once”, “off”, “fire”. Here’s an example to add an event handler to a “Map” object on “click” event:

 

Comparable classes with examples

The Map/Map classes (Bing/Mapbox)

For both Bing map and Mapbox, the Map class is the central class of each API. It’s used to create a map object on a web page and all other objects displayed on the map will be added to the map object.

Bing

Creation API:

Map(<HTMLElement> element, <MapOptions | ViewOptions> optionsObject?)

Example:

Note that the “mapTypeId” enumeration prop of the “viewOptions” object in the example above is used to define the type of map view displayed. It can be aerial, auto, birdseye, road, etc. There are no comparable type for this in Mapbox, but this is accomplished in Mapbox using different tile layers. This is the same for the “labelOverlay” prop. I’ll give an example of how to switch map view types in both APIs shortly.

Mapbox

Creation API:

L.mapbox.map(<string> htmlElementId, <string> mapId? | url? | <object> tilejsonObject?, <object> optionsObject?)

or

L.map(<HTMLElement | string> id, <Map options> options?)

Example:

Note that in Mapbox’s Map object creation API, there’s not a way to define the initial bounds like it does in Bing map. Thus the code example above has to initialize the map’s initial bounds in 2 steps by calling “map.fitBounds” method.

Switching map view types or base layers

Earlier we talked about Mapbox’s Map object creation API doesn’t have a way to set an initial map view type like it does in Bing. But in Mapbox we can accomplish this using different tile layers.

Bing

Mapbox

Location/LatLng classes

Represents the lat and long of a geographical point on a map.

Bing

Creation API:

Location(<number> latitude, <number> longitude, <number> altitude?, <AltitudeReference> altitudeRef?)

Example:

Mapbox

Creation API:

L.latLng(<Number> latitude,<Number> longitude,<Number> altitude?)

Example:

LocationRect/LatLngBounds classes

Represents a geographical rectangle on a map.

Bing

Creation API:

LocationRect(<Location> center, <number> width, <number> height)

or use static methods:

LocationRect.fromCorners(<Location> northwest, <Location> southeast);

LocationRect.fromEdges(<number> north, <number> west, <number> south, <number> east, <number> altitude, <AltitudeReference> altitudeRef);

LocationRect.fromLocations(list of locations/array)

Example:

Mapbox

Creation API:

L.latLngBounds(<LatLng> southWest,<LatLng> northEast )

or

L.latLngBounds(<LatLng[]> latlngsArray )

Example:

Point/Point classes

Represents a point on a map with x and y coordinates in pixels. (Different from Location/LatLng)

Bing

Creation API:

Point(<number> x, <number> y);

Example:

Mapbox

Creation API:

L.point(<Number> x, <Number> y,<Boolean> round?)

Example:

Pushpin/Marker classes

Represents a pushpin/marker object on a map.

Bing

Creation API:

Pushpin(<Location> location, <PushpinOptions> optionsObj?)

Example:

Mapbox

Creation API:

L.marker(<LatLng> latlng,<Marker options> options?)

Example:

Related: PushpinOptions/Icon classes

Infobox/Popup classes

Represents an info box/popup for a pin/marker on a map.

Bing

Creation API:

Infobox(<Location> location, <InfoboxOptions> optionsObject)

Example:

Mapbox

Creation API:

L.popup(<Popup options> options?,<ILayer> source?)

Example:

Polygon/Polygon, MultiPolygon classes

Represents a polygon or multipolygon on a map.

Bing

Creation API:

Polygon(<Location[]> locations, <PolygonOptions> optionsObject)
or
Polygon(<Location[][]> arraysOfLocations, <PolygonOptions> optionsObject) for both polygon and complex polygons.

More info about complex polygons can be found here.

Example:

Mapbox

Creation API:

L.polygon(<LatLng[]> latlngs,<Polyline options> options? )
or
L.polygon(<LatLng[][]> latlngs,<Polyline options> options? )

L.multiPolygon(<LatLng[][]> latlngs,<Polyline options> options? )

Example:

Related: Color, EntitiyCollection/Polyline, Path classes

Color class/<Path options> object

Represents color values to use on an object on a map.

Bing

Creation API:

Color(<number> alpha, <number> red, <number> green, <number> blue);

Example:

Mapbox

Creation API:

N/A, not a class

Example:

EntityCollection/LayerGroup, FeatureGroup, GeoJson classes

In Bing map, an EntityCollection object is used to contain a collection of entities, which can be of type: Infobox, Polygon, Polyline, Pushpin, TileLayer or EntityCollection itself. It allows us to treat a collection of entities such as pushpins as a whole, and add that to a map in one call. Also, a “map” object by default has an “entities” prop that’s an EntityCollection type. Any qualifying entity such as a Pushpin object can be added to the map by using “map.entities.push(pushpin)”.

In Mapbox, there’s not an obvious match for this class. The most comparable classes I can find for EntityCollection in Bing are LayerGroup, FeatureGroup, and GeoJson. GeoJson extends FeatureGroup, which extends LayerGroup. But in this project, it turned out that the LayerGroup class was sufficient for OUR needs. The LayerGroup class also allows us to treat an array of objects such as markers as a whole, and then add that to a map in one call.

Bing

Creation API:

EntityCollection(<EntityCollectionOptions> optionsObject)

Example:

Mapbox

Creation API:

L.layerGroup( <ILayer[]> layers? )

L.featureGroup( <ILayer[]> layers? )

L.geoJson( <Object> geojson?,<GeoJSON options> | <Path options> options? )

Example:

GeoLocationProvider class/map.locate(), CircleMarker class

These are the classes/methods used to obtain and display the user’s current location using W3C GeoLocation API. In Bing map, there’s the GeoLocationProvider class dedicated to this. In Mapbox, there’s not a dedicated class, but simply the “locate()” method on the “map” object. Once the location is found, we can use the Mapbox CircleMarker class to draw a circle at the found location. Whereas in Bing map, we can just use the “addAccuracyCircle()” method of the GeoLocationProvider class to draw a circle.

Bing

Creation API:

GeoLocationProvider(<Map> map)

Methods:

getCurrentPosition(<PositionOptions> options)

addAccuracyCircle(<Location> center, <number> radiusInMeters, <number> segments, <PositionCircleOptions> options)

Example:

Mapbox

Creation API:

N/A

Methods:

locate(<Locate options> options? )

L.circleMarker(<LatLng> latlng,<Path options> options? )

Example:

TileLayer/TileLayer classes

Represents (raster) tile layers on a map. We can use this to load custom tile images on our map.

Bing

Creation API:

TileLayer(<TileLayerOptions> options)

Example:

Mapbox

Creation API:

L.tileLayer(<String> urlTemplate, <TileLayer options> options?)
or
L.mapbox.tileLayer(<string> mapboxMapId | url | <object> tileJson, <TileLayer options> options?)

Example:

Custom wrapper for TileLayer

Concluding remarks

I have to say that I’ve enjoyed working with both APIs, except the fact that Bing map licensing costs more and it had an issue with loading custom tiles. I think both APIs are well designed and fairly pleasant to work with.

As you can probably tell from looking over the API differences, the Bing map API was designed to have a more traditional OOP look and feel like C# and Java, since everything starts with a class. Mapbox/Leaflet, on the other hand, has a good mix of OOP and functional programming flavor to it, as evidenced by code like this: marker.setRadius(200).addTo(map). So if you are more of an traditional OOP type of developer, then you will probably prefer working with Bing map. If you have been working with JavaScript for a while, then you might find Mapbox/Leaflet to be more natural to you.

Well, that’s it for now. Even though this is NOT a comprehensive list of comparable classes between Bing and Mapbox, I believe I’ve covered quite a few of the prominent API classes commonly used in mapping. I hope you’ve found this blog post series helpful on learning about both mapping APIs, and giving you a head start if you were to move from Bing to Mapbox some day.

 

Leave a Reply

Your email address will not be published. Required fields are marked *