1 /******************************************************************************
2  *
3  * Project:  PROJ
4  * Purpose:  Test ISO19111:2019 implementation
5  * Author:   Even Rouault <even dot rouault at spatialys dot com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "gtest_include.h"
30 
31 #include "proj/io.hpp"
32 #include "proj/metadata.hpp"
33 #include "proj/util.hpp"
34 
35 using namespace osgeo::proj::io;
36 using namespace osgeo::proj::metadata;
37 using namespace osgeo::proj::util;
38 
39 // ---------------------------------------------------------------------------
40 
TEST(metadata,citation)41 TEST(metadata, citation) {
42     Citation c("my citation");
43     Citation c2(c);
44     ASSERT_TRUE(c2.title().has_value());
45     ASSERT_EQ(*(c2.title()), "my citation");
46 }
47 
48 // ---------------------------------------------------------------------------
49 
equals(ExtentNNPtr extent1,ExtentNNPtr extent2)50 static bool equals(ExtentNNPtr extent1, ExtentNNPtr extent2) {
51     return extent1->contains(extent2) && extent2->contains(extent1);
52 }
53 
equals(GeographicExtentNNPtr extent1,GeographicExtentNNPtr extent2)54 static bool equals(GeographicExtentNNPtr extent1,
55                    GeographicExtentNNPtr extent2) {
56     return extent1->contains(extent2) && extent2->contains(extent1);
57 }
58 
getBBox(ExtentNNPtr extent)59 static GeographicExtentNNPtr getBBox(ExtentNNPtr extent) {
60     assert(extent->geographicElements().size() == 1);
61     return extent->geographicElements()[0];
62 }
63 
TEST(metadata,extent)64 TEST(metadata, extent) {
65     Extent::create(
66         optional<std::string>(), std::vector<GeographicExtentNNPtr>(),
67         std::vector<VerticalExtentNNPtr>(), std::vector<TemporalExtentNNPtr>());
68 
69     auto world = Extent::createFromBBOX(-180, -90, 180, 90);
70     EXPECT_TRUE(world->isEquivalentTo(world.get()));
71     EXPECT_TRUE(world->contains(world));
72 
73     auto west_hemisphere = Extent::createFromBBOX(-180, -90, 0, 90);
74     EXPECT_TRUE(!world->isEquivalentTo(west_hemisphere.get()));
75     EXPECT_TRUE(world->contains(west_hemisphere));
76     EXPECT_TRUE(!west_hemisphere->contains(world));
77 
78     auto world_inter_world = world->intersection(world);
79     ASSERT_TRUE(world_inter_world != nullptr);
80     EXPECT_TRUE(equals(NN_CHECK_ASSERT(world_inter_world), world));
81 
82     auto france = Extent::createFromBBOX(-5, 40, 12, 51);
83     EXPECT_TRUE(france->contains(france));
84     EXPECT_TRUE(world->contains(france));
85     EXPECT_TRUE(!france->contains(
86         world)); // We are only speaking about geography here ;-)
87     EXPECT_TRUE(world->intersects(france));
88     EXPECT_TRUE(france->intersects(world));
89 
90     auto france_inter_france = france->intersection(france);
91     ASSERT_TRUE(france_inter_france != nullptr);
92     EXPECT_TRUE(equals(NN_CHECK_ASSERT(france_inter_france), france));
93 
94     auto france_inter_world = france->intersection(world);
95     ASSERT_TRUE(france_inter_world != nullptr);
96     EXPECT_TRUE(equals(NN_CHECK_ASSERT(france_inter_world), france));
97 
98     auto world_inter_france = world->intersection(france);
99     ASSERT_TRUE(world_inter_france != nullptr);
100     EXPECT_TRUE(equals(NN_CHECK_ASSERT(world_inter_france), france));
101 
102     auto france_shifted =
103         Extent::createFromBBOX(-5 + 5, 40 + 5, 12 + 5, 51 + 5);
104     EXPECT_TRUE(france->intersects(france_shifted));
105     EXPECT_TRUE(france_shifted->intersects(france));
106     EXPECT_TRUE(!france->contains(france_shifted));
107     EXPECT_TRUE(!france_shifted->contains(france));
108 
109     auto europe = Extent::createFromBBOX(-30, 25, 30, 70);
110     EXPECT_TRUE(europe->contains(france));
111     EXPECT_TRUE(!france->contains(europe));
112 
113     auto france_inter_europe = france->intersection(europe);
114     ASSERT_TRUE(france_inter_europe != nullptr);
115     EXPECT_TRUE(equals(NN_CHECK_ASSERT(france_inter_europe), france));
116 
117     auto europe_intersects_france = europe->intersection(france);
118     ASSERT_TRUE(europe_intersects_france != nullptr);
119     EXPECT_TRUE(equals(NN_CHECK_ASSERT(europe_intersects_france), france));
120 
121     auto nz = Extent::createFromBBOX(155.0, -60.0, -170.0, -25.0);
122     EXPECT_TRUE(nz->contains(nz));
123     EXPECT_TRUE(world->contains(nz));
124     EXPECT_TRUE(nz->intersects(world));
125     EXPECT_TRUE(world->intersects(nz));
126     EXPECT_TRUE(!nz->contains(world));
127     EXPECT_TRUE(!nz->contains(france));
128     EXPECT_TRUE(!france->contains(nz));
129     EXPECT_TRUE(!nz->intersects(france));
130     EXPECT_TRUE(!france->intersects(nz));
131 
132     {
133         auto nz_inter_world = nz->intersection(world);
134         ASSERT_TRUE(nz_inter_world != nullptr);
135         EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_world), nz));
136     }
137 
138     {
139         auto nz_inter_world = getBBox(nz)->intersection(getBBox(world));
140         ASSERT_TRUE(nz_inter_world != nullptr);
141         EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_world), getBBox(nz)));
142     }
143 
144     {
145         auto world_inter_nz = world->intersection(nz);
146         ASSERT_TRUE(world_inter_nz != nullptr);
147         EXPECT_TRUE(equals(NN_CHECK_ASSERT(world_inter_nz), nz));
148     }
149 
150     {
151         auto world_inter_nz = getBBox(world)->intersection(getBBox(nz));
152         ASSERT_TRUE(world_inter_nz != nullptr);
153         EXPECT_TRUE(equals(NN_CHECK_ASSERT(world_inter_nz), getBBox(nz)));
154     }
155 
156     EXPECT_TRUE(nz->intersection(france) == nullptr);
157     EXPECT_TRUE(france->intersection(nz) == nullptr);
158 
159     auto bbox_antimeridian_north =
160         Extent::createFromBBOX(155.0, 10.0, -170.0, 30.0);
161     EXPECT_TRUE(!nz->contains(bbox_antimeridian_north));
162     EXPECT_TRUE(!bbox_antimeridian_north->contains(nz));
163     EXPECT_TRUE(!nz->intersects(bbox_antimeridian_north));
164     EXPECT_TRUE(!bbox_antimeridian_north->intersects(nz));
165     EXPECT_TRUE(!nz->intersection(bbox_antimeridian_north));
166     EXPECT_TRUE(!bbox_antimeridian_north->intersection(nz));
167 
168     auto nz_pos_long = Extent::createFromBBOX(155.0, -60.0, 180.0, -25.0);
169     EXPECT_TRUE(nz->contains(nz_pos_long));
170     EXPECT_TRUE(!nz_pos_long->contains(nz));
171     EXPECT_TRUE(nz->intersects(nz_pos_long));
172     EXPECT_TRUE(nz_pos_long->intersects(nz));
173     auto nz_inter_nz_pos_long = nz->intersection(nz_pos_long);
174     ASSERT_TRUE(nz_inter_nz_pos_long != nullptr);
175     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_nz_pos_long), nz_pos_long));
176     auto nz_pos_long_inter_nz = nz_pos_long->intersection(nz);
177     ASSERT_TRUE(nz_pos_long_inter_nz != nullptr);
178     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_pos_long_inter_nz), nz_pos_long));
179 
180     auto nz_neg_long = Extent::createFromBBOX(-180.0, -60.0, -170.0, -25.0);
181     EXPECT_TRUE(nz->contains(nz_neg_long));
182     EXPECT_TRUE(!nz_neg_long->contains(nz));
183     EXPECT_TRUE(nz->intersects(nz_neg_long));
184     EXPECT_TRUE(nz_neg_long->intersects(nz));
185     auto nz_inter_nz_neg_long = nz->intersection(nz_neg_long);
186     ASSERT_TRUE(nz_inter_nz_neg_long != nullptr);
187     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_nz_neg_long), nz_neg_long));
188     auto nz_neg_long_inter_nz = nz_neg_long->intersection(nz);
189     ASSERT_TRUE(nz_neg_long_inter_nz != nullptr);
190     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_neg_long_inter_nz), nz_neg_long));
191 
192     auto nz_smaller = Extent::createFromBBOX(160, -55.0, -175.0, -30.0);
193     EXPECT_TRUE(nz->contains(nz_smaller));
194     EXPECT_TRUE(!nz_smaller->contains(nz));
195 
196     auto nz_pos_long_shifted_west =
197         Extent::createFromBBOX(150.0, -60.0, 175.0, -25.0);
198     EXPECT_TRUE(!nz->contains(nz_pos_long_shifted_west));
199     EXPECT_TRUE(!nz_pos_long_shifted_west->contains(nz));
200     EXPECT_TRUE(nz->intersects(nz_pos_long_shifted_west));
201     EXPECT_TRUE(nz_pos_long_shifted_west->intersects(nz));
202 
203     auto nz_smaller_shifted = Extent::createFromBBOX(165, -60.0, -170.0, -25.0);
204     EXPECT_TRUE(!nz_smaller->contains(nz_smaller_shifted));
205     EXPECT_TRUE(!nz_smaller_shifted->contains(nz_smaller));
206     EXPECT_TRUE(nz_smaller->intersects(nz_smaller_shifted));
207     EXPECT_TRUE(nz_smaller_shifted->intersects(nz_smaller));
208 
209     auto nz_shifted = Extent::createFromBBOX(165.0, -60.0, -160.0, -25.0);
210     auto nz_intersect_nz_shifted = nz->intersection(nz_shifted);
211     ASSERT_TRUE(nz_intersect_nz_shifted != nullptr);
212     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_intersect_nz_shifted),
213                        Extent::createFromBBOX(165, -60.0, -170.0, -25.0)));
214 
215     auto nz_inter_nz_smaller = nz->intersection(nz_smaller);
216     ASSERT_TRUE(nz_inter_nz_smaller != nullptr);
217     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_nz_smaller), nz_smaller));
218 
219     auto nz_smaller_inter_nz = nz_smaller->intersection(nz);
220     ASSERT_TRUE(nz_smaller_inter_nz != nullptr);
221     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_smaller_inter_nz), nz_smaller));
222 
223     auto world_smaller = Extent::createFromBBOX(-179, -90, 179, 90);
224     EXPECT_TRUE(!world_smaller->contains(nz));
225     EXPECT_TRUE(!nz->contains(world_smaller));
226 
227     auto nz_inter_world_smaller = nz->intersection(world_smaller);
228     ASSERT_TRUE(nz_inter_world_smaller != nullptr);
229     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_world_smaller),
230                        Extent::createFromBBOX(155, -60, 179, -25)));
231 
232     auto world_smaller_inter_nz = world_smaller->intersection(nz);
233     ASSERT_TRUE(world_smaller_inter_nz != nullptr);
234     EXPECT_TRUE(equals(NN_CHECK_ASSERT(world_smaller_inter_nz),
235                        Extent::createFromBBOX(155, -60, 179, -25)));
236 
237     auto world_smaller_east = Extent::createFromBBOX(-179, -90, 150, 90);
238     EXPECT_TRUE(!world_smaller_east->contains(nz));
239     EXPECT_TRUE(!nz->contains(world_smaller_east));
240 
241     auto nz_inter_world_smaller_east = nz->intersection(world_smaller_east);
242     ASSERT_TRUE(nz_inter_world_smaller_east != nullptr);
243     EXPECT_EQ(nn_dynamic_pointer_cast<GeographicBoundingBox>(
244                   nz_inter_world_smaller_east->geographicElements()[0])
245                   ->westBoundLongitude(),
246               -179);
247     EXPECT_EQ(nn_dynamic_pointer_cast<GeographicBoundingBox>(
248                   nz_inter_world_smaller_east->geographicElements()[0])
249                   ->eastBoundLongitude(),
250               -170);
251     EXPECT_TRUE(equals(NN_CHECK_ASSERT(nz_inter_world_smaller_east),
252                        Extent::createFromBBOX(-179, -60, -170, -25)));
253 
254     auto world_smaller_east_inter_nz = world_smaller_east->intersection(nz);
255     ASSERT_TRUE(world_smaller_east_inter_nz != nullptr);
256     EXPECT_EQ(nn_dynamic_pointer_cast<GeographicBoundingBox>(
257                   world_smaller_east_inter_nz->geographicElements()[0])
258                   ->westBoundLongitude(),
259               -179);
260     EXPECT_EQ(nn_dynamic_pointer_cast<GeographicBoundingBox>(
261                   world_smaller_east_inter_nz->geographicElements()[0])
262                   ->eastBoundLongitude(),
263               -170);
264     EXPECT_TRUE(equals(NN_CHECK_ASSERT(world_smaller_east_inter_nz),
265                        Extent::createFromBBOX(-179, -60, -170, -25)));
266 
267     auto east_hemisphere = Extent::createFromBBOX(0, -90, 180, 90);
268     auto east_hemisphere_inter_nz = east_hemisphere->intersection(nz);
269     ASSERT_TRUE(east_hemisphere_inter_nz != nullptr);
270     EXPECT_TRUE(equals(NN_CHECK_ASSERT(east_hemisphere_inter_nz),
271                        Extent::createFromBBOX(155.0, -60.0, 180.0, -25.0)));
272 
273     auto minus_180_to_156 = Extent::createFromBBOX(-180, -90, 156, 90);
274     auto minus_180_to_156_inter_nz = minus_180_to_156->intersection(nz);
275     ASSERT_TRUE(minus_180_to_156_inter_nz != nullptr);
276     EXPECT_TRUE(equals(NN_CHECK_ASSERT(minus_180_to_156_inter_nz),
277                        Extent::createFromBBOX(-180.0, -60.0, -170.0, -25.0)));
278 }
279 
280 // ---------------------------------------------------------------------------
281 
TEST(metadata,identifier_empty)282 TEST(metadata, identifier_empty) {
283     auto id(Identifier::create());
284     Identifier id2(*id);
285     ASSERT_TRUE(!id2.authority().has_value());
286     ASSERT_TRUE(id2.code().empty());
287     ASSERT_TRUE(!id2.codeSpace().has_value());
288     ASSERT_TRUE(!id2.version().has_value());
289     ASSERT_TRUE(!id2.description().has_value());
290 }
291 
292 // ---------------------------------------------------------------------------
293 
TEST(metadata,identifier_properties)294 TEST(metadata, identifier_properties) {
295     PropertyMap properties;
296     properties.set(Identifier::AUTHORITY_KEY, "authority");
297     properties.set(Identifier::CODESPACE_KEY, "codespace");
298     properties.set(Identifier::VERSION_KEY, "version");
299     properties.set(Identifier::DESCRIPTION_KEY, "description");
300     auto id(Identifier::create("my code", properties));
301     Identifier id2(*id);
302     ASSERT_TRUE(id2.authority().has_value());
303     ASSERT_EQ(*(id2.authority()->title()), "authority");
304     ASSERT_EQ(id2.code(), "my code");
305     ASSERT_TRUE(id2.codeSpace().has_value());
306     ASSERT_EQ(*(id2.codeSpace()), "codespace");
307     ASSERT_TRUE(id2.version().has_value());
308     ASSERT_EQ(*(id2.version()), "version");
309     ASSERT_TRUE(id2.description().has_value());
310     ASSERT_EQ(*(id2.description()), "description");
311 }
312 
313 // ---------------------------------------------------------------------------
314 
TEST(metadata,identifier_code_integer)315 TEST(metadata, identifier_code_integer) {
316     PropertyMap properties;
317     properties.set(Identifier::CODE_KEY, 1234);
318     auto id(Identifier::create(std::string(), properties));
319     ASSERT_EQ(id->code(), "1234");
320 }
321 
322 // ---------------------------------------------------------------------------
323 
TEST(metadata,identifier_code_string)324 TEST(metadata, identifier_code_string) {
325     PropertyMap properties;
326     properties.set(Identifier::CODE_KEY, "1234");
327     auto id(Identifier::create(std::string(), properties));
328     ASSERT_EQ(id->code(), "1234");
329 }
330 
331 // ---------------------------------------------------------------------------
332 
TEST(metadata,identifier_code_invalid_type)333 TEST(metadata, identifier_code_invalid_type) {
334     PropertyMap properties;
335     properties.set(Identifier::CODE_KEY, true);
336     ASSERT_THROW(Identifier::create(std::string(), properties),
337                  InvalidValueTypeException);
338 }
339 
340 // ---------------------------------------------------------------------------
341 
TEST(metadata,identifier_authority_citation)342 TEST(metadata, identifier_authority_citation) {
343     PropertyMap properties;
344     properties.set(Identifier::AUTHORITY_KEY,
345                    nn_make_shared<Citation>("authority"));
346     auto id(Identifier::create(std::string(), properties));
347     ASSERT_TRUE(id->authority().has_value());
348     ASSERT_EQ(*(id->authority()->title()), "authority");
349 }
350 
351 // ---------------------------------------------------------------------------
352 
TEST(metadata,identifier_authority_invalid_type)353 TEST(metadata, identifier_authority_invalid_type) {
354     PropertyMap properties;
355     properties.set(Identifier::AUTHORITY_KEY, true);
356     ASSERT_THROW(Identifier::create(std::string(), properties),
357                  InvalidValueTypeException);
358 }
359 
360 // ---------------------------------------------------------------------------
361 
TEST(metadata,id)362 TEST(metadata, id) {
363     auto in_wkt = "ID[\"EPSG\",4946,1.5,\n"
364                   "    CITATION[\"my citation\"],\n"
365                   "    URI[\"urn:ogc:def:crs:EPSG::4946\"]]";
366     auto id =
367         nn_dynamic_pointer_cast<Identifier>(WKTParser().createFromWKT(in_wkt));
368     ASSERT_TRUE(id != nullptr);
369 
370     EXPECT_TRUE(id->authority().has_value());
371     EXPECT_EQ(*(id->authority()->title()), "my citation");
372     EXPECT_EQ(*(id->codeSpace()), "EPSG");
373     EXPECT_EQ(id->code(), "4946");
374     EXPECT_TRUE(id->version().has_value());
375     EXPECT_EQ(*(id->version()), "1.5");
376     EXPECT_TRUE(id->uri().has_value());
377     EXPECT_EQ(*(id->uri()), "urn:ogc:def:crs:EPSG::4946");
378 
379     auto got_wkt = id->exportToWKT(WKTFormatter::create().get());
380     EXPECT_EQ(got_wkt, in_wkt);
381 }
382 
383 // ---------------------------------------------------------------------------
384 
TEST(metadata,Identifier_isEquivalentName)385 TEST(metadata, Identifier_isEquivalentName) {
386     EXPECT_TRUE(Identifier::isEquivalentName("Central_Meridian",
387                                              "Central_- ()/Meridian"));
388 
389     EXPECT_TRUE(Identifier::isEquivalentName("\xc3\xa1", "a"));
390 
391     EXPECT_TRUE(Identifier::isEquivalentName("a", "\xc3\xa1"));
392 
393     EXPECT_TRUE(Identifier::isEquivalentName("\xc3\xa4", "\xc3\xa1"));
394 }
395