proj4 2.20.2 → 2.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bower.json +1 -1
- package/component.json +1 -1
- package/dist/lib/constants/Datum.d.ts +108 -0
- package/dist/proj4-src.js +239 -148
- package/dist/proj4.js +1 -1
- package/lib/constants/Datum.js +90 -9
- package/lib/parseCode.js +14 -15
- package/lib/projections/krovak.js +1 -1
- package/lib/projections/ortho.js +16 -13
- package/package.json +2 -2
- package/proj.db +0 -0
package/dist/proj4-src.js
CHANGED
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
|
|
260
260
|
class PROJJSONBuilderBase {
|
|
261
261
|
static getId(node) {
|
|
262
|
-
const idNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
262
|
+
const idNode = node.find((child) => Array.isArray(child) && child[0] === 'ID');
|
|
263
263
|
if (idNode && idNode.length >= 3) {
|
|
264
264
|
return {
|
|
265
265
|
authority: idNode[1],
|
|
@@ -269,20 +269,20 @@
|
|
|
269
269
|
return null;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
static convertUnit(node, type =
|
|
272
|
+
static convertUnit(node, type = 'unit') {
|
|
273
273
|
if (!node || node.length < 3) {
|
|
274
|
-
return { type, name:
|
|
274
|
+
return { type, name: 'unknown', conversion_factor: null };
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
const name = node[1];
|
|
278
278
|
const conversionFactor = parseFloat(node[2]) || null;
|
|
279
279
|
|
|
280
|
-
const idNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
280
|
+
const idNode = node.find((child) => Array.isArray(child) && child[0] === 'ID');
|
|
281
281
|
const id = idNode
|
|
282
282
|
? {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
authority: idNode[1],
|
|
284
|
+
code: parseInt(idNode[2], 10),
|
|
285
|
+
}
|
|
286
286
|
: null;
|
|
287
287
|
|
|
288
288
|
return {
|
|
@@ -294,7 +294,7 @@
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
static convertAxis(node) {
|
|
297
|
-
const name = node[1] ||
|
|
297
|
+
const name = node[1] || 'Unknown';
|
|
298
298
|
|
|
299
299
|
// Determine the direction
|
|
300
300
|
let direction;
|
|
@@ -308,16 +308,16 @@
|
|
|
308
308
|
else throw new Error(`Unknown axis abbreviation: ${abbreviation}`);
|
|
309
309
|
} else {
|
|
310
310
|
// Use the explicit direction provided in the AXIS node
|
|
311
|
-
direction = node[2]
|
|
311
|
+
direction = node[2] ? node[2].toLowerCase() : 'unknown';
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
-
const orderNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
314
|
+
const orderNode = node.find((child) => Array.isArray(child) && child[0] === 'ORDER');
|
|
315
315
|
const order = orderNode ? parseInt(orderNode[1], 10) : null;
|
|
316
316
|
|
|
317
317
|
const unitNode = node.find(
|
|
318
318
|
(child) =>
|
|
319
319
|
Array.isArray(child) &&
|
|
320
|
-
(child[0] ===
|
|
320
|
+
(child[0] === 'LENGTHUNIT' || child[0] === 'ANGLEUNIT' || child[0] === 'SCALEUNIT')
|
|
321
321
|
);
|
|
322
322
|
const unit = this.convertUnit(unitNode);
|
|
323
323
|
|
|
@@ -331,7 +331,7 @@
|
|
|
331
331
|
|
|
332
332
|
static extractAxes(node) {
|
|
333
333
|
return node
|
|
334
|
-
.filter((child) => Array.isArray(child) && child[0] ===
|
|
334
|
+
.filter((child) => Array.isArray(child) && child[0] === 'AXIS')
|
|
335
335
|
.map((axis) => this.convertAxis(axis))
|
|
336
336
|
.sort((a, b) => (a.order || 0) - (b.order || 0)); // Sort by the "order" property
|
|
337
337
|
}
|
|
@@ -339,17 +339,17 @@
|
|
|
339
339
|
static convert(node, result = {}) {
|
|
340
340
|
|
|
341
341
|
switch (node[0]) {
|
|
342
|
-
case
|
|
343
|
-
result.type =
|
|
342
|
+
case 'PROJCRS':
|
|
343
|
+
result.type = 'ProjectedCRS';
|
|
344
344
|
result.name = node[1];
|
|
345
|
-
result.base_crs = node.find((child) => Array.isArray(child) && child[0] ===
|
|
346
|
-
? this.convert(node.find((child) => Array.isArray(child) && child[0] ===
|
|
345
|
+
result.base_crs = node.find((child) => Array.isArray(child) && child[0] === 'BASEGEOGCRS')
|
|
346
|
+
? this.convert(node.find((child) => Array.isArray(child) && child[0] === 'BASEGEOGCRS'))
|
|
347
347
|
: null;
|
|
348
|
-
result.conversion = node.find((child) => Array.isArray(child) && child[0] ===
|
|
349
|
-
? this.convert(node.find((child) => Array.isArray(child) && child[0] ===
|
|
348
|
+
result.conversion = node.find((child) => Array.isArray(child) && child[0] === 'CONVERSION')
|
|
349
|
+
? this.convert(node.find((child) => Array.isArray(child) && child[0] === 'CONVERSION'))
|
|
350
350
|
: null;
|
|
351
351
|
|
|
352
|
-
const csNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
352
|
+
const csNode = node.find((child) => Array.isArray(child) && child[0] === 'CS');
|
|
353
353
|
if (csNode) {
|
|
354
354
|
result.coordinate_system = {
|
|
355
355
|
type: csNode[1],
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
const lengthUnitNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
360
|
+
const lengthUnitNode = node.find((child) => Array.isArray(child) && child[0] === 'LENGTHUNIT');
|
|
361
361
|
if (lengthUnitNode) {
|
|
362
362
|
const unit = this.convertUnit(lengthUnitNode);
|
|
363
363
|
result.coordinate_system.unit = unit; // Add unit to coordinate_system
|
|
@@ -366,23 +366,23 @@
|
|
|
366
366
|
result.id = this.getId(node);
|
|
367
367
|
break;
|
|
368
368
|
|
|
369
|
-
case
|
|
370
|
-
case
|
|
371
|
-
result.type =
|
|
369
|
+
case 'BASEGEOGCRS':
|
|
370
|
+
case 'GEOGCRS':
|
|
371
|
+
result.type = 'GeographicCRS';
|
|
372
372
|
result.name = node[1];
|
|
373
373
|
|
|
374
374
|
// Handle DATUM or ENSEMBLE
|
|
375
375
|
const datumOrEnsembleNode = node.find(
|
|
376
|
-
(child) => Array.isArray(child) && (child[0] ===
|
|
376
|
+
(child) => Array.isArray(child) && (child[0] === 'DATUM' || child[0] === 'ENSEMBLE')
|
|
377
377
|
);
|
|
378
378
|
if (datumOrEnsembleNode) {
|
|
379
379
|
const datumOrEnsemble = this.convert(datumOrEnsembleNode);
|
|
380
|
-
if (datumOrEnsembleNode[0] ===
|
|
380
|
+
if (datumOrEnsembleNode[0] === 'ENSEMBLE') {
|
|
381
381
|
result.datum_ensemble = datumOrEnsemble;
|
|
382
382
|
} else {
|
|
383
383
|
result.datum = datumOrEnsemble;
|
|
384
384
|
}
|
|
385
|
-
const primem = node.find((child) => Array.isArray(child) && child[0] ===
|
|
385
|
+
const primem = node.find((child) => Array.isArray(child) && child[0] === 'PRIMEM');
|
|
386
386
|
if (primem && primem[1] !== 'Greenwich') {
|
|
387
387
|
datumOrEnsemble.prime_meridian = {
|
|
388
388
|
name: primem[1],
|
|
@@ -392,42 +392,42 @@
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
result.coordinate_system = {
|
|
395
|
-
type:
|
|
395
|
+
type: 'ellipsoidal',
|
|
396
396
|
axis: this.extractAxes(node),
|
|
397
397
|
};
|
|
398
398
|
|
|
399
399
|
result.id = this.getId(node);
|
|
400
400
|
break;
|
|
401
401
|
|
|
402
|
-
case
|
|
403
|
-
result.type =
|
|
402
|
+
case 'DATUM':
|
|
403
|
+
result.type = 'GeodeticReferenceFrame';
|
|
404
404
|
result.name = node[1];
|
|
405
|
-
result.ellipsoid = node.find((child) => Array.isArray(child) && child[0] ===
|
|
406
|
-
? this.convert(node.find((child) => Array.isArray(child) && child[0] ===
|
|
405
|
+
result.ellipsoid = node.find((child) => Array.isArray(child) && child[0] === 'ELLIPSOID')
|
|
406
|
+
? this.convert(node.find((child) => Array.isArray(child) && child[0] === 'ELLIPSOID'))
|
|
407
407
|
: null;
|
|
408
408
|
break;
|
|
409
409
|
|
|
410
|
-
case
|
|
411
|
-
result.type =
|
|
410
|
+
case 'ENSEMBLE':
|
|
411
|
+
result.type = 'DatumEnsemble';
|
|
412
412
|
result.name = node[1];
|
|
413
413
|
|
|
414
414
|
// Extract ensemble members
|
|
415
415
|
result.members = node
|
|
416
|
-
.filter((child) => Array.isArray(child) && child[0] ===
|
|
416
|
+
.filter((child) => Array.isArray(child) && child[0] === 'MEMBER')
|
|
417
417
|
.map((member) => ({
|
|
418
|
-
type:
|
|
418
|
+
type: 'DatumEnsembleMember',
|
|
419
419
|
name: member[1],
|
|
420
420
|
id: this.getId(member), // Extract ID as { authority, code }
|
|
421
421
|
}));
|
|
422
422
|
|
|
423
423
|
// Extract accuracy
|
|
424
|
-
const accuracyNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
424
|
+
const accuracyNode = node.find((child) => Array.isArray(child) && child[0] === 'ENSEMBLEACCURACY');
|
|
425
425
|
if (accuracyNode) {
|
|
426
426
|
result.accuracy = parseFloat(accuracyNode[1]);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
// Extract ellipsoid
|
|
430
|
-
const ellipsoidNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
430
|
+
const ellipsoidNode = node.find((child) => Array.isArray(child) && child[0] === 'ELLIPSOID');
|
|
431
431
|
if (ellipsoidNode) {
|
|
432
432
|
result.ellipsoid = this.convert(ellipsoidNode); // Convert the ellipsoid node
|
|
433
433
|
}
|
|
@@ -436,66 +436,66 @@
|
|
|
436
436
|
result.id = this.getId(node);
|
|
437
437
|
break;
|
|
438
438
|
|
|
439
|
-
case
|
|
440
|
-
result.type =
|
|
439
|
+
case 'ELLIPSOID':
|
|
440
|
+
result.type = 'Ellipsoid';
|
|
441
441
|
result.name = node[1];
|
|
442
442
|
result.semi_major_axis = parseFloat(node[2]);
|
|
443
443
|
result.inverse_flattening = parseFloat(node[3]);
|
|
444
|
-
node.find((child) => Array.isArray(child) && child[0] ===
|
|
445
|
-
? this.convert(node.find((child) => Array.isArray(child) && child[0] ===
|
|
444
|
+
node.find((child) => Array.isArray(child) && child[0] === 'LENGTHUNIT')
|
|
445
|
+
? this.convert(node.find((child) => Array.isArray(child) && child[0] === 'LENGTHUNIT'), result)
|
|
446
446
|
: null;
|
|
447
447
|
break;
|
|
448
448
|
|
|
449
|
-
case
|
|
450
|
-
result.type =
|
|
449
|
+
case 'CONVERSION':
|
|
450
|
+
result.type = 'Conversion';
|
|
451
451
|
result.name = node[1];
|
|
452
|
-
result.method = node.find((child) => Array.isArray(child) && child[0] ===
|
|
453
|
-
? this.convert(node.find((child) => Array.isArray(child) && child[0] ===
|
|
452
|
+
result.method = node.find((child) => Array.isArray(child) && child[0] === 'METHOD')
|
|
453
|
+
? this.convert(node.find((child) => Array.isArray(child) && child[0] === 'METHOD'))
|
|
454
454
|
: null;
|
|
455
455
|
result.parameters = node
|
|
456
|
-
.filter((child) => Array.isArray(child) && child[0] ===
|
|
456
|
+
.filter((child) => Array.isArray(child) && child[0] === 'PARAMETER')
|
|
457
457
|
.map((param) => this.convert(param));
|
|
458
458
|
break;
|
|
459
459
|
|
|
460
|
-
case
|
|
461
|
-
result.type =
|
|
460
|
+
case 'METHOD':
|
|
461
|
+
result.type = 'Method';
|
|
462
462
|
result.name = node[1];
|
|
463
463
|
result.id = this.getId(node);
|
|
464
464
|
break;
|
|
465
465
|
|
|
466
|
-
case
|
|
467
|
-
result.type =
|
|
466
|
+
case 'PARAMETER':
|
|
467
|
+
result.type = 'Parameter';
|
|
468
468
|
result.name = node[1];
|
|
469
469
|
result.value = parseFloat(node[2]);
|
|
470
470
|
result.unit = this.convertUnit(
|
|
471
471
|
node.find(
|
|
472
472
|
(child) =>
|
|
473
473
|
Array.isArray(child) &&
|
|
474
|
-
(child[0] ===
|
|
474
|
+
(child[0] === 'LENGTHUNIT' || child[0] === 'ANGLEUNIT' || child[0] === 'SCALEUNIT')
|
|
475
475
|
)
|
|
476
476
|
);
|
|
477
477
|
result.id = this.getId(node);
|
|
478
478
|
break;
|
|
479
479
|
|
|
480
|
-
case
|
|
481
|
-
result.type =
|
|
480
|
+
case 'BOUNDCRS':
|
|
481
|
+
result.type = 'BoundCRS';
|
|
482
482
|
|
|
483
483
|
// Process SOURCECRS
|
|
484
|
-
const sourceCrsNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
484
|
+
const sourceCrsNode = node.find((child) => Array.isArray(child) && child[0] === 'SOURCECRS');
|
|
485
485
|
if (sourceCrsNode) {
|
|
486
486
|
const sourceCrsContent = sourceCrsNode.find((child) => Array.isArray(child));
|
|
487
487
|
result.source_crs = sourceCrsContent ? this.convert(sourceCrsContent) : null;
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// Process TARGETCRS
|
|
491
|
-
const targetCrsNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
491
|
+
const targetCrsNode = node.find((child) => Array.isArray(child) && child[0] === 'TARGETCRS');
|
|
492
492
|
if (targetCrsNode) {
|
|
493
493
|
const targetCrsContent = targetCrsNode.find((child) => Array.isArray(child));
|
|
494
494
|
result.target_crs = targetCrsContent ? this.convert(targetCrsContent) : null;
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
// Process ABRIDGEDTRANSFORMATION
|
|
498
|
-
const transformationNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
498
|
+
const transformationNode = node.find((child) => Array.isArray(child) && child[0] === 'ABRIDGEDTRANSFORMATION');
|
|
499
499
|
if (transformationNode) {
|
|
500
500
|
result.transformation = this.convert(transformationNode);
|
|
501
501
|
} else {
|
|
@@ -503,25 +503,25 @@
|
|
|
503
503
|
}
|
|
504
504
|
break;
|
|
505
505
|
|
|
506
|
-
case
|
|
507
|
-
result.type =
|
|
506
|
+
case 'ABRIDGEDTRANSFORMATION':
|
|
507
|
+
result.type = 'Transformation';
|
|
508
508
|
result.name = node[1];
|
|
509
|
-
result.method = node.find((child) => Array.isArray(child) && child[0] ===
|
|
510
|
-
? this.convert(node.find((child) => Array.isArray(child) && child[0] ===
|
|
509
|
+
result.method = node.find((child) => Array.isArray(child) && child[0] === 'METHOD')
|
|
510
|
+
? this.convert(node.find((child) => Array.isArray(child) && child[0] === 'METHOD'))
|
|
511
511
|
: null;
|
|
512
512
|
|
|
513
513
|
result.parameters = node
|
|
514
|
-
.filter((child) => Array.isArray(child) && (child[0] ===
|
|
514
|
+
.filter((child) => Array.isArray(child) && (child[0] === 'PARAMETER' || child[0] === 'PARAMETERFILE'))
|
|
515
515
|
.map((param) => {
|
|
516
|
-
if (param[0] ===
|
|
516
|
+
if (param[0] === 'PARAMETER') {
|
|
517
517
|
return this.convert(param);
|
|
518
|
-
} else if (param[0] ===
|
|
518
|
+
} else if (param[0] === 'PARAMETERFILE') {
|
|
519
519
|
return {
|
|
520
520
|
name: param[1],
|
|
521
521
|
value: param[2],
|
|
522
522
|
id: {
|
|
523
|
-
|
|
524
|
-
|
|
523
|
+
'authority': 'EPSG',
|
|
524
|
+
'code': 8656
|
|
525
525
|
}
|
|
526
526
|
};
|
|
527
527
|
}
|
|
@@ -530,7 +530,7 @@
|
|
|
530
530
|
// Adjust the Scale difference parameter if present
|
|
531
531
|
if (result.parameters.length === 7) {
|
|
532
532
|
const scaleDifference = result.parameters[6];
|
|
533
|
-
if (scaleDifference.name ===
|
|
533
|
+
if (scaleDifference.name === 'Scale difference') {
|
|
534
534
|
scaleDifference.value = Math.round((scaleDifference.value - 1) * 1e12) / 1e6;
|
|
535
535
|
}
|
|
536
536
|
}
|
|
@@ -538,14 +538,14 @@
|
|
|
538
538
|
result.id = this.getId(node);
|
|
539
539
|
break;
|
|
540
540
|
|
|
541
|
-
case
|
|
541
|
+
case 'AXIS':
|
|
542
542
|
if (!result.coordinate_system) {
|
|
543
|
-
result.coordinate_system = { type:
|
|
543
|
+
result.coordinate_system = { type: 'unspecified', axis: [] };
|
|
544
544
|
}
|
|
545
545
|
result.coordinate_system.axis.push(this.convertAxis(node));
|
|
546
546
|
break;
|
|
547
547
|
|
|
548
|
-
case
|
|
548
|
+
case 'LENGTHUNIT':
|
|
549
549
|
const unit = this.convertUnit(node, 'LinearUnit');
|
|
550
550
|
if (result.coordinate_system && result.coordinate_system.axis) {
|
|
551
551
|
result.coordinate_system.axis.forEach((axis) => {
|
|
@@ -578,7 +578,7 @@
|
|
|
578
578
|
super.convert(node, result);
|
|
579
579
|
|
|
580
580
|
// Skip `CS` and `USAGE` nodes for WKT2-2015
|
|
581
|
-
if (result.coordinate_system
|
|
581
|
+
if (result.coordinate_system && result.coordinate_system.subtype === 'Cartesian') {
|
|
582
582
|
delete result.coordinate_system;
|
|
583
583
|
}
|
|
584
584
|
if (result.usage) {
|
|
@@ -594,7 +594,7 @@
|
|
|
594
594
|
super.convert(node, result);
|
|
595
595
|
|
|
596
596
|
// Handle `CS` node for WKT2-2019
|
|
597
|
-
const csNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
597
|
+
const csNode = node.find((child) => Array.isArray(child) && child[0] === 'CS');
|
|
598
598
|
if (csNode) {
|
|
599
599
|
result.coordinate_system = {
|
|
600
600
|
subtype: csNode[1],
|
|
@@ -603,13 +603,21 @@
|
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
// Handle `USAGE` node for WKT2-2019
|
|
606
|
-
const usageNode = node.find((child) => Array.isArray(child) && child[0] ===
|
|
606
|
+
const usageNode = node.find((child) => Array.isArray(child) && child[0] === 'USAGE');
|
|
607
607
|
if (usageNode) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
608
|
+
const scope = usageNode.find((child) => Array.isArray(child) && child[0] === 'SCOPE');
|
|
609
|
+
const area = usageNode.find((child) => Array.isArray(child) && child[0] === 'AREA');
|
|
610
|
+
const bbox = usageNode.find((child) => Array.isArray(child) && child[0] === 'BBOX');
|
|
611
|
+
result.usage = {};
|
|
612
|
+
if (scope) {
|
|
613
|
+
result.usage.scope = scope[1];
|
|
614
|
+
}
|
|
615
|
+
if (area) {
|
|
616
|
+
result.usage.area = area[1];
|
|
617
|
+
}
|
|
618
|
+
if (bbox) {
|
|
619
|
+
result.usage.bbox = bbox.slice(1);
|
|
620
|
+
}
|
|
613
621
|
}
|
|
614
622
|
|
|
615
623
|
return result;
|
|
@@ -623,21 +631,21 @@
|
|
|
623
631
|
*/
|
|
624
632
|
function detectWKT2Version(root) {
|
|
625
633
|
// Check for WKT2-2019-specific nodes
|
|
626
|
-
if (root.find((child) => Array.isArray(child) && child[0] ===
|
|
627
|
-
return
|
|
634
|
+
if (root.find((child) => Array.isArray(child) && child[0] === 'USAGE')) {
|
|
635
|
+
return '2019'; // `USAGE` is specific to WKT2-2019
|
|
628
636
|
}
|
|
629
637
|
|
|
630
638
|
// Check for WKT2-2015-specific nodes
|
|
631
|
-
if (root.find((child) => Array.isArray(child) && child[0] ===
|
|
632
|
-
return
|
|
639
|
+
if (root.find((child) => Array.isArray(child) && child[0] === 'CS')) {
|
|
640
|
+
return '2015'; // `CS` is valid in both, but default to 2015 unless `USAGE` is present
|
|
633
641
|
}
|
|
634
642
|
|
|
635
|
-
if (root[0] ===
|
|
636
|
-
return
|
|
643
|
+
if (root[0] === 'BOUNDCRS' || root[0] === 'PROJCRS' || root[0] === 'GEOGCRS') {
|
|
644
|
+
return '2015'; // These are valid in both, but default to 2015
|
|
637
645
|
}
|
|
638
646
|
|
|
639
647
|
// Default to WKT2-2015 if no specific indicators are found
|
|
640
|
-
return
|
|
648
|
+
return '2015';
|
|
641
649
|
}
|
|
642
650
|
|
|
643
651
|
/**
|
|
@@ -647,7 +655,7 @@
|
|
|
647
655
|
*/
|
|
648
656
|
function buildPROJJSON(root) {
|
|
649
657
|
const version = detectWKT2Version(root);
|
|
650
|
-
const builder = version ===
|
|
658
|
+
const builder = version === '2019' ? PROJJSONBuilder2019 : PROJJSONBuilder2015;
|
|
651
659
|
return builder.convert(root);
|
|
652
660
|
}
|
|
653
661
|
|
|
@@ -662,30 +670,30 @@
|
|
|
662
670
|
|
|
663
671
|
// Check for WKT2-specific keywords
|
|
664
672
|
if (
|
|
665
|
-
normalizedWKT.includes(
|
|
666
|
-
normalizedWKT.includes(
|
|
667
|
-
normalizedWKT.includes(
|
|
668
|
-
normalizedWKT.includes(
|
|
669
|
-
normalizedWKT.includes(
|
|
670
|
-
normalizedWKT.includes(
|
|
671
|
-
normalizedWKT.includes(
|
|
673
|
+
normalizedWKT.includes('PROJCRS') ||
|
|
674
|
+
normalizedWKT.includes('GEOGCRS') ||
|
|
675
|
+
normalizedWKT.includes('BOUNDCRS') ||
|
|
676
|
+
normalizedWKT.includes('VERTCRS') ||
|
|
677
|
+
normalizedWKT.includes('LENGTHUNIT') ||
|
|
678
|
+
normalizedWKT.includes('ANGLEUNIT') ||
|
|
679
|
+
normalizedWKT.includes('SCALEUNIT')
|
|
672
680
|
) {
|
|
673
|
-
return
|
|
681
|
+
return 'WKT2';
|
|
674
682
|
}
|
|
675
683
|
|
|
676
684
|
// Check for WKT1-specific keywords
|
|
677
685
|
if (
|
|
678
|
-
normalizedWKT.includes(
|
|
679
|
-
normalizedWKT.includes(
|
|
680
|
-
normalizedWKT.includes(
|
|
681
|
-
normalizedWKT.includes(
|
|
682
|
-
normalizedWKT.includes(
|
|
686
|
+
normalizedWKT.includes('PROJCS') ||
|
|
687
|
+
normalizedWKT.includes('GEOGCS') ||
|
|
688
|
+
normalizedWKT.includes('LOCAL_CS') ||
|
|
689
|
+
normalizedWKT.includes('VERT_CS') ||
|
|
690
|
+
normalizedWKT.includes('UNIT')
|
|
683
691
|
) {
|
|
684
|
-
return
|
|
692
|
+
return 'WKT1';
|
|
685
693
|
}
|
|
686
694
|
|
|
687
695
|
// Default to WKT1 if no specific indicators are found
|
|
688
|
-
return
|
|
696
|
+
return 'WKT1';
|
|
689
697
|
}
|
|
690
698
|
|
|
691
699
|
var NEUTRAL = 1;
|
|
@@ -1010,7 +1018,7 @@
|
|
|
1010
1018
|
if (result.units === 'meter') {
|
|
1011
1019
|
result.to_meter = 1; // Only set to_meter if units are 'meter'
|
|
1012
1020
|
}
|
|
1013
|
-
} else if (unit
|
|
1021
|
+
} else if (unit && unit.name) {
|
|
1014
1022
|
result.units = unit.name.toLowerCase();
|
|
1015
1023
|
if (result.units === 'metre') {
|
|
1016
1024
|
result.units = 'meter'; // Normalize 'metre' to 'meter'
|
|
@@ -1051,7 +1059,7 @@
|
|
|
1051
1059
|
transformPROJJSON(projjson.source_crs, result);
|
|
1052
1060
|
|
|
1053
1061
|
if (projjson.transformation) {
|
|
1054
|
-
if (projjson.transformation.method
|
|
1062
|
+
if (projjson.transformation.method && projjson.transformation.method.name === 'NTv2') {
|
|
1055
1063
|
// Set nadgrids to the filename from the parameterfile
|
|
1056
1064
|
result.nadgrids = projjson.transformation.parameters[0].value;
|
|
1057
1065
|
} else {
|
|
@@ -1081,8 +1089,8 @@
|
|
|
1081
1089
|
case 'type':
|
|
1082
1090
|
if (value === 'GeographicCRS') {
|
|
1083
1091
|
result.projName = 'longlat';
|
|
1084
|
-
} else if (value === 'ProjectedCRS') {
|
|
1085
|
-
result.projName = projjson.conversion
|
|
1092
|
+
} else if (value === 'ProjectedCRS' && projjson.conversion && projjson.conversion.method) {
|
|
1093
|
+
result.projName = projjson.conversion.method.name; // Retain original capitalization
|
|
1086
1094
|
}
|
|
1087
1095
|
break;
|
|
1088
1096
|
|
|
@@ -1124,7 +1132,7 @@
|
|
|
1124
1132
|
const { units, to_meter } = processUnit(value.unit);
|
|
1125
1133
|
result.units = units;
|
|
1126
1134
|
result.to_meter = to_meter;
|
|
1127
|
-
} else if (value.axis[0]
|
|
1135
|
+
} else if (value.axis[0] && value.axis[0].unit) {
|
|
1128
1136
|
const { units, to_meter } = processUnit(value.axis[0].unit);
|
|
1129
1137
|
result.units = units;
|
|
1130
1138
|
result.to_meter = to_meter;
|
|
@@ -1590,6 +1598,9 @@
|
|
|
1590
1598
|
}
|
|
1591
1599
|
var codes = ['3857', '900913', '3785', '102113'];
|
|
1592
1600
|
function checkMercator(item) {
|
|
1601
|
+
if (item.title) {
|
|
1602
|
+
return item.title.toLowerCase().indexOf('epsg:') === 0 && codes.indexOf(item.title.substr(5)) > -1;
|
|
1603
|
+
}
|
|
1593
1604
|
var auth = match(item, 'authority');
|
|
1594
1605
|
if (!auth) {
|
|
1595
1606
|
return;
|
|
@@ -1612,31 +1623,27 @@
|
|
|
1612
1623
|
* @returns {import('./defs').ProjectionDefinition}
|
|
1613
1624
|
*/
|
|
1614
1625
|
function parse(code) {
|
|
1626
|
+
let out;
|
|
1615
1627
|
if (testObj(code)) {
|
|
1616
1628
|
// check to see if this is a WKT string
|
|
1617
1629
|
if (testDef(code)) {
|
|
1618
|
-
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
var out = wkt(code);
|
|
1622
|
-
// test of spetial case, due to this being a very common and often malformed
|
|
1623
|
-
if (checkMercator(out)) {
|
|
1624
|
-
return defs['EPSG:3857'];
|
|
1625
|
-
}
|
|
1630
|
+
out = defs[code];
|
|
1631
|
+
} else if (testWKT(code)) {
|
|
1632
|
+
out = wkt(code);
|
|
1626
1633
|
var maybeProjStr = checkProjStr(out);
|
|
1627
1634
|
if (maybeProjStr) {
|
|
1628
|
-
|
|
1635
|
+
out = projStr(maybeProjStr);
|
|
1629
1636
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
if (testProj(code)) {
|
|
1633
|
-
return projStr(code);
|
|
1637
|
+
} else if (testProj(code)) {
|
|
1638
|
+
out = projStr(code);
|
|
1634
1639
|
}
|
|
1635
1640
|
} else if (!('projName' in code)) {
|
|
1636
|
-
|
|
1641
|
+
out = wkt(code);
|
|
1637
1642
|
} else {
|
|
1638
|
-
|
|
1643
|
+
out = code;
|
|
1639
1644
|
}
|
|
1645
|
+
// test for special Web Mercator case, due to this being a very common and often malformed
|
|
1646
|
+
return out && checkMercator(out) ? defs['EPSG:3857'] : out;
|
|
1640
1647
|
}
|
|
1641
1648
|
|
|
1642
1649
|
function extend (destination, source) {
|
|
@@ -2260,7 +2267,7 @@
|
|
|
2260
2267
|
towgs84: '-178.3,-316.7,-131.5,5.278,6.077,10.979,19.166'
|
|
2261
2268
|
},
|
|
2262
2269
|
EPSG_4231: {
|
|
2263
|
-
towgs84: '-83.11,-97.38,-117.22,0.
|
|
2270
|
+
towgs84: '-83.11,-97.38,-117.22,0.005693,-0.044698,0.044285,0.1218'
|
|
2264
2271
|
},
|
|
2265
2272
|
EPSG_4274: {
|
|
2266
2273
|
towgs84: '-230.994,102.591,25.199,0.633,-0.239,0.9,1.95'
|
|
@@ -2290,7 +2297,7 @@
|
|
|
2290
2297
|
towgs84: '8.846,-4.394,-1.122,-0.00237,-0.146528,0.130428,0.783926'
|
|
2291
2298
|
},
|
|
2292
2299
|
EPSG_4289: {
|
|
2293
|
-
towgs84: '565.7381,50.4018,465.2904,-
|
|
2300
|
+
towgs84: '565.7381,50.4018,465.2904,-0.395026,0.330772,-1.876073,4.07244'
|
|
2294
2301
|
},
|
|
2295
2302
|
EPSG_4230: {
|
|
2296
2303
|
towgs84: '-68.863,-134.888,-111.49,-0.53,-0.14,0.57,-3.4'
|
|
@@ -2320,7 +2327,7 @@
|
|
|
2320
2327
|
towgs84: '-45,417,-3.5'
|
|
2321
2328
|
},
|
|
2322
2329
|
EPSG_4611: {
|
|
2323
|
-
towgs84: '-162.619,-276.959,-161.764,0.067753,-2.
|
|
2330
|
+
towgs84: '-162.619,-276.959,-161.764,0.067753,-2.243648,-1.158828,-1.094246'
|
|
2324
2331
|
},
|
|
2325
2332
|
EPSG_4633: {
|
|
2326
2333
|
towgs84: '137.092,131.66,91.475,-1.9436,-11.5993,-4.3321,-7.4824'
|
|
@@ -2338,7 +2345,7 @@
|
|
|
2338
2345
|
towgs84: '482.5,-130.6,564.6,-1.042,-0.214,-0.631,8.15'
|
|
2339
2346
|
},
|
|
2340
2347
|
EPSG_4660: {
|
|
2341
|
-
towgs84: '982.6087,552.753,-540.873,
|
|
2348
|
+
towgs84: '982.6087,552.753,-540.873,6.681627,-31.611492,-19.848161,16.805'
|
|
2342
2349
|
},
|
|
2343
2350
|
EPSG_4662: {
|
|
2344
2351
|
towgs84: '97.295,-263.247,310.882,-1.5999,0.8386,3.1409,13.3259'
|
|
@@ -2368,7 +2375,7 @@
|
|
|
2368
2375
|
towgs84: '217.109,86.452,23.711,0.0183,-0.0003,0.007,-0.0093'
|
|
2369
2376
|
},
|
|
2370
2377
|
EPSG_9333: {
|
|
2371
|
-
towgs84: '0,0,0,-
|
|
2378
|
+
towgs84: '0,0,0,-0.008393,0.000749,-0.010276,0'
|
|
2372
2379
|
},
|
|
2373
2380
|
EPSG_9059: {
|
|
2374
2381
|
towgs84: '0,0,0'
|
|
@@ -2524,7 +2531,7 @@
|
|
|
2524
2531
|
towgs84: '674.374,15.056,405.346'
|
|
2525
2532
|
},
|
|
2526
2533
|
EPSG_4617: {
|
|
2527
|
-
towgs84: '-0.991,1.9072,0.5129,
|
|
2534
|
+
towgs84: '-0.991,1.9072,0.5129,0.02579,0.00965,0.01166,0'
|
|
2528
2535
|
},
|
|
2529
2536
|
EPSG_4663: {
|
|
2530
2537
|
towgs84: '-210.502,-66.902,-48.476,2.094,-15.067,-5.817,0.485'
|
|
@@ -2587,10 +2594,10 @@
|
|
|
2587
2594
|
towgs84: '275.57,676.78,229.6'
|
|
2588
2595
|
},
|
|
2589
2596
|
EPSG_4283: {
|
|
2590
|
-
towgs84: '
|
|
2597
|
+
towgs84: '0.06155,-0.01087,-0.04019,0.039492,0.032722,0.032898,-0.009994'
|
|
2591
2598
|
},
|
|
2592
2599
|
EPSG_4317: {
|
|
2593
|
-
towgs84: '2.3287,-147.0425,-92.0802,-0.
|
|
2600
|
+
towgs84: '2.3287,-147.0425,-92.0802,-0.309248,0.324822,0.497299,5.689063'
|
|
2594
2601
|
},
|
|
2595
2602
|
EPSG_4272: {
|
|
2596
2603
|
towgs84: '59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993'
|
|
@@ -2713,7 +2720,7 @@
|
|
|
2713
2720
|
towgs84: '-208.4058,-109.8777,-2.5764'
|
|
2714
2721
|
},
|
|
2715
2722
|
ESRI_104101: {
|
|
2716
|
-
towgs84: '
|
|
2723
|
+
towgs84: '372.87,149.23,585.29'
|
|
2717
2724
|
},
|
|
2718
2725
|
EPSG_4693: {
|
|
2719
2726
|
towgs84: '0,-0.15,0.68'
|
|
@@ -3398,6 +3405,87 @@
|
|
|
3398
3405
|
},
|
|
3399
3406
|
EPSG_4818: {
|
|
3400
3407
|
towgs84: '589,76,480'
|
|
3408
|
+
},
|
|
3409
|
+
EPSG_10328: {
|
|
3410
|
+
towgs84: '0,0,0'
|
|
3411
|
+
},
|
|
3412
|
+
EPSG_9782: {
|
|
3413
|
+
towgs84: '0,0,0'
|
|
3414
|
+
},
|
|
3415
|
+
EPSG_9777: {
|
|
3416
|
+
towgs84: '0,0,0'
|
|
3417
|
+
},
|
|
3418
|
+
EPSG_10690: {
|
|
3419
|
+
towgs84: '0,0,0'
|
|
3420
|
+
},
|
|
3421
|
+
EPSG_10639: {
|
|
3422
|
+
towgs84: '0,0,0'
|
|
3423
|
+
},
|
|
3424
|
+
EPSG_10739: {
|
|
3425
|
+
towgs84: '0,0,0'
|
|
3426
|
+
},
|
|
3427
|
+
EPSG_7686: {
|
|
3428
|
+
towgs84: '0,0,0'
|
|
3429
|
+
},
|
|
3430
|
+
EPSG_8900: {
|
|
3431
|
+
towgs84: '0,0,0'
|
|
3432
|
+
},
|
|
3433
|
+
EPSG_5886: {
|
|
3434
|
+
towgs84: '0,0,0'
|
|
3435
|
+
},
|
|
3436
|
+
EPSG_7683: {
|
|
3437
|
+
towgs84: '0,0,0'
|
|
3438
|
+
},
|
|
3439
|
+
EPSG_6668: {
|
|
3440
|
+
towgs84: '0,0,0'
|
|
3441
|
+
},
|
|
3442
|
+
EPSG_20046: {
|
|
3443
|
+
towgs84: '0,0,0'
|
|
3444
|
+
},
|
|
3445
|
+
EPSG_10299: {
|
|
3446
|
+
towgs84: '0,0,0'
|
|
3447
|
+
},
|
|
3448
|
+
EPSG_10310: {
|
|
3449
|
+
towgs84: '0,0,0'
|
|
3450
|
+
},
|
|
3451
|
+
EPSG_10475: {
|
|
3452
|
+
towgs84: '0,0,0'
|
|
3453
|
+
},
|
|
3454
|
+
EPSG_4742: {
|
|
3455
|
+
towgs84: '0,0,0'
|
|
3456
|
+
},
|
|
3457
|
+
EPSG_10671: {
|
|
3458
|
+
towgs84: '0,0,0'
|
|
3459
|
+
},
|
|
3460
|
+
EPSG_10762: {
|
|
3461
|
+
towgs84: '0,0,0'
|
|
3462
|
+
},
|
|
3463
|
+
EPSG_10725: {
|
|
3464
|
+
towgs84: '0,0,0'
|
|
3465
|
+
},
|
|
3466
|
+
EPSG_10791: {
|
|
3467
|
+
towgs84: '0,0,0'
|
|
3468
|
+
},
|
|
3469
|
+
EPSG_10800: {
|
|
3470
|
+
towgs84: '0,0,0'
|
|
3471
|
+
},
|
|
3472
|
+
EPSG_10305: {
|
|
3473
|
+
towgs84: '0,0,0'
|
|
3474
|
+
},
|
|
3475
|
+
EPSG_10941: {
|
|
3476
|
+
towgs84: '0,0,0'
|
|
3477
|
+
},
|
|
3478
|
+
EPSG_10968: {
|
|
3479
|
+
towgs84: '0,0,0'
|
|
3480
|
+
},
|
|
3481
|
+
EPSG_10875: {
|
|
3482
|
+
towgs84: '0,0,0'
|
|
3483
|
+
},
|
|
3484
|
+
EPSG_6318: {
|
|
3485
|
+
towgs84: '0,0,0'
|
|
3486
|
+
},
|
|
3487
|
+
EPSG_10910: {
|
|
3488
|
+
towgs84: '0,0,0'
|
|
3401
3489
|
}
|
|
3402
3490
|
};
|
|
3403
3491
|
|
|
@@ -6916,7 +7004,7 @@
|
|
|
6916
7004
|
return (p);
|
|
6917
7005
|
}
|
|
6918
7006
|
|
|
6919
|
-
var names$n = ['Krovak', 'krovak'];
|
|
7007
|
+
var names$n = ['Krovak', 'Krovak Modified', 'Krovak (North Orientated)', 'Krovak Modified (North Orientated)', 'krovak'];
|
|
6920
7008
|
var krovak = {
|
|
6921
7009
|
init: init$n,
|
|
6922
7010
|
forward: forward$m,
|
|
@@ -8910,8 +8998,8 @@
|
|
|
8910
8998
|
|
|
8911
8999
|
/* Place parameters in static storage for common use
|
|
8912
9000
|
------------------------------------------------- */
|
|
8913
|
-
this.sin_p14 = Math.sin(this.lat0);
|
|
8914
|
-
this.cos_p14 = Math.cos(this.lat0);
|
|
9001
|
+
this.sin_p14 = Math.sin(this.lat0 || 0);
|
|
9002
|
+
this.cos_p14 = Math.cos(this.lat0 || 0);
|
|
8915
9003
|
}
|
|
8916
9004
|
|
|
8917
9005
|
/* Orthographic forward equations--mapping lat,long to x,y
|
|
@@ -8926,7 +9014,7 @@
|
|
|
8926
9014
|
var lat = p.y;
|
|
8927
9015
|
/* Forward equations
|
|
8928
9016
|
----------------- */
|
|
8929
|
-
dlon = adjust_lon(lon - this.long0, this.over);
|
|
9017
|
+
dlon = adjust_lon(lon - (this.long0 || 0), this.over);
|
|
8930
9018
|
|
|
8931
9019
|
sinphi = Math.sin(lat);
|
|
8932
9020
|
cosphi = Math.cos(lat);
|
|
@@ -8936,7 +9024,7 @@
|
|
|
8936
9024
|
ksp = 1;
|
|
8937
9025
|
if ((g > 0) || (Math.abs(g) <= EPSLN)) {
|
|
8938
9026
|
x = this.a * ksp * cosphi * Math.sin(dlon);
|
|
8939
|
-
y = this.y0 + this.a * ksp * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon);
|
|
9027
|
+
y = (this.y0 || 0) + this.a * ksp * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon);
|
|
8940
9028
|
}
|
|
8941
9029
|
p.x = x;
|
|
8942
9030
|
p.y = y;
|
|
@@ -8949,36 +9037,39 @@
|
|
|
8949
9037
|
var sinz, cosz; /* sin of z and cos of z */
|
|
8950
9038
|
var con;
|
|
8951
9039
|
var lon, lat;
|
|
9040
|
+
var long0, lat0;
|
|
8952
9041
|
/* Inverse equations
|
|
8953
9042
|
----------------- */
|
|
8954
|
-
p.x -= this.x0;
|
|
8955
|
-
p.y -= this.y0;
|
|
9043
|
+
p.x -= this.x0 || 0;
|
|
9044
|
+
p.y -= this.y0 || 0;
|
|
8956
9045
|
rh = Math.sqrt(p.x * p.x + p.y * p.y);
|
|
8957
9046
|
z = asinz(rh / this.a);
|
|
8958
9047
|
|
|
8959
9048
|
sinz = Math.sin(z);
|
|
8960
9049
|
cosz = Math.cos(z);
|
|
8961
9050
|
|
|
8962
|
-
|
|
9051
|
+
long0 = this.long0 || 0;
|
|
9052
|
+
lat0 = this.lat0 || 0;
|
|
9053
|
+
lon = long0;
|
|
8963
9054
|
if (Math.abs(rh) <= EPSLN) {
|
|
8964
|
-
lat =
|
|
9055
|
+
lat = lat0;
|
|
8965
9056
|
p.x = lon;
|
|
8966
9057
|
p.y = lat;
|
|
8967
9058
|
return p;
|
|
8968
9059
|
}
|
|
8969
9060
|
lat = asinz(cosz * this.sin_p14 + (p.y * sinz * this.cos_p14) / rh);
|
|
8970
|
-
con = Math.abs(
|
|
9061
|
+
con = Math.abs(lat0) - HALF_PI;
|
|
8971
9062
|
if (Math.abs(con) <= EPSLN) {
|
|
8972
|
-
if (
|
|
8973
|
-
lon = adjust_lon(
|
|
9063
|
+
if (lat0 >= 0) {
|
|
9064
|
+
lon = adjust_lon(long0 + Math.atan2(p.x, -p.y), this.over);
|
|
8974
9065
|
} else {
|
|
8975
|
-
lon = adjust_lon(
|
|
9066
|
+
lon = adjust_lon(long0 - Math.atan2(-p.x, p.y), this.over);
|
|
8976
9067
|
}
|
|
8977
9068
|
p.x = lon;
|
|
8978
9069
|
p.y = lat;
|
|
8979
9070
|
return p;
|
|
8980
9071
|
}
|
|
8981
|
-
lon = adjust_lon(
|
|
9072
|
+
lon = adjust_lon(long0 + Math.atan2((p.x * sinz), rh * this.cos_p14 * cosz - p.y * this.sin_p14 * sinz), this.over);
|
|
8982
9073
|
p.x = lon;
|
|
8983
9074
|
p.y = lat;
|
|
8984
9075
|
return p;
|
|
@@ -9119,7 +9210,7 @@
|
|
|
9119
9210
|
if (this.face === FACE_ENUM.RIGHT) {
|
|
9120
9211
|
lon = qsc_shift_lon_origin(lon, +HALF_PI);
|
|
9121
9212
|
} else if (this.face === FACE_ENUM.BACK) {
|
|
9122
|
-
lon = qsc_shift_lon_origin(lon,
|
|
9213
|
+
lon = qsc_shift_lon_origin(lon, +SPI);
|
|
9123
9214
|
} else if (this.face === FACE_ENUM.LEFT) {
|
|
9124
9215
|
lon = qsc_shift_lon_origin(lon, -HALF_PI);
|
|
9125
9216
|
}
|
|
@@ -9299,7 +9390,7 @@
|
|
|
9299
9390
|
if (this.face === FACE_ENUM.RIGHT) {
|
|
9300
9391
|
lp.lam = qsc_shift_lon_origin(lp.lam, -HALF_PI);
|
|
9301
9392
|
} else if (this.face === FACE_ENUM.BACK) {
|
|
9302
|
-
lp.lam = qsc_shift_lon_origin(lp.lam, -
|
|
9393
|
+
lp.lam = qsc_shift_lon_origin(lp.lam, -SPI);
|
|
9303
9394
|
} else if (this.face === FACE_ENUM.LEFT) {
|
|
9304
9395
|
lp.lam = qsc_shift_lon_origin(lp.lam, +HALF_PI);
|
|
9305
9396
|
}
|
|
@@ -9353,9 +9444,9 @@
|
|
|
9353
9444
|
/* Helper function: shift the longitude. */
|
|
9354
9445
|
function qsc_shift_lon_origin(lon, offset) {
|
|
9355
9446
|
var slon = lon + offset;
|
|
9356
|
-
if (slon < -
|
|
9447
|
+
if (slon < -SPI) {
|
|
9357
9448
|
slon += TWO_PI;
|
|
9358
|
-
} else if (slon >
|
|
9449
|
+
} else if (slon > +SPI) {
|
|
9359
9450
|
slon -= TWO_PI;
|
|
9360
9451
|
}
|
|
9361
9452
|
return slon;
|
|
@@ -10552,7 +10643,7 @@
|
|
|
10552
10643
|
nadgrid,
|
|
10553
10644
|
transform,
|
|
10554
10645
|
mgrs,
|
|
10555
|
-
version: '2.20.
|
|
10646
|
+
version: '2.20.4'
|
|
10556
10647
|
});
|
|
10557
10648
|
includedProjections(proj4);
|
|
10558
10649
|
|