-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeographicDatasets.pck.st
More file actions
3872 lines (3268 loc) · 160 KB
/
GeographicDatasets.pck.st
File metadata and controls
3872 lines (3268 loc) · 160 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'From Cuis 6.0 [latest update: #5795] on 7 May 2023 at 7:06:01 pm'!
'Description '!
!provides: 'GeographicDatasets' 1 17!
!requires: 'ImageProcessing' 1 nil nil!
!requires: 'Cartography' 1 1 nil!
!requires: 'WebClient' 1 16 nil!
SystemOrganization addCategory: 'GeographicDatasets-Tests'!
SystemOrganization addCategory: 'GeographicDatasets'!
!classDefinition: #EGM96Geoid5Test category: 'GeographicDatasets-Tests'!
TestCase subclass: #EGM96Geoid5Test
instanceVariableNames: 'instance'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets-Tests'!
!classDefinition: 'EGM96Geoid5Test class' category: 'GeographicDatasets-Tests'!
EGM96Geoid5Test class
instanceVariableNames: ''!
!classDefinition: #ShuttleRadarTopographyMission3Test category: 'GeographicDatasets-Tests'!
TestCase subclass: #ShuttleRadarTopographyMission3Test
instanceVariableNames: 'instance'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets-Tests'!
!classDefinition: 'ShuttleRadarTopographyMission3Test class' category: 'GeographicDatasets-Tests'!
ShuttleRadarTopographyMission3Test class
instanceVariableNames: ''!
!classDefinition: #TiledECEFSampledWorldTest category: 'GeographicDatasets-Tests'!
TestCase subclass: #TiledECEFSampledWorldTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets-Tests'!
!classDefinition: 'TiledECEFSampledWorldTest class' category: 'GeographicDatasets-Tests'!
TiledECEFSampledWorldTest class
instanceVariableNames: ''!
!classDefinition: #TiledECEFVariableSampledDEMTest category: 'GeographicDatasets-Tests'!
TestCase subclass: #TiledECEFVariableSampledDEMTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets-Tests'!
!classDefinition: 'TiledECEFVariableSampledDEMTest class' category: 'GeographicDatasets-Tests'!
TiledECEFVariableSampledDEMTest class
instanceVariableNames: ''!
!classDefinition: #TrueMarbleTest category: 'GeographicDatasets-Tests'!
TestCase subclass: #TrueMarbleTest
instanceVariableNames: 'instance'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets-Tests'!
!classDefinition: 'TrueMarbleTest class' category: 'GeographicDatasets-Tests'!
TrueMarbleTest class
instanceVariableNames: ''!
!classDefinition: #MeridiansAndParallelsSampledWorld category: 'GeographicDatasets'!
Object subclass: #MeridiansAndParallelsSampledWorld
instanceVariableNames: 'nominalGSD'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'MeridiansAndParallelsSampledWorld class' category: 'GeographicDatasets'!
MeridiansAndParallelsSampledWorld class
instanceVariableNames: ''!
!classDefinition: #SampledTile category: 'GeographicDatasets'!
Object subclass: #SampledTile
instanceVariableNames: 'samples'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'SampledTile class' category: 'GeographicDatasets'!
SampledTile class
instanceVariableNames: 'nullInstance'!
!classDefinition: #ValidableTile category: 'GeographicDatasets'!
SampledTile subclass: #ValidableTile
instanceVariableNames: 'needsValidation'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'ValidableTile class' category: 'GeographicDatasets'!
ValidableTile class
instanceVariableNames: ''!
!classDefinition: #TiledSampledWorld category: 'GeographicDatasets'!
Object subclass: #TiledSampledWorld
instanceVariableNames: 'name allTiles cachedTiles datasetPath'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledSampledWorld class' category: 'GeographicDatasets'!
TiledSampledWorld class
instanceVariableNames: 'instance'!
!classDefinition: #TiledECEFSampledWorld category: 'GeographicDatasets'!
TiledSampledWorld subclass: #TiledECEFSampledWorld
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledECEFSampledWorld class' category: 'GeographicDatasets'!
TiledECEFSampledWorld class
instanceVariableNames: ''!
!classDefinition: #TiledECEFUniformlySampledWorld category: 'GeographicDatasets'!
TiledECEFSampledWorld subclass: #TiledECEFUniformlySampledWorld
instanceVariableNames: 'voxelSide samplesPerTileSide'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledECEFUniformlySampledWorld class' category: 'GeographicDatasets'!
TiledECEFUniformlySampledWorld class
instanceVariableNames: ''!
!classDefinition: #TiledECEFNaturalEarth category: 'GeographicDatasets'!
TiledECEFUniformlySampledWorld subclass: #TiledECEFNaturalEarth
instanceVariableNames: 'sourceDataset'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledECEFNaturalEarth class' category: 'GeographicDatasets'!
TiledECEFNaturalEarth class
instanceVariableNames: ''!
!classDefinition: #TiledECEFVariableSampledWorld category: 'GeographicDatasets'!
TiledECEFSampledWorld subclass: #TiledECEFVariableSampledWorld
instanceVariableNames: 'tilesPerFaceSide maxSamplesPerTileSide'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledECEFVariableSampledWorld class' category: 'GeographicDatasets'!
TiledECEFVariableSampledWorld class
instanceVariableNames: ''!
!classDefinition: #TiledECEFVariableSampledDEM category: 'GeographicDatasets'!
TiledECEFVariableSampledWorld subclass: #TiledECEFVariableSampledDEM
instanceVariableNames: 'sourceGeoid sourceDEM'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledECEFVariableSampledDEM class' category: 'GeographicDatasets'!
TiledECEFVariableSampledDEM class
instanceVariableNames: ''!
!classDefinition: #TiledECEFVariableSampledNaturalEarth category: 'GeographicDatasets'!
TiledECEFVariableSampledWorld subclass: #TiledECEFVariableSampledNaturalEarth
instanceVariableNames: 'sourceDataset'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledECEFVariableSampledNaturalEarth class' category: 'GeographicDatasets'!
TiledECEFVariableSampledNaturalEarth class
instanceVariableNames: ''!
!classDefinition: #TiledLatitudeLongitudeSampledWorld category: 'GeographicDatasets'!
TiledSampledWorld subclass: #TiledLatitudeLongitudeSampledWorld
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledLatitudeLongitudeSampledWorld class' category: 'GeographicDatasets'!
TiledLatitudeLongitudeSampledWorld class
instanceVariableNames: ''!
!classDefinition: #EGM96Geoid5 category: 'GeographicDatasets'!
TiledLatitudeLongitudeSampledWorld subclass: #EGM96Geoid5
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'EGM96Geoid5 class' category: 'GeographicDatasets'!
EGM96Geoid5 class
instanceVariableNames: ''!
!classDefinition: #NaturalEarth category: 'GeographicDatasets'!
TiledLatitudeLongitudeSampledWorld subclass: #NaturalEarth
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'NaturalEarth class' category: 'GeographicDatasets'!
NaturalEarth class
instanceVariableNames: ''!
!classDefinition: #ShuttleRadarTopographyMission category: 'GeographicDatasets'!
TiledLatitudeLongitudeSampledWorld subclass: #ShuttleRadarTopographyMission
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'ShuttleRadarTopographyMission class' category: 'GeographicDatasets'!
ShuttleRadarTopographyMission class
instanceVariableNames: ''!
!classDefinition: #ShuttleRadarTopographyMission3 category: 'GeographicDatasets'!
ShuttleRadarTopographyMission subclass: #ShuttleRadarTopographyMission3
instanceVariableNames: 'cacheSize logFilename'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'ShuttleRadarTopographyMission3 class' category: 'GeographicDatasets'!
ShuttleRadarTopographyMission3 class
instanceVariableNames: ''!
!classDefinition: #ShuttleRadarTopographyMission30 category: 'GeographicDatasets'!
ShuttleRadarTopographyMission subclass: #ShuttleRadarTopographyMission30
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'ShuttleRadarTopographyMission30 class' category: 'GeographicDatasets'!
ShuttleRadarTopographyMission30 class
instanceVariableNames: ''!
!classDefinition: #TrueMarble category: 'GeographicDatasets'!
TiledLatitudeLongitudeSampledWorld subclass: #TrueMarble
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TrueMarble class' category: 'GeographicDatasets'!
TrueMarble class
instanceVariableNames: ''!
!classDefinition: #TiledWebmercatorSampledWorld category: 'GeographicDatasets'!
TiledSampledWorld subclass: #TiledWebmercatorSampledWorld
instanceVariableNames: 'projection zoomLevel'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'TiledWebmercatorSampledWorld class' category: 'GeographicDatasets'!
TiledWebmercatorSampledWorld class
instanceVariableNames: ''!
!classDefinition: #GoogleSampledWorld category: 'GeographicDatasets'!
TiledWebmercatorSampledWorld subclass: #GoogleSampledWorld
instanceVariableNames: 'loPassSigma'
classVariableNames: ''
poolDictionaries: ''
category: 'GeographicDatasets'!
!classDefinition: 'GoogleSampledWorld class' category: 'GeographicDatasets'!
GoogleSampledWorld class
instanceVariableNames: ''!
!MeridiansAndParallelsSampledWorld commentStamp: '<historical>' prior: 0!
A synthetic dataset that simply plots a latitude and longitude grid.!
!SampledTile commentStamp: '<historical>' prior: 0!
A sampled tile for any subclass of TiledSampledWorld. Holds 'samples'. If nil, the tile is null (no data available).!
!ValidableTile commentStamp: '<historical>' prior: 0!
Tiles that require a validation before being queried for values.
In particular, for SRTM 3 datasets, to enforce the invariant that the last column and last row must equal first column or row of nieghbor.!
!TiledSampledWorld commentStamp: '<historical>' prior: 0!
A TiledSampledWorld holds a sampled image or magnitude of the whole world. Useful for DEM (like STRM) or global images (like TrueMarble or BlueMarble).
Sampled data can be, for example, 16bit ints (like STRM) or 16, 24 or 32 bit RGB (like trueMarble). Float32 and Float64 values are possible too.
Fine sampling of the whole world would result in too high memory usage.
Ivar allTiles is an Array2D with size to hold all possible tiles. Each tile is a (sub)instance of SampledTile. At each position, it can hold:
- nil: Have no information. Must build the tile from the tile file if needed.
- SampledTile with samples = nil. All samples have null value (0 if numbers, Black if colors, etc.). No need to hold data. The data file does not exist (because of being missing, or being intentionally not included, i.e. SRTM3 at the oceans)
- SampledTile with samples notNil. Might be a FloatImage, Form, etc.
Ivar cachedTiles is a FIFO holding the positions of cached regular tiles.
When a missing tile is requested, it is built and stored at allTiles. The position is added as first to cachedTiles. If cache is already full, then the last element of cachedTiles is removed and removed from allTiles (replaced by nil), to be garbage collected.
Several interpolation approaches are possible (but must be hooked in!!).
General references on projections
http://www.remotesensing.org/geotiff/spec/geotiff2.5.html
https://en.wikipedia.org/wiki/List_of_map_projections
Note: everywhere in this hierarchy, the following coordinates are zero-based:
- position of a tile
- position of a sample inside a tile
- position of a sample inside the "global" sample space (or face for ECEF subclasses)!
!TiledECEFSampledWorld commentStamp: '<historical>' prior: 0!
Provide an alternative and convenient sampling of the world surface.
Here, we want a sampling that is cheap to evalate for ECEF coordinates (quite as if converted to LLA gps coordinates, and altitude was discarded).
We use a cube, and each face is in "Earth from space" projection: https://en.wikipedia.org/wiki/Orthographic_projection_in_cartography.
This makes conversion from ECEF trivial.
Could also be called 'OrthographicSphericalCubeWorld' or such.
!
!TiledECEFUniformlySampledWorld commentStamp: '<historical>' prior: 0!
All our tiles are sampled uniformly and have the same size.!
!TiledECEFNaturalEarth commentStamp: '<historical>' prior: 0!
For comfortably experimenting with ECEF sampled datasets.
A nice looking, low resolution, NaturalEarth.
Note: TiledECEFSampledWorlds are Spheres. So, TiledECEFNaturalEarth is most likely not geometrically precise if you want really good geometric accuracy.!
!TiledECEFVariableSampledWorld commentStamp: '<historical>' prior: 0!
Each tile can have different size and resolution. Sampling is done with normalized coordinates, [0.0 .. 1.0] x [0.0 .. 1.0] in the OpenCL style. See FloatImage>>bilinearInterpolatedValueAtOpenCLClampToEdgeNormalizedX:y: .
- tilesPerFaceSide is the number of tiles each face is divided in, both horizontally and vertically.
- maxSamplesPerTileSide is a hint for building tiles. Do not use tiles larger than this.!
!TiledECEFVariableSampledDEM commentStamp: 'jmv 8/13/2018 13:35:30' prior: 0!
ECEF sampled DEM with tiles of uniform extent in metres, but with varying resolution (and sample count).
TiledECEFVariableSampledDEM loRes exampleSanFrancisco displayAutoRangeAt: 0@0 zoom: 1.
TiledECEFVariableSampledDEM hiRes exampleSanFrancisco displayAutoRangeAt: 0@0 zoom: 1.
TiledECEFVariableSampledDEM hiRes exampleBuenosAires displayAutoRangeAt: 0@0 zoom: 1.
TiledECEFVariableSampledDEM loRes exampleArgentina displayAutoRangeAt: 0@0 zoom: 1.
TiledECEFVariableSampledDEM loRes exampleWorld displayAutoRangeAt: 0@0 zoom: 1.!
!TiledECEFVariableSampledNaturalEarth commentStamp: '<historical>' prior: 0!
Quite like TiledECEFNaturalEarth, but with tiles of non-equal size. Also, tiles are instances of FloatMatrix (i.e. monochrome).!
!TiledLatitudeLongitudeSampledWorld commentStamp: '<historical>' prior: 0!
Sampling is done in an Equirrectangular projection, in particular Plate Carrée. This uniformly sampled geodetic Latitude and Longitude. See https://en.wikipedia.org/wiki/Equirectangular_projection
This is the same projection used by most global raster datasets (BlueMarble, TrueMarble, DEMs, etc).
Fine sampling of the whole world would result in too high memory usage. Tiles include an integer number of degrees of latitude and longitude. There is a cache that forgets LRU tiles.!
!EGM96Geoid5 commentStamp: 'jmv 8/13/2018 13:35:48' prior: 0!
This class implements the EGM96 Geoid Dataset with a sample grid of 5 minutes. It is based on the data file obtained from: http://geographiclib.sourceforge.net/html/geoid.html#geoidinst.
Aconcagua:
EGM96Geoid5 getInstance atLatitude: -32.6534 longitude: -70.0111
EGM96Geoid5 getInstance exampleArgentina displayAutoRangeAt: 0@0 zoom: 1.
EGM96Geoid5 getInstance exampleWorld displayAutoRangeAt: 0@0 zoom: 1.!
!NaturalEarth commentStamp: 'jmv 8/13/2018 13:36:01' prior: 0!
Cool RGB images of the whole world.
NaturalEarth getInstance exampleArgentina displayAutoRangeAt: 0@0 zoom: 1.
NaturalEarth getInstance exampleWorld displayAutoRangeAt: 0@0 zoom: 1.
!
!ShuttleRadarTopographyMission commentStamp: '<historical>' prior: 0!
Abstract superclass for SRTM and SRTM-like DEMs.!
!ShuttleRadarTopographyMission3 commentStamp: 'jmv 8/13/2018 13:36:15' prior: 0!
3 arc sec. 16bit signed data. Horizontal resolution is 3 arc sec (lat/lon) or about 90 meters. Vertical resolution is 1 m. Vertical accuracy is about 17m. Relative to WGS84 / EGM96 geoid (see http://en.wikipedia.org/wiki/EGM96 ).
Aconcagua:
(ShuttleRadarTopographyMission3 getInstance atLatitude: -32.653333 longitude: -70.010833) + (EGM96Geoid5 getInstance atLatitude: -32.653333 longitude: -70.010833). 6961.112276686436.
Official (https://en.wikipedia.org/wiki/Aconcagua) : 6961 :)
ShuttleRadarTopographyMission3 getInstance atLatitude: -80 longitude: 180
ShuttleRadarTopographyMission3 getInstance atLatitude: -30 longitude: 180
ShuttleRadarTopographyMission3 getInstance atLatitude: -90 longitude: 180
ShuttleRadarTopographyMission3 getInstance exampleSanFrancisco displayAutoRangeAt: 0@0 zoom: 1.
ShuttleRadarTopographyMission3 getInstance exampleBuenosAires displayAutoRangeAt: 0@0 zoom: 1.!
!ShuttleRadarTopographyMission30 commentStamp: 'jmv 8/13/2018 13:36:48' prior: 0!
30 arc sec. 16bit signed data. Horizontal resolution is 30 arc sec (lat/lon) or about 900 meters. Vertical resolution is also of approximately 900m. Relative to WGS84 / EGM96 geoid (see http://en.wikipedia.org/wiki/EGM96 ).
Aconcagua (slightly off, this answers the highest sample around):
ShuttleRadarTopographyMission30 getInstance atLatitude: -32.6625 longitude: -70.02083333.
ShuttleRadarTopographyMission30 getInstance atLatitude: -80 longitude: 180
ShuttleRadarTopographyMission30 getInstance atLatitude: -30 longitude: 180
ShuttleRadarTopographyMission30 getInstance atLatitude: -90 longitude: 180
ShuttleRadarTopographyMission30 getInstance atLatitude: -80 longitude: 179.99
ShuttleRadarTopographyMission30 getInstance atLatitude: -30 longitude: 179.99
ShuttleRadarTopographyMission30 getInstance atLatitude: -90 longitude: 179.99
ShuttleRadarTopographyMission30 getInstance exampleSanFrancisco displayAutoRangeAt: 0@0 zoom: 1.
ShuttleRadarTopographyMission30 getInstance exampleBuenosAires displayAutoRangeAt: 0@0 zoom: 1.
ShuttleRadarTopographyMission30 getInstance exampleArgentina displayAutoRangeAt: 0@0 zoom: 1.!
!TrueMarble commentStamp: 'jmv 8/13/2018 13:37:27' prior: 0!
Cool RGB images of the whole world. Resolution is 7.5 arc sec (lat/lon), or about 250 meters.
TrueMarble getInstance exampleSanFrancisco displayAutoRangeAt: 0@0 zoom: 1.
TrueMarble getInstance exampleBuenosAires displayAutoRangeAt: 0@0 zoom: 1.
For example, for original tile A1, geotiff data (read with listgeo.exe):
----------------------------------------------------------------------------------------------------
Geotiff_Information:
Version: 1
Key_Revision: 1.0
Tagged_Information:
ModelTiepointTag (2,3):
0 0 0
-180 90 0
ModelPixelScaleTag (1,3):
0.00208333333 0.00208333333 0
End_Of_Tags.
Keyed_Information:
GTModelTypeGeoKey (Short,1): ModelTypeGeographic
GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
GeographicTypeGeoKey (Short,1): GCS_WGS_84
GeogCitationGeoKey (Ascii,7): "WGS 84"
GeogAngularUnitsGeoKey (Short,1): Angular_Degree
End_Of_Keys.
End_Of_Geotiff.
GCS: 4326/WGS 84
Datum: 6326/World Geodetic System 1984
Ellipsoid: 7030/WGS 84 (6378137.00,6356752.31)
Prime Meridian: 8901/Greenwich (0.000000/ 0d 0' 0.00"E)
Corner Coordinates:
Upper Left (180d 0' 0.00"W, 90d 0' 0.00"N)
Lower Left (180d 0' 0.00"W, 45d 0' 0.00"N)
Upper Right (135d 0' 0.00"W, 90d 0' 0.00"N)
Lower Right (135d 0' 0.00"W, 45d 0' 0.00"N)
Center (157d30' 0.00"W, 67d30' 0.00"N)
-----------------------------------------------------------------------------------------------
But as each pixel represents an area of 7.5x7.5 arc sec, the center of topLeft pixel is:
179d 59' 56.25"W or 179.9989583333333W
89d 59' 56.25"N or 89.9989583333333N
http://www.remotesensing.org/geotiff/faq.html#Who%20owns%20GeoTIFF%20Format?
What is the effect of PixelIsArea vs. PixelIsPoint?
Setting the GTRasterTypeGeoKey value to RasterPixelIsPoint or RasterPixelIsArea alters how the raster coordinate space is to be interpreted. This is defined in section 2.5.2.2 of the GeoTIFF specification. In the case of PixelIsArea (default) a pixel is treated as an area and the raster coordinate (0,0) is the top left corner of the top left pixel. PixelIsPoint treats pixels as point samples with empty space between the "pixel" samples. In this case raster (0,0) is the location of the top left raster pixel.
The net effect is a half pixel offset when interpreting PixelIsPoint vs. PixelIsArea images. There has been some confusion on the intepretation of this value in the GeoTIFF community for some time and some held the opinion that this parameter should have no effect on the georeferencing of the image. While this matter has been settled, the result is that some software packages, particularly those built on GDAL 1.7.x or older incorrectly interprete the georeferenced location of PixelIsPoint images - placing them offset by half a pixel. One way of avoiding compatability problems is to produce GeoTIFF files with the default PixelIsArea interpretation.
For us, I (jmv) divided each TrueMarble tile into 5x5 smaller tiles, and saved each of these in a file. Each smaller tile is 9x9 degrees of long/lat (original are 45x45 degrees) and 4320x4320 pixels (original are 21600x21600 pxels). This is to save memory, and better caching behavior.!
!TiledWebmercatorSampledWorld commentStamp: '<historical>' prior: 0!
Tiles are web mercator projection, with integer scale, as used by most popular mapping apps.
zoomLevel is an integer greater or equal to zero. Tiles are always 256@256 in extent. The world is divided in (2 ^ zoomLevel) tiles in each direction. Total tiles is (2^(2*zoomLevel)). Tiles have no overlap. The origin is the 'topLeft' corner of the topLeft pixel. This means that the value for the topLeft pixel is actually for coordinates 0.5@0.5.
See https://wiki.openstreetmap.org/wiki/Zoom_levels!
!GoogleSampledWorld commentStamp: '<historical>' prior: 0!
Google maps tiles.
GoogleSampledWorld exampleArgentina displayAutoRangeAt: 0@0 zoom: 1!
!EGM96Geoid5Test methodsFor: 'setUp - tearDown' stamp: 'jmv 3/4/2016 10:39'!
setUp
instance _ EGM96Geoid5 getInstance! !
!EGM96Geoid5Test methodsFor: 'testing' stamp: 'jmv 3/23/2016 12:23'!
testGlobaPixelPosition
"As specified at http://geographiclib.sourceforge.net/html/geoid.html
# Origin 90N 0E
"
self assert: (instance globalPixelPositionForLatitude: 90 longitude: 0) = (0@0).
self assert: (instance globalPixelPositionForLatitude: -90 longitude: 359.999999) rounded = ((instance tileAt: 0@0) samples extent-1)! !
!EGM96Geoid5Test methodsFor: 'testing' stamp: 'jmv 11/10/2015 12:57'!
testInterpolationBeyondEnds
"Test preconditions. If not true, can not do test."
self assert: (instance atLatitude: -37 longitude: 359) > 16 description: 'Test assumption failed'.
self assert: (instance atLatitude: -37 longitude: 360) > 16 description: 'Test assumption failed'.
"Real test. Stuff we want to assert."
self assert: (instance atLatitude: -37 longitude: 359.95) > 16 description: 'Looks like trying to interpolate outside bounds'.
self assert: (instance atLatitude: -37 longitude: 359.995) > 16 description: 'Looks like trying to interpolate outside bounds'.! !
!ShuttleRadarTopographyMission3Test methodsFor: 'setUp - tearDown' stamp: 'jmv 3/4/2016 10:40'!
setUp
instance _ ShuttleRadarTopographyMission3 getInstance! !
!ShuttleRadarTopographyMission3Test methodsFor: 'testing' stamp: 'jmv 1/22/2016 16:21'!
testFilenames
| tileExtentSamples |
self assert: (instance filenameForTileAt: 0@0) = 'N89W180.hgt'.
self assert: (instance filenameForTileAt: 0@1) = 'N88W180.hgt'.
self assert: (instance filenameForTileAt: 0@88) = 'N01W180.hgt'.
self assert: (instance filenameForTileAt: 0@89) = 'N00W180.hgt'.
self assert: (instance filenameForTileAt: 0@90) = 'S01W180.hgt'.
self assert: (instance filenameForTileAt: 0@91) = 'S02W180.hgt'.
self assert: (instance filenameForTileAt: 0@178) = 'S89W180.hgt'.
self assert: (instance filenameForTileAt: 0@179) = 'S90W180.hgt'.
self assert: (instance filenameForTileAt: 0@89) = 'N00W180.hgt'.
self assert: (instance filenameForTileAt: 1@89) = 'N00W179.hgt'.
self assert: (instance filenameForTileAt: 2@89) = 'N00W178.hgt'.
self assert: (instance filenameForTileAt: 102@89) = 'N00W078.hgt'.
self assert: (instance filenameForTileAt: 172@89) = 'N00W008.hgt'.
self assert: (instance filenameForTileAt: 178@89) = 'N00W002.hgt'.
self assert: (instance filenameForTileAt: 179@89) = 'N00W001.hgt'.
self assert: (instance filenameForTileAt: 180@89) = 'N00E000.hgt'.
self assert: (instance filenameForTileAt: 181@89) = 'N00E001.hgt'.
self assert: (instance filenameForTileAt: 280@89) = 'N00E100.hgt'.
self assert: (instance filenameForTileAt: 350@89) = 'N00E170.hgt'.
self assert: (instance filenameForTileAt: 358@89) = 'N00E178.hgt'.
self assert: (instance filenameForTileAt: 359@89) = 'N00E179.hgt'.
tileExtentSamples _ instance class tileWidthInSamples @ instance class tileHeightInSamples.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -0.9 longitude: -100) // tileExtentSamples) = 'S01W100.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -90 longitude: -180) // tileExtentSamples) = 'S90W180.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -90 longitude: -100) // tileExtentSamples) = 'S90W100.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -90 longitude: -99) // tileExtentSamples) = 'S90W099.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -90 longitude: -1) // tileExtentSamples) = 'S90W001.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -89.5 longitude: -180) // tileExtentSamples) = 'S90W180.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -89.5 longitude: -100) // tileExtentSamples) = 'S90W100.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -89.5 longitude: -99) // tileExtentSamples) = 'S90W099.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: -89.5 longitude: -1) // tileExtentSamples) = 'S90W001.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 90 longitude: -180) // tileExtentSamples) = 'N89W180.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 90 longitude: -100) // tileExtentSamples) = 'N89W100.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 90 longitude: -99) // tileExtentSamples) = 'N89W099.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 90 longitude: -1) // tileExtentSamples) = 'N89W001.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 89.5 longitude: -180) // tileExtentSamples) = 'N89W180.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 89.5 longitude: -100) // tileExtentSamples) = 'N89W100.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 89.5 longitude: -99) // tileExtentSamples) = 'N89W099.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 89.5 longitude: -1) // tileExtentSamples) = 'N89W001.hgt'.
self assert: (instance filenameForTileAt: (instance globalPixelPositionForLatitude: 0.0 longitude: 179.99999) // tileExtentSamples) = 'S01E179.hgt'.! !
!ShuttleRadarTopographyMission3Test methodsFor: 'testing' stamp: 'jmv 1/22/2016 16:21'!
testGlobaPixelPosition
"As specified in http://www2.jpl.nasa.gov/srtm/faq.html"
| tileExtentSamples |
self assert: (instance globalPixelPositionForLatitude: 90 longitude: -180) = (0@0).
tileExtentSamples _ instance class tileWidthInSamples @ instance class tileHeightInSamples.
self assert: (instance globalPixelPositionForLatitude: -90 longitude: 179.999999) rounded = (ShuttleRadarTopographyMission3 tileCount * tileExtentSamples)! !
!TiledECEFSampledWorldTest methodsFor: 'testing' stamp: 'jmv 3/23/2016 16:41'!
test01
"
TiledECEFSampledWorldTest new test01
"
| ne r tile posInTile faceIndex |
r _ GPSPosition wgs84SemiMajorAxis.
ne _ TiledECEFVariableSampledNaturalEarth tilesPerFaceSide: 3 samplesPerTileSide: 101.
{
(1@0@0) normalized * r.
(0@1@0) normalized * r.
(0@0@1) normalized * r.
(1@1@1) normalized * r.
(1@1@1.01) normalized * r.
(1@1@0.99) normalized * r } do: [ :ecef |
faceIndex _ ne bestFaceFor: ecef.
ne withTileAndPositionInTileOf: ecef atFace: faceIndex evaluate: [ :tilePos :openCLNormalizedPositionInTile |
tile _ ne newTileSamples.
posInTile _ (tile oneBasedCoordinatesForOpenCLNormalized: openCLNormalizedPositionInTile) - 1.
ne
ecefOfFace: faceIndex
tilePosition: tilePos
positionInTile: posInTile
samples: tile
into: [ :recoveredECEF :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: (recoveredECEF - ecef) length < 1e-6 ]
ifFalse: [ self halt ]]]]! !
!TiledECEFSampledWorldTest methodsFor: 'testing' stamp: 'jmv 3/23/2016 16:41'!
test02
"
TiledECEFSampledWorldTest new test02
"
| ne tile |
ne _ TiledECEFVariableSampledNaturalEarth tilesPerFaceSide: 1 samplesPerTileSide: 101.
tile _ ne newTileSamples.
"North pole"
ne
ecefOfFace: 1
tilePosition: 0@0
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0@0@1) ]
ifFalse: [ self halt ]].
"South pole"
ne
ecefOfFace: 6
tilePosition: 0@0
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0@0@ -1) ]
ifFalse: [ self halt ]].
"Points around Equator"
ne
ecefOfFace: 2
tilePosition: 0@0
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0 @ -1 @ 0) ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 3
tilePosition: 0@0
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (1@0@0) ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 4
tilePosition: 0@0
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0@1@0) ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 5
tilePosition: 0@0
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (-1@0@0) ]
ifFalse: [ self halt ]].! !
!TiledECEFSampledWorldTest methodsFor: 'testing' stamp: 'jmv 3/23/2016 16:41'!
test03
"
TiledECEFSampledWorldTest new test03
"
| ne tile |
ne _ TiledECEFVariableSampledNaturalEarth tilesPerFaceSide: 3 samplesPerTileSide: 101.
tile _ ne newTileSamples.
"North pole"
ne
ecefOfFace: 1
tilePosition: 1@1
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0@0@1) ]
ifFalse: [ self halt ]].
"South pole"
ne
ecefOfFace: 6
tilePosition: 1@1
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0 @ 0 @ -1) ]
ifFalse: [ self halt ]].
"Points around Equator"
ne
ecefOfFace: 2
tilePosition: 1@1
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0 @ -1 @ 0) ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 3
tilePosition: 1@1
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (1@0@0) ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 4
tilePosition: 1@1
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (0@1@0) ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 5
tilePosition: 1@1
positionInTile: 50@50
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: ecef normalized = (-1@0@0) ]
ifFalse: [ self halt ]].! !
!TiledECEFSampledWorldTest methodsFor: 'testing' stamp: 'jmv 3/23/2016 16:41'!
test04
"
TiledECEFSampledWorldTest new test04
"
| ne tile |
ne _ TiledECEFNaturalEarth tilesPerFaceSide: 3 samplesPerTileSide: 101.
tile _ ne newTileSamples.
"Last position of a tile is equivalent to first position in next tile"
1 to: 6 do: [ :faceIndex |
0 to: 1 do: [ :i |
0 to: 1 do: [ :j |
ne
ecefOfFace: faceIndex
tilePosition: j@i
positionInTile: 101@50
samples: tile
into: [ :ecef1 :pointBelongsInFace1 :pointBelongsInExtendedFace1 |
ne
ecefOfFace: faceIndex
tilePosition: j+1@i
positionInTile: 1@50
samples: tile
into: [ :ecef2 :pointBelongsInFace2 :pointBelongsInExtendedFace2 |
(pointBelongsInFace1 and: pointBelongsInFace2)
ifTrue: [ self assert: ecef1 = ecef2 ]
ifFalse: [ self halt ]]].
ne
ecefOfFace: faceIndex
tilePosition: j@i
positionInTile: 50@101
samples: tile
into: [ :ecef3 :pointBelongsInFace3 :pointBelongsInExtendedFace3 |
ne
ecefOfFace: faceIndex
tilePosition: j@(i+1)
positionInTile: 50@1
samples: tile
into: [ :ecef4 :pointBelongsInFace4 :pointBelongsInExtendedFace4 |
(pointBelongsInFace3 and: pointBelongsInFace4)
ifTrue: [ self assert: ecef3 = ecef4 ]
ifFalse: [ self halt ]]]]]]! !
!TiledECEFSampledWorldTest methodsFor: 'testing' stamp: 'jmv 3/23/2016 16:42'!
test05
"
TiledECEFSampledWorldTest new test05
"
| cornerEcef ne tile faceIndex |
cornerEcef _ (1 @ -1 @ 1) normalized * GPSPosition wgs84SemiMajorAxis.
ne _ TiledECEFNaturalEarth tilesPerFaceSide: 3 samplesPerTileSide: 101.
tile _ ne newTileSamples.
faceIndex _ ne bestFaceFor: cornerEcef.
ne withTileAndPositionInTileOf: cornerEcef atFace: faceIndex evaluate: [ :tilePos :pos |
"test preconditions. If fail, rethink test."
self assert: faceIndex = 1.
self assert: tilePos = (2@2).
self assert: (pos - (66.5352128002918@66.5352128002918)) r < 1.0e-12.
"Now the actual test. The ecef for the corner is the same coming from any of the three faces"
ne
ecefOfFace: 1
tilePosition: 2@2
positionInTile: pos
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: (ecef - cornerEcef) length < 1e-6 ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 2
tilePosition: 2@0
positionInTile: pos x @ (100-pos y)
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: (ecef - cornerEcef) length < 1e-6 ]
ifFalse: [ self halt ]].
ne
ecefOfFace: 3
tilePosition: 0@0
positionInTile: (100-pos x) @ (100-pos y)
samples: tile
into: [ :ecef :pointBelongsInFace :pointBelongsInExtendedFace |
pointBelongsInFace
ifTrue: [ self assert: (ecef - cornerEcef) length < 1e-6 ]
ifFalse: [ self halt ]]]! !
!TiledECEFVariableSampledDEMTest methodsFor: 'testing' stamp: 'jmv 3/3/2016 11:51'!
test01
"
TiledECEFVariableSampledDEMTest new test01
"
| dem demH distanceToSphere1 distanceToSphere3 geoid geoidH lat0 lat1 long0 long1 sphereECEF sphericCubeDEM surfaceECEF |
dem _ ShuttleRadarTopographyMission3 getInstance.
geoid _ EGM96Geoid5 getInstance.
sphericCubeDEM _ TiledECEFVariableSampledDEM getInstance.
sphericCubeDEM sourceDEM: dem geoid: geoid.
lat0 _ -34.7. lat1 _ -34.3. "South"
long0 _ -58.7. long1 _ -58.3. "West"
long0 to: long1 count: 100 do: [ :long |
lat0 to: lat1 count: 100 do: [ :lat |
geoidH _ geoid atLatitude: lat longitude: long.
demH _ dem atLatitude: lat longitude: long.
surfaceECEF _ (GPSPosition latitude: lat longitude: long altitude: geoidH+demH) asECEF.
sphereECEF _ surfaceECEF normalized * GPSPosition wgs84SemiMajorAxis.
distanceToSphere1 _ surfaceECEF length - sphereECEF length.
self assert: (distanceToSphere1 abs - (surfaceECEF - sphereECEF) length) abs < 1e-8 description: 'surfaceECEF and sphereECEF must be colinear'.
distanceToSphere3 _ sphericCubeDEM atECEF: sphereECEF.
self assert: (distanceToSphere1 - distanceToSphere3) abs < 10 description: 'Spheric ECEF DEM not equal source dataset'
]]! !
!TrueMarbleTest methodsFor: 'setUp - tearDown' stamp: 'jmv 3/4/2016 10:39'!
setUp
instance _ TrueMarble getInstance! !
!TrueMarbleTest methodsFor: 'testing' stamp: 'jmv 1/22/2016 16:18'!
testGlobaPixelPosition
"See TrueMarble class comment"
| latLongForHalfAPixel lastX lastY |
latLongForHalfAPixel _ TrueMarble sampleWidthSeconds/3600*0.5.
lastX _ TrueMarble tileWidthInSamples * TrueMarble tileCount x - 1.
lastY _ TrueMarble tileHeightInSamples * TrueMarble tileCount y - 1.
self assert: (instance globalPixelPositionForLatitude: 90-latLongForHalfAPixel longitude: -180+latLongForHalfAPixel) r < 1.0e-10.
self assert: ((instance globalPixelPositionForLatitude: 90-latLongForHalfAPixel longitude: 180-latLongForHalfAPixel) - (lastX@0)) r < 1.0e-10.
self assert: ((instance globalPixelPositionForLatitude: -90+latLongForHalfAPixel longitude: -180+latLongForHalfAPixel) - (0@lastY)) r < 1.0e-10.
self assert: ((instance globalPixelPositionForLatitude: -90+latLongForHalfAPixel longitude: 180-latLongForHalfAPixel) - (lastX@lastY)) r < 1.0e-10.
self shouldnt: [ instance atLatitude: 90 longitude: -180 ] raise: Error.
self shouldnt: [ instance atLatitude: -90 longitude: -180 ] raise: Error.
self shouldnt: [ instance atLatitude: 90 longitude: 180 ] raise: Error.
self shouldnt: [ instance atLatitude: -90 longitude: 180 ] raise: Error.
self shouldnt: [ instance atLatitude: 90 longitude: 179.999999 ] raise: Error.
self shouldnt: [ instance atLatitude: -90 longitude: 179.999999 ] raise: Error.! !
!MeridiansAndParallelsSampledWorld methodsFor: 'accessing' stamp: 'jmv 5/3/2016 15:15'!
atGPSPosition: aGPSPosition
^self atLatitude: aGPSPosition latitude longitude: aGPSPosition longitude! !
!MeridiansAndParallelsSampledWorld methodsFor: 'accessing' stamp: 'jmv 7/13/2016 15:08:39'!
atLatitude: lat longitude: lon
"latitude in -90.0 (south pole) to 90.0 (north pole)
longitude in -180.0 (west) to 180.0 (east)"
| b g r c1 c2 c3 k k2 k3 k1 |
"
(lon + 69) abs < 0.001 ifTrue: [ ^ Color gray: 0.3 ].
(lat + 41) abs < 0.001 ifTrue: [ ^ Color gray: 0.4 ].
"
"ok para AgMacro and AgMicro"
k _ nominalGSD / 10000 * 2.
k _ 10.0 raisedToInteger: k log rounded.
k1 _ nominalGSD / 10000 * 1.5.
c1 _ (lat-(lat roundTo: k)) abs min: (lon-(lon roundTo: k)) abs.
c2 _ (lat-(lat roundTo: k*10)) abs min: (lon-(lon roundTo: k*10)) abs.
c3 _ (lat-(lat roundTo: k*50)) abs min: (lon-(lon roundTo: k*50)) abs.
" {c1. c2. c2 } print."
" (c2 / c3 between: 0.5 and: 2.1) ifTrue: [ ^Color black ].
(c1 / c2 between: 0.5 and: 2.1) ifTrue: [ ^Color gray: 0.2].
(c1 / c3 between: 0.5 and: 2.1) ifTrue: [ ^Color gray: 0.2]."
" (c3 /k < 5) ifTrue: [ ^Color black ].
(c2 /k < 1) ifTrue: [ ^Color gray: 0.3].
(c1 /k < 0.1) ifTrue: [ ^Color gray: 0.6]."
k3 _ 0.05*1.3 * 1.5. "larger => wider AA (softer)"
k2 _ 0.05*0.3. "larger => wider lines"
c1 _ c1 /k1 - k2 / k3.
k3 _ 0.05*1.3 * 2. "larger => wider AA (softer)"
k2 _ 0.05*1.0. "larger => wider lines"
c2 _ c2 /k1 - k2 / k3.
k3 _ 0.05*1.3 * 2. "larger => wider AA (softer)"
k2 _ 0.05*2. "larger => wider lines"
c3 _ c3 /k1 - k2 / k3.
r _ c1 min: 1 max: 0.
g _ c2 min: 1 max: 0.
b _ c3 min: 1 max: 0.
^ Color gray: (r*0.15) + (g*0.25) + (b*0.6)! !
!MeridiansAndParallelsSampledWorld methodsFor: 'accessing' stamp: 'jmv 7/12/2016 14:40:35'!
nominalGSD: meters
nominalGSD _ meters! !
!SampledTile methodsFor: 'testing' stamp: 'jmv 3/23/2016 12:00'!
isNull
^samples isNil! !
!SampledTile methodsFor: 'accessing' stamp: 'jmv 3/23/2016 13:18'!
sampleCount
^ samples ifNil: [ 0 ] ifNotNil: [ :s | s scalarSize ]! !
!SampledTile methodsFor: 'accessing' stamp: 'jmv 3/23/2016 11:46'!
samples
^samples! !
!SampledTile methodsFor: 'accessing' stamp: 'jmv 3/23/2016 11:46'!
samples: data
samples _ data! !
!SampledTile class methodsFor: 'instance creation' stamp: 'jmv 3/23/2016 11:47'!
null
nullInstance ifNil: [
nullInstance _ self samples: nil ].
^ nullInstance! !
!SampledTile class methodsFor: 'instance creation' stamp: 'jmv 3/23/2016 11:46'!
samples: data
^self new samples: data! !
!ValidableTile methodsFor: 'testing' stamp: 'jmv 3/23/2016 12:41'!
needsValidation
^needsValidation! !
!ValidableTile methodsFor: 'accessing' stamp: 'jmv 3/23/2016 12:38'!
samples: data
needsValidation _ data notNil.
super samples: data! !
!ValidableTile methodsFor: 'accessing' stamp: 'jmv 3/23/2016 12:42'!
validated
needsValidation _ false! !
!TiledSampledWorld methodsFor: 'accessing' stamp: 'jmv 8/17/2018 10:01:07'!
atLatitude: lat longitude: lon sampleArea: scalarSampleSizeMeters
"scalarSampleSizeMeters is a number that usually the gsd in meters of the destination.
It might be used to apply some low pass filtering for certain datasets.
For example, GoogleSampledWorld supports using source tiles with higher resolution than destination, and apply a low pass, for maximum quality.
Default implementation ignores the extra parameter."
^ self atLatitude: lat longitude: lon! !
!TiledSampledWorld methodsFor: 'accessing' stamp: 'lpc 4/26/2016 15:25'!
datasetPath
^ datasetPath! !
!TiledSampledWorld methodsFor: 'accessing' stamp: 'jmv 9/11/2015 15:44'!
datasetPath: aString
datasetPath _ aString! !
!TiledSampledWorld methodsFor: 'initialization' stamp: 'jmv 11/10/2015 13:05'!
initialize
allTiles _ Array2D extent: self class tileCount.
cachedTiles _ OrderedCollection new! !
!TiledSampledWorld methodsFor: 'private' stamp: 'jmv 11/9/2015 16:23'!
sampleTile: tile at: zeroBasedPosition
"Subclasses must implement this method.
zeroBasedPosition = 0@0 means topLeft.
Subclasses using Forms should answer:
^tile colorAt: zeroBasedPosition
Subclasses using Matrices (FloatMatrix, ByteMatrix) or any Array2D should answer:
^tile at: zeroBasedPosition+1
because Matrices (and Array2D) are 1-based.
Something else. If using NearestNeighbor, just round argument. But if using bilinear interpolation:
Bilinear Interpolation requires adding (and repeating):
- as a last row, the first row of the tile below
- as a last column, the first column of the tile to the right
As examples of this, ShuttleRadarTopographyMission3 DEM format files are 1201x1201, and they already include
these repeated row and column.
But EGM96Geoid5 files doesn't. The file is 4320 x 2161, so in #loadFromFile, and extra column is added, repeating first column. No need to do it for the rows!!
"
self subclassResponsibility! !
!TiledSampledWorld methodsFor: 'examples' stamp: 'jmv 8/9/2018 17:34:38'!
demoResult: extent
^ FloatImage extent: extent! !
!TiledSampledWorld methodsFor: 'examples' stamp: 'jmv 8/17/2018 17:03:11'!
example: aString lat0: lat0 long0: long0 pixelExtent: pixelExtent gsdArcSeconds: gsdArcSeconds
"Result is in an equal spaced latLong grid
gsdArcSeconds gives that spacing"
| demo lat1 long1 filename pixelSizeOnGroundMeters |
filename _ name, '-', aString.
lat1 _ lat0 - (pixelExtent y-1 * gsdArcSeconds / 3600.0).
long1 _ long0 + (pixelExtent x-1 * gsdArcSeconds / 3600.0).
demo _ self demoResult: pixelExtent.
"On a latLong grid, gsd in the vertical direction almost doesn't change. Compute it and use it for sample filtering.
Truth is that on a latLong grid vertical resolution is not the same as horizontal resolution, especially at high latitudes.
We'd need an area filter that (in meters) gets horizontally narrower as we get closer to the poles.