Test Failed
Push — coordFormatterTests ( 5811de )
by no
02:50
created

provideSpacingLevelOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 13
nc 1
nop 0
1
<?php
2
3
namespace Tests\DataValues\Geo\Formatters;
4
5
use DataValues\Geo\Formatters\GeoCoordinateFormatter;
6
use DataValues\Geo\Parsers\GeoCoordinateParser;
7
use DataValues\Geo\Values\LatLongValue;
8
use DataValues\StringValue;
9
use ValueFormatters\FormatterOptions;
10
11
/**
12
 * @covers DataValues\Geo\Formatters\GeoCoordinateFormatter
13
 *
14
 * @group ValueFormatters
15
 * @group DataValueExtensions
16
 *
17
 * @license GPL-2.0+
18
 * @author Jeroen De Dauw < [email protected] >
19
 * @author Addshore
20
 * @author Daniel Kinzler
21
 */
22
class GeoCoordinateFormatterTest extends \PHPUnit_Framework_TestCase {
23
24
	public function floatNotationProvider() {
25
		return array(
26
			'0, degree' => array(
27
				new LatLongValue( 0, 0 ),
28
				1,
29
				'0, 0'
30
			),
31
			'negative zero' => array(
32
				new LatLongValue( -0.25, 0.25 ),
33
				1,
34
				'0, 0'
35
			),
36
			'signed, minute' => array(
37
				new LatLongValue( -55.755786, 37.25633 ),
38
				1.0/60,
39
				'-55.75, 37.25'
40
			),
41
			'signed, degree' => array(
42
				new LatLongValue( -55.755786, 37.25633 ),
43
				1,
44
				'-56, 37'
45
			),
46
			'three degrees' => array(
47
				new LatLongValue( -55.755786, 37.25633 ),
48
				3,
49
				'-57, 36'
50
			),
51
			'seven degrees' => array(
52
				new LatLongValue( -55.755786, 37.25633 ),
53
				7,
54
				'-56, 35'
55
			),
56
			'ten degrees' => array(
57
				new LatLongValue( -55.755786, 37.25633 ),
58
				10,
59
				'-60, 40'
60
			),
61
			'rounding degrees down' => array(
62
				new LatLongValue( -14.9, 14.9 ),
63
				10,
64
				'-10, 10'
65
			),
66
			'rounding degrees up' => array(
67
				new LatLongValue( -15, 15 ),
68
				10,
69
				'-20, 20'
70
			),
71
			'rounding fractions down' => array(
72
				new LatLongValue( -0.049, 0.049 ),
73
				0.1,
74
				'0, 0'
75
			),
76
			'rounding fractions up' => array(
77
				new LatLongValue( -0.05, 0.05 ),
78
				0.1,
79
				'-0.1, 0.1'
80
			),
81
		);
82
	}
83
84
	private function makeOptions( $format, $precision ) {
85
		$options = new FormatterOptions();
86
		$options->setOption( GeoCoordinateFormatter::OPT_FORMAT, $format );
87
		$options->setOption( GeoCoordinateFormatter::OPT_DIRECTIONAL, false );
88
		$options->setOption( GeoCoordinateFormatter::OPT_PRECISION, $precision );
89
90
		return $options;
91
	}
92
93
	/**
94
	 * @dataProvider floatNotationProvider
95
	 */
96
	public function testFloatNotationFormatting( LatLongValue $latLong, $precision, $expected ) {
97
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_FLOAT, $precision );
98
		$this->assertFormatsCorrectly( $latLong, $options, $expected );
99
	}
100
101
	/**
102
	 * @dataProvider floatNotationProvider
103
	 */
104
	public function testFloatNotationRoundTrip( LatLongValue $value, $precision, $expected ) {
105
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_FLOAT, $precision );
106
		$this->assertRoundTrip( $value, $options );
107
	}
108
109
	public function decimalDegreeNotationProvider() {
110
		return array(
111
			'0, degree' => array(
112
				new LatLongValue( 0, 0 ),
113
				1,
114
				'0°, 0°'
115
			),
116
			'negative zero' => array(
117
				new LatLongValue( -0.25, 0.25 ),
118
				1,
119
				'0°, 0°'
120
			),
121
			'signed, minute' => array(
122
				new LatLongValue( -55.755786, 37.25633 ),
123
				1.0/60,
124
				'-55.75°, 37.25°'
125
			),
126
			'signed, degree' => array(
127
				new LatLongValue( -55.755786, 37.25633 ),
128
				1,
129
				'-56°, 37°'
130
			),
131
			'three degrees' => array(
132
				new LatLongValue( -55.755786, 37.25633 ),
133
				3,
134
				'-57°, 36°'
135
			),
136
			'seven degrees' => array(
137
				new LatLongValue( -55.755786, 37.25633 ),
138
				7,
139
				'-56°, 35°'
140
			),
141
			'ten degrees' => array(
142
				new LatLongValue( -55.755786, 37.25633 ),
143
				10,
144
				'-60°, 40°'
145
			),
146
			'rounding degrees down' => array(
147
				new LatLongValue( -14.9, 14.9 ),
148
				10,
149
				'-10°, 10°'
150
			),
151
			'rounding degrees up' => array(
152
				new LatLongValue( -15, 15 ),
153
				10,
154
				'-20°, 20°'
155
			),
156
			'rounding fractions down' => array(
157
				new LatLongValue( -0.049, 0.049 ),
158
				0.1,
159
				'0.0°, 0.0°'
160
			),
161
			'rounding fractions up' => array(
162
				new LatLongValue( -0.05, 0.05 ),
163
				0.1,
164
				'-0.1°, 0.1°'
165
			),
166
		);
167
	}
168
169
	/**
170
	 * @dataProvider decimalDegreeNotationProvider
171
	 */
172
	public function testDecimalDegreeNotationFormatting( LatLongValue $latLong, $precision, $expected ) {
173
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_DD, $precision );
174
		$this->assertFormatsCorrectly( $latLong, $options, $expected );
175
	}
176
177
	/**
178
	 * @dataProvider decimalDegreeNotationProvider
179
	 */
180
	public function testDecimalDegreeNotationRoundTrip( LatLongValue $latLong, $precision, $expected ) {
181
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_DD, $precision );
182
		$this->assertRoundTrip( $latLong, $options );
183
	}
184
185
	public function decimalMinuteNotationProvider() {
186
		return array(
187
			'0, degree' => array(
188
				new LatLongValue( 0, 0 ),
189
				1,
190
				'0°, 0°'
191
			),
192
			'0, minute' => array(
193
				new LatLongValue( 0, 0 ),
194
				1.0/60,
195
				'0° 0\', 0° 0\''
196
			),
197
			'0, second' => array(
198
				new LatLongValue( 0, 0 ),
199
				1.0/3600,
200
				'0° 0.00\', 0° 0.00\''
201
			),
202
			'negative zero' => array(
203
				new LatLongValue( -1.0/128, 1.0/128 ),
204
				1.0/60,
205
				'0° 0\', 0° 0\''
206
			),
207
			'negative, not zero' => array(
208
				new LatLongValue( -0.25, 0.25 ),
209
				1.0/60,
210
				'-0° 15\', 0° 15\''
211
			),
212
			'second' => array(
213
				new LatLongValue( -55.755786, 37.25633 ),
214
				1.0/3600,
215
				'-55° 45.35\', 37° 15.38\''
216
			),
217
			'minute' => array(
218
				new LatLongValue( -55.755786, 37.25633 ),
219
				1.0/60,
220
				'-55° 45\', 37° 15\''
221
			),
222
			'ten minutes' => array(
223
				new LatLongValue( -55.755786, 37.25633 ),
224
				10.0/60,
225
				'-55° 49\', 37° 19\''
226
			),
227
			'fifty minutes' => array(
228
				new LatLongValue( -55.755786, 37.25633 ),
229
				50.0/60,
230
				'-55° 50\', 37° 30\''
231
			),
232
			'degree' => array(
233
				new LatLongValue( -55.755786, 37.25633 ),
234
				1,
235
				'-56°, 37°'
236
			),
237
			'ten degrees' => array(
238
				new LatLongValue( -55.755786, 37.25633 ),
239
				10,
240
				'-60°, 40°'
241
			),
242
			'rounding minutes down' => array(
243
				new LatLongValue( -14.9 / 60, 14.9 / 60 ),
244
				10 / 60,
245
				'-0° 10\', 0° 10\''
246
			),
247
			'rounding minutes up' => array(
248
				new LatLongValue( -15 / 60, 15 / 60 ),
249
				10 / 60,
250
				'-0° 20\', 0° 20\''
251
			),
252
			'rounding fractions down' => array(
253
				new LatLongValue( -0.049 / 60, 0.049 / 60 ),
254
				0.1 / 60,
255
				'0° 0.0\', 0° 0.0\''
256
			),
257
			'rounding fractions up' => array(
258
				new LatLongValue( -0.05 / 60, 0.05 / 60 ),
259
				0.1 / 60,
260
				'-0° 0.1\', 0° 0.1\''
261
			),
262
		);
263
	}
264
265
	/**
266
	 * @dataProvider decimalMinuteNotationProvider
267
	 */
268
	public function testDecimalMinuteNotationFormatting( LatLongValue $latLong, $precision, $expected ) {
269
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_DM, $precision );
270
		$this->assertFormatsCorrectly( $latLong, $options, $expected );
271
	}
272
273
	/**
274
	 * @dataProvider decimalMinuteNotationProvider
275
	 */
276
	public function testDecimalMinuteNotationRoundTrip( LatLongValue $latLong, $precision, $expected ) {
277
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_DM, $precision );
278
		$this->assertRoundTrip( $latLong, $options );
279
	}
280
281
	public function decimalMinuteSecondNotationProvider() {
282
		return array(
283
			'0, degree' => array(
284
				new LatLongValue( 0, 0 ),
285
				1,
286
				'0°, 0°'
287
			),
288
			'0, minute' => array(
289
				new LatLongValue( 0, 0 ),
290
				1.0/60,
291
				'0° 0\', 0° 0\''
292
			),
293
			'0, second' => array(
294
				new LatLongValue( 0, 0 ),
295
				1.0/3600,
296
				'0° 0\' 0", 0° 0\' 0"'
297
			),
298
			'negative zero' => array(
299
				new LatLongValue( -1.0/8192, 1.0/8192 ),
300
				1.0/3600,
301
				'0° 0\' 0", 0° 0\' 0"'
302
			),
303
			'negative, not zero' => array(
304
				new LatLongValue( -1.0/4096, 1.0/4096 ),
305
				1.0/7200,
306
				'-0° 0\' 1.0", 0° 0\' 1.0"'
307
			),
308
			'second' => array(
309
				new LatLongValue( -55.755786, 37.25 ),
310
				1.0/3600,
311
				'-55° 45\' 21", 37° 15\' 0"'
312
			),
313
			'second/100' => array(
314
				new LatLongValue( -55.755786, 37.25633 ),
315
				1.0/360000,
316
				'-55° 45\' 20.83", 37° 15\' 22.79"'
317
			),
318
			'ten seconds' => array(
319
				new LatLongValue( -55.755786, 37.25633 ),
320
				10.0/3600,
321
				'-55° 45\' 20", 37° 15\' 20"'
322
			),
323
			'fifty seconds' => array(
324
				new LatLongValue( -55.755786, 37.25633 ),
325
				50.0/3600,
326
				'-55° 45\' 0", 37° 15\' 0"'
327
			),
328
			'minute' => array(
329
				new LatLongValue( -55.755786, 37.25633 ),
330
				1.0/60,
331
				'-55° 45\', 37° 15\''
332
			),
333
			'degree' => array(
334
				new LatLongValue( -55.755786, 37.25633 ),
335
				1,
336
				'-56°, 37°'
337
			),
338
			'ten degrees' => array(
339
				new LatLongValue( -55.755786, 37.25633 ),
340
				10,
341
				'-60°, 40°'
342
			),
343
			'rounding seconds down' => array(
344
				new LatLongValue( -14.9 / 3600, 14.9 / 3600 ),
345
				10 / 3600,
346
				'-0° 0\' 10", 0° 0\' 10"'
347
			),
348
			'rounding seconds up' => array(
349
				new LatLongValue( -15 / 3600, 15 / 3600 ),
350
				10 / 3600,
351
				'-0° 0\' 20", 0° 0\' 20"'
352
			),
353
			'rounding fractions down' => array(
354
				new LatLongValue( -0.049 / 3600, 0.049 / 3600 ),
355
				0.1 / 3600,
356
				'0° 0\' 0.0", 0° 0\' 0.0"'
357
			),
358
			'rounding fractions up' => array(
359
				new LatLongValue( -0.05 / 3600, 0.05 / 3600 ),
360
				0.1 / 3600,
361
				'-0° 0\' 0.1", 0° 0\' 0.1"'
362
			),
363
		);
364
	}
365
366
	/**
367
	 * @dataProvider decimalMinuteSecondNotationProvider
368
	 */
369
	public function testDecimalMinuteSecondNotationFormatting( LatLongValue $latLong, $precision, $expected ) {
370
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_DMS, $precision );
371
		$this->assertFormatsCorrectly( $latLong, $options, $expected );
372
	}
373
374
	/**
375
	 * @dataProvider decimalMinuteSecondNotationProvider
376
	 */
377
	public function testDecimalMinuteSecondNotationRoundTrip( LatLongValue $latLong, $precision, $expected ) {
378
		$options = $this->makeOptions( GeoCoordinateFormatter::TYPE_DMS, $precision );
379
		$this->assertRoundTrip( $latLong, $options );
380
	}
381
382
	private function assertFormatsCorrectly( LatLongValue $latLong, FormatterOptions $options, $expected ) {
383
		$formatter = new GeoCoordinateFormatter( $options );
384
385
		$this->assertSame(
386
			$expected,
387
			$formatter->format( $latLong ),
388
			'format()'
389
		);
390
391
		$precision = $options->getOption( GeoCoordinateFormatter::OPT_PRECISION );
392
		$this->assertSame(
393
			$expected,
394
			$formatter->formatLatLongValue( $latLong, $precision ),
395
			'formatLatLongValue()'
396
		);
397
	}
398
399
	private function assertRoundTrip( LatLongValue $value, FormatterOptions $options ) {
400
		$formatter = new GeoCoordinateFormatter( $options );
401
		$parser = new GeoCoordinateParser();
402
403
		$formatted = $formatter->format( $value );
404
		$parsed = $parser->parse( $formatted );
405
406
		// NOTE: $parsed may be != $coord, because of rounding, so we can't compare directly.
407
		$formattedParsed = $formatter->format( $parsed );
408
409
		$this->assertSame( $formatted, $formattedParsed );
410
	}
411
412
	public function testDirectionalOptionGetsAppliedForDecimalMinutes() {
413
		$coordinates = array(
414
			'55° 0\' N, 37° 0\' E' => array( 55, 37 ),
415
			'55° 30\' N, 37° 30\' W' => array( 55.5, -37.5 ),
416
			'55° 30\' S, 37° 30\' E' => array( -55.5, 37.5 ),
417
			'55° 30\' S, 37° 30\' W' => array( -55.5, -37.5 ),
418
			'0° 0\' N, 0° 0\' E' => array( 0, 0 ),
419
		);
420
421
		$this->assertIsDirectionalFormatMap( $coordinates, GeoCoordinateFormatter::TYPE_DM );
422
	}
423
424
	private function assertIsDirectionalFormatMap( array $coordinates, $format ) {
425
		foreach ( $coordinates as $expected => $arguments ) {
426
			$options = new FormatterOptions();
427
			$options->setOption( GeoCoordinateFormatter::OPT_FORMAT, $format );
428
			$options->setOption( GeoCoordinateFormatter::OPT_DIRECTIONAL, true );
429
			$options->setOption( GeoCoordinateFormatter::OPT_PRECISION, 1.0/60 );
430
431
			$this->assertFormatsCorrectly(
432
				new LatLongValue( $arguments[0], $arguments[1] ),
433
				$options,
434
				$expected
435
			);
436
		}
437
	}
438
439
	public function testDirectionalOptionGetsAppliedForFloats() {
440
		$coordinates = array(
441
			'55.75 N, 37.25 W' => array( 55.755786, -37.25633 ),
442
			'55.75 S, 37.25 E' => array( -55.755786, 37.25633 ),
443
			'55 S, 37.25 W' => array( -55, -37.25633 ),
444
			'5.5 N, 37 E' => array( 5.5, 37 ),
445
			'0 N, 0 E' => array( 0, 0 ),
446
		);
447
448
		$this->assertIsDirectionalFormatMap( $coordinates, GeoCoordinateFormatter::TYPE_FLOAT );
449
	}
450
451
	private function provideSpacingLevelOptions() {
452
		return array(
453
			'none' => array(),
454
			'latlong' => array( GeoCoordinateFormatter::OPT_SPACE_LATLONG ),
455
			'direction' => array( GeoCoordinateFormatter::OPT_SPACE_DIRECTION ),
456
			'coordparts' => array( GeoCoordinateFormatter::OPT_SPACE_COORDPARTS ),
457
			'latlong_direction' => array(
458
				GeoCoordinateFormatter::OPT_SPACE_LATLONG,
459
				GeoCoordinateFormatter::OPT_SPACE_DIRECTION
460
			),
461
			'all' => array(
462
				GeoCoordinateFormatter::OPT_SPACE_LATLONG,
463
				GeoCoordinateFormatter::OPT_SPACE_DIRECTION,
464
				GeoCoordinateFormatter::OPT_SPACE_COORDPARTS,
465
			),
466
		);
467
	}
468
469
	public function testSpacingOptionGetsAppliedForDecimalMinutes() {
470
		$coordinates = array(
471
			'none' => array(
472
				'55°0\'N,37°0\'E' => array( 55, 37 ),
473
				'55°30\'N,37°30\'W' => array( 55.5, -37.5 ),
474
				'0°0\'N,0°0\'E' => array( 0, 0 ),
475
			),
476
			'latlong' => array(
477
				'55°0\'N, 37°0\'E' => array( 55, 37 ),
478
				'55°30\'N, 37°30\'W' => array( 55.5, -37.5 ),
479
				'0°0\'N, 0°0\'E' => array( 0, 0 ),
480
			),
481
			'direction' => array(
482
				'55°0\' N,37°0\' E' => array( 55, 37 ),
483
				'55°30\' N,37°30\' W' => array( 55.5, -37.5 ),
484
				'0°0\' N,0°0\' E' => array( 0, 0 ),
485
			),
486
			'coordparts' => array(
487
				'55° 0\'N,37° 0\'E' => array( 55, 37 ),
488
				'55° 30\'N,37° 30\'W' => array( 55.5, -37.5 ),
489
				'0° 0\'N,0° 0\'E' => array( 0, 0 ),
490
			),
491
			'latlong_direction' => array(
492
				'55°0\' N, 37°0\' E' => array( 55, 37 ),
493
				'55°30\' N, 37°30\' W' => array( 55.5, -37.5 ),
494
				'0°0\' N, 0°0\' E' => array( 0, 0 ),
495
			),
496
		);
497
498
		$this->assertSpacingCorrect( $coordinates, GeoCoordinateFormatter::TYPE_DM );
499
	}
500
501
	private function assertSpacingCorrect( array $coordSets, $format ) {
502
		$spacingLevelOptions = $this->provideSpacingLevelOptions();
503
		foreach( $coordSets as $spacingKey => $coordinates ) {
504
			foreach ( $coordinates as $expected => $arguments ) {
505
				$options = new FormatterOptions();
506
				$options->setOption( GeoCoordinateFormatter::OPT_FORMAT, $format );
507
				$options->setOption( GeoCoordinateFormatter::OPT_DIRECTIONAL, true );
508
				$options->setOption( GeoCoordinateFormatter::OPT_PRECISION, 1.0/60 );
509
				$options->setOption( GeoCoordinateFormatter::OPT_SPACING_LEVEL, $spacingLevelOptions[$spacingKey] );
510
511
				$this->assertFormatsCorrectly(
512
					new LatLongValue( $arguments[0], $arguments[1] ),
513
					$options,
514
					$expected
515
				);
516
			}
517
		}
518
	}
519
520
	public function testSpacingOptionGetsAppliedForFloats() {
521
		$coordinates = array(
522
			'none' => array(
523
				'55.75N,37.25W' => array( 55.755786, -37.25633 ),
524
				'0N,0E' => array( 0, 0 ),
525
			),
526
			'latlong' => array(
527
				'55.75N, 37.25W' => array( 55.755786, -37.25633 ),
528
				'0N, 0E' => array( 0, 0 ),
529
			),
530
			'direction' => array(
531
				'55.75 N,37.25 W' => array( 55.755786, -37.25633 ),
532
				'0 N,0 E' => array( 0, 0 ),
533
			),
534
			'coordparts' => array(
535
				'55.75N,37.25W' => array( 55.755786, -37.25633 ),
536
				'0N,0E' => array( 0, 0 ),
537
			),
538
			'latlong_direction' => array(
539
				'55.75 N, 37.25 W' => array( 55.755786, -37.25633 ),
540
				'0 N, 0 E' => array( 0, 0 ),
541
			),
542
			'all' => array(
543
				'55.75 N, 37.25 W' => array( 55.755786, -37.25633 ),
544
				'0 N, 0 E' => array( 0, 0 ),
545
			),
546
		);
547
548
		$this->assertSpacingCorrect( $coordinates, GeoCoordinateFormatter::TYPE_FLOAT );
549
	}
550
551
	public function testWrongType() {
552
		$this->setExpectedException( 'InvalidArgumentException' );
553
554
		$formatter = new GeoCoordinateFormatter( new FormatterOptions() );
555
556
		$formatter->format( new StringValue( 'Evil' ) );
0 ignored issues
show
Documentation introduced by
new \DataValues\StringValue('Evil') is of type object<DataValues\StringValue>, but the function expects a object<DataValues\Geo\Values\LatLongValue>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
557
	}
558
559
	public function testGivenInvalidFormattingOption_formatThrowsException() {
560
		$options = new FormatterOptions();
561
		$options->setOption( GeoCoordinateFormatter::OPT_FORMAT, 'not a format' );
562
		$formatter = new GeoCoordinateFormatter( $options );
563
564
		$this->setExpectedException( 'InvalidArgumentException' );
565
		$formatter->format( new LatLongValue( 0, 0 ) );
566
	}
567
568
	/**
569
	 * @dataProvider invalidPrecisionProvider
570
	 */
571
	public function testFormatWithInvalidPrecision_fallsBackToDefaultPrecision( $precision ) {
572
		$options = new FormatterOptions();
573
		$options->setOption( GeoCoordinateFormatter::OPT_PRECISION, $precision );
574
		$formatter = new GeoCoordinateFormatter( $options );
575
576
		$formatted = $formatter->format( new LatLongValue( 1.2, 3.4 ) );
577
		$this->assertSame( '1.2, 3.4', $formatted );
578
	}
579
580
	/**
581
	 * @dataProvider invalidPrecisionProvider
582
	 */
583
	public function testFormatLatLongValueWithInvalidPrecision_fallsBackToDefaultPrecision( $precision ) {
584
		$formatter = new GeoCoordinateFormatter( new FormatterOptions() );
585
586
		$formatted = $formatter->formatLatLongValue( new LatLongValue( 1.2, 3.4 ), $precision );
587
		$this->assertSame( '1.2, 3.4', $formatted );
588
	}
589
590
	public function invalidPrecisionProvider() {
591
		return array(
592
			array( null ),
593
			array( '' ),
594
			array( 0 ),
595
			array( -1 ),
596
			array( NAN ),
597
			array( INF ),
598
		);
599
	}
600
601
}
602