Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce LngLatLike param in LngLat.convert #3232

Merged
merged 3 commits into from
Sep 21, 2016
Merged

Enforce LngLatLike param in LngLat.convert #3232

merged 3 commits into from
Sep 21, 2016

Conversation

lbud
Copy link
Contributor

@lbud lbud commented Sep 20, 2016

This PR enforces LngLatLike-ness of the LngLatLike argument to LngLat.convert.

Previously LngLat.convert passed through non-LngLatLike (as class instance or array) arguments unchanged. In updating tests I caught once instance where its arguments become {lng: <lng>, lat: <lat>} but not a LngLat class instance, so I added that conditional to LngLat.convert. If its argument is none of those three types it now throws an error describing the nature of a LngLatLike argument.

This method is used in the internals of methods like setCenter, panTo, etc etc and should stop error reports of the ever-cryptic "failed to invert matrix".

@lucaswoj — there's not some special reason I'm missing why LatLng.convert may need to pass through non-LngLatLike arguments, is there? (I removed the one test that verified that worked: c3820ae#diff-c6936a6d2842db2080af49f8c5dac23aL27)

Launch Checklist

  • briefly describe the changes in this PR
  • write tests for all new functionality
  • manually test the debug page

Copy link
Member

@mourner mourner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. As far as I remember, the only reason for pass-through was to handle undefined/null values to enable things like var value = LngLat.convert(lnglat) || defaultValue, but I'm not sure whether it's used anywhere now.

@@ -82,9 +82,11 @@ LngLat.prototype.toString = function () {
LngLat.convert = function (input) {
if (input instanceof LngLat) {
return input;
}
if (Array.isArray(input)) {
} else if (input && input.lng && input.lat) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is dodgy: 0, 0 is a valid point but this will fail since 0 is falsy

}
if (Array.isArray(input)) {
} else if (input && input.lng && input.lat) {
return input;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If given {lng, lat} object, it should return a LngLat instance rather than the object.

Copy link
Contributor

@lucaswoj lucaswoj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I remember, the only reason for pass-through was to handle undefined/null values to enable things like var value = LngLat.convert(lnglat) || defaultValue, but I'm not sure whether it's used anywhere now.

I know one place it is still used: http://github.com/mapbox/mapbox-gl-js/blob/master/js/geo/lng_lat_bounds.js#L83-L84

We will need to fix that site and check for others.

* stricter object property checking + LngLat conversion
* don't rely on passthrough of non-LngLatLike args in LngLatBounds
* add tests for cases of concern
@@ -82,8 +82,8 @@ LngLat.prototype.toString = function () {
LngLat.convert = function (input) {
if (input instanceof LngLat) {
return input;
} else if (input && input.lng && input.lat) {
return input;
} else if (typeof input !== 'undefined' && input.hasOwnProperty('lng') && input.hasOwnProperty('lat')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{} is truthy, so no need for the first typeof. Also, I think you can safely use 'lng' in input instead of hasOwnProperty because it doesn't matter in this case if it's own or inherited from a prototype.

return this.extend(LngLatBounds.convert(obj));
} else if (obj.every(function(i) { return !isNaN(i); })) {
return this.extend(LngLat.convert(obj));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid closures here — they're not necessary and can be a perf hit.

@lbud
Copy link
Contributor Author

lbud commented Sep 21, 2016

👍 thanks for the review everybody — have addressed in a702272 and c1e423c.

Good catch @lucaswoj — I fixed and went through everywhere we use LngLat.convert to ensure this doesn't break anything:

line reference code comment
./geo/lng_lat.js:79: * var ll = mapboxgl.LngLat.convert(arr); documentation
./geo/lng_lat.js:82: LngLat.convert = function (input) { function signature
./geo/lng_lat_bounds.js:46: this._ne = LngLat.convert(ne); depends on strict LngLatLike-ness
./geo/lng_lat_bounds.js:57: this._sw = LngLat.convert(sw); depends on strict LngLatLike-ness
./geo/lng_lat_bounds.js:87: return this.extend(LngLat.convert(obj)); depends on LngLatLike-ness (enforces array with numeric members)
./geo/lng_lat_bounds.js:207: * Internally, the function callsLngLat#convertto convert arrays toLngLatvalues. documentation
./geo/transform.js:282: ll = LngLat.convert(lnglat); depends on strict LngLatLike-ness
./source/image_source.js:98: return map.transform.locationCoordinate(LngLat.convert(coord)).zoomTo(0); depends on strict LngLatLike-ness
./source/video_source.js:122: return map.transform.locationCoordinate(LngLat.convert(coord)).zoomTo(0); depends on strict LngLatLike-ness
./ui/camera.js:347: tr.center = LngLat.convert(options.center); depends on strict LngLatLike-ness
./ui/camera.js:420: toLngLat = LngLat.convert(options.center); depends on strict LngLatLike-ness
./ui/camera.js:423: toLngLat = LngLat.convert(options.around); depends on strict LngLatLike-ness
./ui/camera.js:572: var center = 'center' in options ? LngLat.convert(options.center) : this.getCenter(); depends on strict LngLatLike-ness / is only called if LngLatLike obj is defined
./ui/map.js:429: return this.transform.locationPoint(LngLat.convert(lnglat)); depends on strict LngLatLike-ness
./ui/marker.js:89: this._lngLat = LngLat.convert(lnglat); depends on strict LngLatLike-ness
./ui/popup.js:141: this._lngLat = LngLat.convert(lnglat); depends on strict LngLatLike-ness

@lbud lbud merged commit 56e9761 into master Sep 21, 2016
@lbud lbud deleted the LngLatcheck branch September 21, 2016 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants