@@ -128,11 +128,11 @@ function findScreenBearing(boundingRectangleBearing: number, preferredBearing: n
128
128
let bearing = boundingRectangleBearing ;
129
129
// Rotate the bearing by 90 degrees if the screen is wider than it is tall
130
130
if ( screenRatio > 1 ) {
131
- bearing = bearing + 90 % 360 ;
131
+ bearing = bearing + ( 90 % 360 ) ;
132
132
}
133
133
134
134
// Rotate the bearing 180 degrees if the preferred bearing is on the opposite side of the screen
135
- if ( bearing < preferredBearing - 90 % 360 || bearing > preferredBearing + 90 % 360 ) {
135
+ if ( bearing < preferredBearing - ( 90 % 360 ) || bearing > preferredBearing + ( 90 % 360 ) ) {
136
136
bearing = ( bearing + 180 ) % 360 ;
137
137
}
138
138
@@ -147,31 +147,39 @@ function findScreenCenter(
147
147
merc : SphericalMercator ,
148
148
) {
149
149
const { left = 0 , right = 0 , top = 0 , bottom = 0 } = padding ;
150
- const centerPoint = turf . center ( boundingRectangle ) ;
151
- if ( ! centerPoint ) {
152
- throw new Error ( 'Unable to calculate centre of surrounding rectangle' ) ;
153
- }
154
- const center = turf . getCoord ( centerPoint ) ! ;
155
- if ( ! isLngLat ( center ) ) {
156
- throw new Error ( 'Unable to calculate centre of surrounding rectangle' ) ;
157
- }
158
150
159
- const centerPx = merc . px ( center , zoom ) ;
151
+ // Use the bounding rectangle's pixel location to calculate the centre of the
152
+ // map. This allows us to account for mercator projection distortion.
153
+ const coords = turf . getCoords ( boundingRectangle ) ;
154
+ const uniqCoords = coords [ 0 ] . reduce ( ( uniq : Position [ ] , coord : [ number , number ] ) => {
155
+ if ( ! uniq . find ( ( c ) => c [ 0 ] === coord [ 0 ] && c [ 1 ] === coord [ 1 ] ) ) {
156
+ uniq . push ( coord ) ;
157
+ }
158
+ return uniq ;
159
+ } , [ ] ) ;
160
+
161
+ const sumCoords = uniqCoords . reduce (
162
+ ( acc : [ number , number ] , coord : [ number , number ] ) => {
163
+ const [ x , y ] = merc . px ( coord as LngLat , zoom ) ;
164
+ acc [ 0 ] = acc [ 0 ] + x ;
165
+ acc [ 1 ] = acc [ 1 ] + y ;
166
+ return acc ;
167
+ } ,
168
+ [ 0 , 0 ] ,
169
+ ) ;
170
+
171
+ const midX = sumCoords [ 0 ] / uniqCoords . length ;
172
+ const midY = sumCoords [ 1 ] / uniqCoords . length ;
160
173
161
- // Calculate the offset required to center the polygon in the viewport
162
174
const xPaddingOffset = right - left ;
163
175
const yPaddingOffset = bottom - top ;
164
176
165
- if ( xPaddingOffset != 0 || yPaddingOffset != 0 ) {
166
- const bearingRadians = bearing * ( Math . PI / 180 ) ;
177
+ const bearingRadians = bearing * ( Math . PI / 180 ) ;
167
178
168
- const centerXOffset = xPaddingOffset * Math . cos ( bearingRadians ) - yPaddingOffset * Math . sin ( bearingRadians ) ;
169
- const centerYOffset = xPaddingOffset * Math . sin ( bearingRadians ) + yPaddingOffset * Math . cos ( bearingRadians ) ;
179
+ const centerXOffset = xPaddingOffset * Math . cos ( bearingRadians ) - yPaddingOffset * Math . sin ( bearingRadians ) ;
180
+ const centerYOffset = xPaddingOffset * Math . sin ( bearingRadians ) + yPaddingOffset * Math . cos ( bearingRadians ) ;
170
181
171
- return merc . ll ( [ centerPx [ 0 ] + centerXOffset , centerPx [ 1 ] + centerYOffset ] , zoom ) ;
172
- } else {
173
- return merc . ll ( centerPx , zoom ) ;
174
- }
182
+ return merc . ll ( [ midX + centerXOffset , midY + centerYOffset ] , zoom ) ;
175
183
}
176
184
177
185
export function minimumBoundingRectangle ( geoJsonInput : turf . AllGeoJSON ) : {
@@ -253,8 +261,4 @@ function findRectangleOrientation(rectangle: Feature<Polygon>): rectangleOrienta
253
261
) ;
254
262
}
255
263
256
- function isLngLat ( lonLat : Position ) : lonLat is LngLat {
257
- return lonLat . length === 2 && lonLat . every ( ( coord ) => typeof coord === 'number' ) ;
258
- }
259
-
260
264
export { mapFitFeatures } ;
0 commit comments