diff --git a/Rakefile b/Rakefile index 3ed44fef..3e1e4003 100644 --- a/Rakefile +++ b/Rakefile @@ -16,3 +16,5 @@ RSpec::Core::RakeTask.new(:spec) do |task| end task :default => :spec + +Dir['tasks/*.rake'].each { |f| load f } diff --git a/spec/fixtures/README.md b/spec/fixtures/README.md new file mode 100644 index 00000000..a6d153dc --- /dev/null +++ b/spec/fixtures/README.md @@ -0,0 +1,26 @@ +# Fixtures + +Every file in this directory is generated by `tasks/fixtures.rake`: + +```sh +bundle exec rake fixtures:build +``` + +If you need a new case, add it to the task and regenerate, so that the next person can see what +a fixture contains without unzipping it. + +The only external tool involved is `ogr2ogr`, which writes the shapefile components and is +already needed to run the suite. The JPEGs embedded in `kmz_with_images.kmz` come from a +constant in the task. + +The geometry is deliberately dull: squares on a regular grid, in NAD83 / UTM zone 11N for the +shapefiles and in round degrees for the KML. What matters in each fixture is its structure +rather than its coordinates — a missing `.shx`, a `.shx` whose basename does not match the +`.shp`, a `__MACOSX` decoy carrying a real extension, a single-vertex LineString, a null +geometry, a name longer than the name column. The specs assert on feature counts and on those +structures, so regenerating with different coordinates is safe, while changing a count, a +component filename, or a placemark name is not. + +Regenerating rewrites the archives in place, and their entry timestamps change every run, so +expect git to report them modified even when nothing about their contents has. Run the suite +afterwards; it is the check that the structures survived. diff --git a/spec/fixtures/archive_containing_kmz.zip b/spec/fixtures/archive_containing_kmz.zip index c645af9a..bf518259 100644 Binary files a/spec/fixtures/archive_containing_kmz.zip and b/spec/fixtures/archive_containing_kmz.zip differ diff --git a/spec/fixtures/archive_with_multiple_kmls.zip b/spec/fixtures/archive_with_multiple_kmls.zip index 58718384..2685459d 100644 Binary files a/spec/fixtures/archive_with_multiple_kmls.zip and b/spec/fixtures/archive_with_multiple_kmls.zip differ diff --git a/spec/fixtures/archive_with_multiple_shps.zip b/spec/fixtures/archive_with_multiple_shps.zip index 24587064..e6ab29c4 100644 Binary files a/spec/fixtures/archive_with_multiple_shps.zip and b/spec/fixtures/archive_with_multiple_shps.zip differ diff --git a/spec/fixtures/archive_without_any_known_file.zip b/spec/fixtures/archive_without_any_known_file.zip index 40980f5e..ede93914 100644 Binary files a/spec/fixtures/archive_without_any_known_file.zip and b/spec/fixtures/archive_without_any_known_file.zip differ diff --git a/spec/fixtures/geo.json b/spec/fixtures/geo.json index 02929ff2..c0efad98 100644 --- a/spec/fixtures/geo.json +++ b/spec/fixtures/geo.json @@ -1,85 +1,81 @@ { - "type":"FeatureCollection", - "features":[ - { - "type":"Feature", - "properties":{ - "name":"Feature A", - "prop0": "value" - }, - "geometry":{ - "type":"Polygon", - "coordinates":[ - [ - [ - -121.30360205078127, - 51.240061941441795 - ], - [ - -120.67188818359377, - 50.95373510735458 - ], - [ - -121.19373876953127, - 50.88447544421283 - ], - [ - -121.30360205078127, - 51.240061941441795 - ] - ] - ] - } + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "name": "Feature A", + "prop0": "value" }, - { - "type":"Feature", - "properties":{ - "name":"Feature B", - "prop0": "value" - }, - "geometry":{ - "type":"Polygon", - "coordinates":[ - [ - [ - -122.41871435546877, - 51.34654878111497 - ], - [ - -121.77052099609377, - 51.53828646289825 - ], - [ - -121.75953466796877, - 51.38084670223434 - ], - [ - -121.94630224609377, - 51.195332650242506 - ], - [ - -122.14405615234377, - 51.10574365681173 - ], - [ - -122.34730322265627, - 51.11608972528383 - ], - [ - -122.41871435546877, - 51.34654878111497 - ] - ] + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 10, + 20 + ], + [ + 11, + 20 + ], + [ + 11, + 21 + ], + [ + 10, + 21 + ], + [ + 10, + 20 ] - } + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "name": "Feature B", + "prop0": "value" }, - { - "type":"Feature", - "properties":{ - "name":"Feature C", - "prop0": "value" - }, - "geometry": null + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 30, + 40 + ], + [ + 31, + 40 + ], + [ + 31, + 41 + ], + [ + 30, + 41 + ], + [ + 30, + 40 + ] + ] + ] } - ] + }, + { + "type": "Feature", + "properties": { + "name": "Feature C", + "prop0": "value" + }, + "geometry": null + } + ] } diff --git a/spec/fixtures/kml_file_with_altitude.kml b/spec/fixtures/kml_file_with_altitude.kml index 0245e9bf..7a672e64 100644 --- a/spec/fixtures/kml_file_with_altitude.kml +++ b/spec/fixtures/kml_file_with_altitude.kml @@ -1,102 +1,19 @@ - - - test.kml - - - - normal - #sn_ylw-pushpin - - - highlight - #sh_ylw-pushpin - - - - - - normal - #s_ylw-pushpin - - - highlight - #s_ylw-pushpin_hl - - - - - - Poly folder - 1 - - Poly 1 - This polygon has 3D geometry - #m_ylw-pushpin - 1 - - - - - -116.7515403029528,49.08210926772687,-5.976855754852295e-05 -116.7512693508186,49.08231630032653,-5.976855754852295e-05 -116.7510313015289,49.08248832080875,-5.976948887109756e-05 -116.7508471244029,49.08248531472098,-5.977042019367218e-05 -116.7503725125688,49.08250416936566,-5.976855754852295e-05 -116.7496625281544,49.08273345629574,-5.976855754852295e-05 -116.7489779002949,49.08298438319456,-5.977042019367218e-05 -116.7488946064397,49.08301748537436,-5.97713515162468e-05 -116.7490631198219,49.08327575277079,-5.977228283882141e-05 -116.749318497487,49.08358107512755,-5.97713515162468e-05 -116.749436515071,49.08383248565602,-5.977042019367218e-05 -116.7495781555165,49.08407072392254,-5.977042019367218e-05 -116.7498202420291,49.08413349769931,-5.977042019367218e-05 -116.7499457606038,49.08431008140729,-5.977414548397064e-05 -116.7499520489836,49.08431234390348,-5.977228283882141e-05 -116.7503115582278,49.08439165279593,-5.977321416139602e-05 -116.7506718895158,49.08425228182594,-5.977321416139602e-05 -116.7507080125254,49.08423131038275,-5.977414548397064e-05 -116.7507055933694,49.08422212975165,-5.977228283882141e-05 -116.750703739572,49.08420944209784,-5.977414548397064e-05 -116.750703393566,49.08419648783546,-5.977228283882141e-05 -116.7507042819026,49.08418317641901,-5.977228283882141e-05 -116.7507068154278,49.08416950873566,-5.97713515162468e-05 -116.7507105823926,49.0841556638061,-5.977321416139602e-05 -116.7507157192947,49.08414173187993,-5.977321416139602e-05 -116.7507223635321,49.08412762329866,-5.977228283882141e-05 -116.7507301038066,49.08411342712892,-5.977321416139602e-05 -116.7507392135647,49.08409923391582,-5.977321416139602e-05 -116.7507496923549,49.08408513361309,-5.977321416139602e-05 -116.750761266729,49.084071035675,-5.97713515162468e-05 -116.7507739353355,49.08405720996314,-5.977228283882141e-05 -116.7507879725211,49.08404356711452,-5.977228283882141e-05 -116.7508028295902,49.08403028585404,-5.977321416139602e-05 -116.7508187813398,49.08401718686463,-5.977414548397064e-05 -116.7508356894705,49.08400453971223,-5.977228283882141e-05 -116.7508534170337,49.08399234410085,-5.977321416139602e-05 -116.7508721014275,49.08398051037167,-5.977321416139602e-05 -116.7508913300066,49.0839693974536,-5.977228283882141e-05 -116.750911378468,49.08395864612137,-5.977321416139602e-05 -116.7509319715652,49.08394852564564,-5.977321416139602e-05 -116.7509529714506,49.08393921563855,-5.977228283882141e-05 -116.7509745164227,49.08393044653347,-5.977228283882141e-05 -116.7509963316859,49.08392239764715,-5.977321416139602e-05 -116.7510182793931,49.08391524859208,-5.97713515162468e-05 -116.7510404978429,49.08390872980158,-5.977042019367218e-05 -116.7510627117906,49.08390311054676,-5.977228283882141e-05 -116.7510850590848,49.08389821121501,-5.97713515162468e-05 -116.7511072653803,49.08389412116959,-5.977228283882141e-05 -116.7511291928312,49.08389102002342,-5.977228283882141e-05 -116.7511508418881,49.08388881782259,-5.977414548397064e-05 -116.7511720760536,49.08388742431816,-5.97713515162468e-05 -116.7511930318276,49.08388692975941,-5.97713515162468e-05 -116.751213297917,49.08388742321508,-5.97713515162468e-05 -116.7512330126194,49.0838886351187,-5.977228283882141e-05 -116.7512520376394,49.08389083503734,-5.977228283882141e-05 -116.7512702369284,49.08389384276833,-5.97713515162468e-05 -116.7512843268737,49.08389702155225,-5.977042019367218e-05 -116.7512876095886,49.08389783822006,-5.97713515162468e-05 -116.7513041569684,49.08390255153084,-5.97713515162468e-05 -116.7513197412218,49.08390816231408,-5.977228283882141e-05 -116.7513342258501,49.0839145803211,-5.977228283882141e-05 -116.7513476108535,49.08392180555226,-5.97713515162468e-05 -116.7513597597331,49.08392974775903,-5.977228283882141e-05 -116.751370809437,49.08393840723678,-5.977414548397064e-05 -116.7513807599652,49.08394778398574,-5.977228283882141e-05 -116.7513893383187,49.08395769750831,-5.977228283882141e-05 -116.75139654315,49.08396841766687,-5.977228283882141e-05 -116.7514023767035,49.08397949469153,-5.977228283882141e-05 -116.7514071110794,49.08399128898851,-5.977321416139602e-05 -116.7514077854947,49.08399335940517,-5.977321416139602e-05 -116.7514102002792,49.08400343956209,-5.977414548397064e-05 -116.7514119173006,49.08401612691037,-5.97713515162468e-05 -116.7514122639388,49.08402899121732,-5.977228283882141e-05 -116.7514113753453,49.08404239259393,-5.977228283882141e-05 -116.7514089789687,49.08405606058831,-5.977228283882141e-05 -116.7514052126546,49.08406981558722,-5.977321416139602e-05 -116.7514000750547,49.0840839274526,-5.977228283882141e-05 -116.751393431464,49.08409794611927,-5.977414548397064e-05 -116.7513856909318,49.08411223228784,-5.977321416139602e-05 -116.7513764448556,49.08412633530327,-5.977321416139602e-05 -116.7513659662339,49.08414043566304,-5.97713515162468e-05 -116.7513543920146,49.08415453366188,-5.977321416139602e-05 -116.7513417235442,49.08416835943742,-5.977414548397064e-05 -116.7513278234237,49.08418200264829,-5.97713515162468e-05 -116.7513128294999,49.08419528368113,-5.977228283882141e-05 -116.7512970147718,49.08420838303356,-5.977228283882141e-05 -116.7512801066886,49.08422103025315,-5.977321416139602e-05 -116.7512622417496,49.08423331558855,-5.977228283882141e-05 -116.7512436947518,49.08424505972144,-5.977228283882141e-05 -116.7512243287458,49.08425626235651,-5.977228283882141e-05 -116.7512042806811,49.08426692378845,-5.977321416139602e-05 -116.7511836875072,49.08427704431207,-5.977321416139602e-05 -116.7511626870726,49.0842864443144,-5.977228283882141e-05 -116.7511411424287,49.08429512349989,-5.977228283882141e-05 -116.7511193274749,49.08430308245879,-5.97713515162468e-05 -116.7510973791614,49.08431032148636,-5.977228283882141e-05 -116.7510751605396,49.0843168402871,-5.977228283882141e-05 -116.7510529464086,49.0843224595436,-5.977228283882141e-05 -116.7510307363199,49.08432726920987,-5.977228283882141e-05 -116.7510085298248,49.08433135923989,-5.977321416139602e-05 -116.750986739122,49.08433446065798,-5.977414548397064e-05 -116.7509648155154,49.08433675219059,-5.977228283882141e-05 -116.7509437185519,49.08433805599805,-5.977414548397064e-05 -116.7509227625858,49.08433855051116,-5.977228283882141e-05 -116.7509024954161,49.08433823691217,-5.977507680654526e-05 -116.7508826440445,49.08433693470243,-5.977321416139602e-05 -116.7508637558201,49.0843347350184,-5.977321416139602e-05 -116.7508454194455,49.0843317269277,-5.977507680654526e-05 -116.7508305095092,49.08432818650067,-5.977228283882141e-05 -116.7507919141879,49.08435059192375,-5.977321416139602e-05 -116.7507901284429,49.08435166752803,-5.977414548397064e-05 -116.7507770880808,49.08435766635651,-5.977507680654526e-05 -116.7505519150925,49.08444479611104,-5.977321416139602e-05 -116.7510549293909,49.08455589694681,-5.977321416139602e-05 -116.7515202853279,49.08439228203611,-5.977414548397064e-05 -116.7524138826689,49.08398076912792,-5.977321416139602e-05 -116.7524158057612,49.08397960383811,-5.977228283882141e-05 -116.7524167720029,49.0839780766761,-5.977228283882141e-05 -116.7524171908977,49.08397645838516,-5.97713515162468e-05 -116.7523125841168,49.08369647401459,-5.977228283882141e-05 -116.7521292884352,49.08326780492037,-5.97713515162468e-05 -116.7520054599166,49.08310831863499,-5.97713515162468e-05 -116.7518159515976,49.0827181363844,-5.977042019367218e-05 -116.7517886110814,49.08254347496219,-5.977042019367218e-05 -116.7516055433862,49.08215321621999,-5.97713515162468e-05 -116.7515925599111,49.08206557211205,-5.976855754852295e-05 -116.7515403029528,49.08210926772687,-5.976855754852295e-05 - - - - - - - Poly 2 - This is a description also - #msn_ylw-pushpin - - 1 - - - - -106.4303166516638,61.16376575099101,0 -100.7627103394292,61.95514185121152,0 -100.2655658472707,65.8275552135498,0 -107.9516815833805,65.21804505333482,0 -106.4303166516638,61.16376575099101,0 - - - - - - - + + + test.kml + + Poly folder + + Poly 1 + This is a description + 10,20,-5.976855754852295e-05 11,20,-5.976855754852295e-05 11,21,-5.976855754852295e-05 10,21,-5.976855754852295e-05 10,20,-5.976855754852295e-05 + + + Poly 2 + This is a description also + 30,40 31,40 31,41 30,41 30,40 + + + diff --git a/spec/fixtures/kml_file_with_ground_overlay.kml b/spec/fixtures/kml_file_with_ground_overlay.kml index ef7fdb37..008620fb 100644 --- a/spec/fixtures/kml_file_with_ground_overlay.kml +++ b/spec/fixtures/kml_file_with_ground_overlay.kml @@ -1,21 +1,14 @@ - - - kml_file_with_ground_overlay.kml - - Overlay folder - - Provincial Boundary - - https://example.com/wms?service=wms&request=GetMap - - - 55.4 - 52.9 - -117.4 - -122.5 - - - - + + + kml_file_with_ground_overlay.kml + + Overlay folder + + Basemap Overlay + https://example.com/wms?service=wms&request=GetMap + 41393129 + + + diff --git a/spec/fixtures/kml_file_with_ground_overlay_and_features.kml b/spec/fixtures/kml_file_with_ground_overlay_and_features.kml index c6f2ab2b..0c6b8cc4 100644 --- a/spec/fixtures/kml_file_with_ground_overlay_and_features.kml +++ b/spec/fixtures/kml_file_with_ground_overlay_and_features.kml @@ -1,53 +1,27 @@ - - - kml_file_with_ground_overlay_and_features.kml - - Overlay folder - - Provincial Boundary - - https://example.com/wms?service=wms&request=GetMap - - - 55.4 - 52.9 - -117.4 - -122.5 - - - - - Poly folder - 1 - - Poly 1 - This is a description - - 1 - - - - -104.2021395645545,60.47065556909579,0 -98.5406390057758,60.08348491099549,0 -98.40794749318894,63.7611693880032,0 -105.6700904293509,63.9562028375275,0 -104.2021395645545,60.47065556909579,0 - - - - - - - Poly 2 - This is a description also - - 1 - - - - -106.4303166516638,61.16376575099101,0 -100.7627103394292,61.95514185121152,0 -100.2655658472707,65.8275552135498,0 -107.9516815833805,65.21804505333482,0 -106.4303166516638,61.16376575099101,0 - - - - - - - + + + kml_file_with_ground_overlay_and_features.kml + + Overlay folder + + Basemap Overlay + https://example.com/wms?service=wms&request=GetMap + 41393129 + + + + Poly folder + + Poly 1 + This is a description + 10,20 11,20 11,21 10,21 10,20 + + + Poly 2 + This is a description also + 30,40 31,40 31,41 30,41 30,40 + + + diff --git a/spec/fixtures/kml_file_with_invalid_altitude.kml b/spec/fixtures/kml_file_with_invalid_altitude.kml index 73bea875..0b51be97 100644 --- a/spec/fixtures/kml_file_with_invalid_altitude.kml +++ b/spec/fixtures/kml_file_with_invalid_altitude.kml @@ -1,103 +1,19 @@ - - - kml_file_with_invalid_altitude - - - - normal - #sn_ylw-pushpin - - - highlight - #sh_ylw-pushpin - - - - - - normal - #s_ylw-pushpin - - - highlight - #s_ylw-pushpin_hl - - - - - - Poly folder - 1 - - Poly 1 - This is a description - #m_ylw-pushpin - 1 - - 1 - - - - -104.2021395645545,60.47065556909579,isNaN -98.5406390057758,60.08348491099549,isNaN -98.40794749318894,63.7611693880032,isNaN -105.6700904293509,63.9562028375275,isNaN -104.2021395645545,60.47065556909579,isNaN - - - - - - - Poly 2 - This is a description also - #msn_ylw-pushpin - - 1 - - - - -106.4303166516638,61.16376575099101,isNan -100.7627103394292,61.95514185121152,isNan -100.2655658472707,65.8275552135498,isNan -107.9516815833805,65.21804505333482,isNan -106.4303166516638,61.16376575099101,isNan - - - - - - - + + + kml_file_with_invalid_altitude + + Poly folder + + Poly 1 + This is a description + 10,20,isNaN 11,20,isNaN 11,21,isNaN 10,21,isNaN 10,20,isNaN + + + Poly 2 + This is a description also + 30,40 31,40 31,41 30,41 30,40 + + + diff --git a/spec/fixtures/kml_file_with_invalid_placemark.kml b/spec/fixtures/kml_file_with_invalid_placemark.kml index ac9af12a..313dbee9 100644 --- a/spec/fixtures/kml_file_with_invalid_placemark.kml +++ b/spec/fixtures/kml_file_with_invalid_placemark.kml @@ -1,50 +1,24 @@ - - - Invalid Placemark Test - - Invalid Placemark Test - 1 - - Point - This is a point description - - -118.0953599553694 - 50.99976018029227 - 0 - -0.009312185540276758 - 0 - 360.2334266582566 - relativeToSeaFloor - - #m_ylw-pushpin0 - - 1 - -118.0968344614107,50.9990699595207,0 - - - - Line - This is a line description - #style22 - - 1 - - -118.089675764091,51.01032558585955,0 -118.0889732713068,51.01048708669069,0 -118.0882312998693,51.01038325932011,0 -118.0877197966999,51.01036310703023,0 -118.0873122359602,51.01049795237122,0 -118.0869963579566,51.01063073053858,0 -118.0867423850594,51.01085054082176,0 -118.0861869980127,51.01098790099778,0 -118.0857272461739,51.0110873219875,0 -118.0851719812874,51.01120768069855,0 -118.0850921271063,51.01080949798535,0 -118.0849976280212,51.01051916845486,0 -118.0845868106561,51.01028844459033,0 -118.0841096483554,51.01009006296836,0 -118.0835695118624,51.00983098183438,0 -118.0829613742243,51.00953266686324,0 -118.0816432002301,51.00884818663192,0 -118.0832712945097,51.00830364503589,0 -118.0834312915085,51.00775322625655,0 -118.0777201624215,51.00940636141441,0 -118.0765236136034,51.00948248932291,0 -118.0762191929838,51.00958404332551,0 -118.0761948478318,51.01005855027813,0 -118.0768415304803,51.01013628851712,0 -118.0816751884237,51.00883791703497,0 - - - - - Invalid Line - This is a line description - #style29 - - 1 - - -118.0895974528835,51.01061493979792,0 - - - - - + + + Invalid Placemark Test + + Invalid Placemark Test + + Point + A point + 10,20 + + + Line + A line + 10,20 11,21 12,22 + + + Invalid Line + A line with one vertex + 10,20 + + + diff --git a/spec/fixtures/kml_file_with_network_link.kml b/spec/fixtures/kml_file_with_network_link.kml index 3914bdc3..d603b444 100644 --- a/spec/fixtures/kml_file_with_network_link.kml +++ b/spec/fixtures/kml_file_with_network_link.kml @@ -1,133 +1,21 @@ - - - - - Traplines of British Columbia - 1 - - - -124.2007623466872 - 54.24685760366558 - 0 - 0.7179969377242983 - 0 - 1651407.948731373 - relativeToSeaFloor - - - - - Trapline_Boundaries - - -124.2007623466872 - 54.24685760366558 - 0 - 0.7179969377242983 - 0 - 1651407.948731373 - relativeToSeaFloor - - 0 - -
- - ]]>
- - #noDirections - - - - https://openmaps.gov.bc.ca/geo/pub/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=pub:WHSE_WILDLIFE_MANAGEMENT.WAA_TRAPLINE_AREAS_SP&STYLES=2983_2984&FORMAT=application/vnd.google-earth.kml+xml&TRANSPARENT=TRUE&maxFeatures=200&format_options=KMATTR:true;KMSCORE:25;MODE:refresh;SUPEROVERLAY:false& - onStop - 2 - bbox=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]&WIDTH=[horizPixels]&HEIGHT=[vertPixels] - - - -
- - - Trapline_Boundaries_Outlined - - -124.2007623466872 - 54.24685760366558 - 0 - 0.7179969377242983 - 0 - 1651407.948731373 - relativeToSeaFloor - - 0 - -
- - ]]>
- - #noDirections - - - - https://openmaps.gov.bc.ca/geo/pub/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=pub:WHSE_WILDLIFE_MANAGEMENT.WAA_TRAPLINE_AREAS_SP&STYLES=2983&FORMAT=application/vnd.google-earth.kml+xml&TRANSPARENT=TRUE&maxFeatures=200&format_options=KMATTR:true;KMSCORE:25;MODE:refresh;SUPEROVERLAY:false& - onStop - 2 - bbox=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]&WIDTH=[horizPixels]&HEIGHT=[vertPixels] - - - -
- - - Trapline_Boundaries_Colour_Filled - - -124.2007623466872 - 54.24685760366558 - 0 - 0.7179969377242983 - 0 - 1651407.948731373 - relativeToSeaFloor - - 0 - -
- - ]]>
- - #noDirections - - - - https://openmaps.gov.bc.ca/geo/pub/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&LAYERS=pub:WHSE_WILDLIFE_MANAGEMENT.WAA_TRAPLINE_AREAS_SP&STYLES=2984&FORMAT=application/vnd.google-earth.kml+xml&TRANSPARENT=TRUE&maxFeatures=200&format_options=KMATTR:true;KMSCORE:25;MODE:refresh;SUPEROVERLAY:false& - onStop - 2 - bbox=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]&WIDTH=[horizPixels]&HEIGHT=[vertPixels] - - - -
-
-
+ + + + Remote Layers + + Remote folder + + Remote Layer 1 + https://example.com/layers/1.kmlonRequest + + + Remote Layer 2 + https://example.com/layers/2.kmlonRequest + + + Remote Layer 3 + https://example.com/layers/3.kmlonRequest + + + + diff --git a/spec/fixtures/kml_file_with_network_link_and_features.kml b/spec/fixtures/kml_file_with_network_link_and_features.kml index eacd58e3..da364a3a 100644 --- a/spec/fixtures/kml_file_with_network_link_and_features.kml +++ b/spec/fixtures/kml_file_with_network_link_and_features.kml @@ -1,48 +1,26 @@ - - - kml_file_with_network_link_and_features.kml - - Remote folder - - Remote Layer - - https://example.com/remote.kmz - onRequest - - - - - Poly folder - 1 - - Poly 1 - This is a description - - 1 - - - - -104.2021395645545,60.47065556909579,0 -98.5406390057758,60.08348491099549,0 -98.40794749318894,63.7611693880032,0 -105.6700904293509,63.9562028375275,0 -104.2021395645545,60.47065556909579,0 - - - - - - - Poly 2 - This is a description also - - 1 - - - - -106.4303166516638,61.16376575099101,0 -100.7627103394292,61.95514185121152,0 -100.2655658472707,65.8275552135498,0 -107.9516815833805,65.21804505333482,0 -106.4303166516638,61.16376575099101,0 - - - - - - - + + + kml_file_with_network_link_and_features.kml + + Remote folder + + Remote Layer + https://example.com/remote.kmzonRequest + + + + Poly folder + + Poly 1 + This is a description + 10,20 11,20 11,21 10,21 10,20 + + + Poly 2 + This is a description also + 30,40 31,40 31,41 30,41 30,40 + + + diff --git a/spec/fixtures/kml_file_without_features.kml b/spec/fixtures/kml_file_without_features.kml index c8d8337d..790dac62 100644 --- a/spec/fixtures/kml_file_without_features.kml +++ b/spec/fixtures/kml_file_without_features.kml @@ -1,10 +1,10 @@ - - - Without Features Test - - Without Features Test - 1 - - + + + Without Features Test + + Without Features Test + 1 + + diff --git a/spec/fixtures/kmz_file_features_without_placemarks.kmz b/spec/fixtures/kmz_file_features_without_placemarks.kmz index d1d4be81..1ba36c23 100644 Binary files a/spec/fixtures/kmz_file_features_without_placemarks.kmz and b/spec/fixtures/kmz_file_features_without_placemarks.kmz differ diff --git a/spec/fixtures/kmz_with_images.kmz b/spec/fixtures/kmz_with_images.kmz index f46189d6..cf3c16bb 100644 Binary files a/spec/fixtures/kmz_with_images.kmz and b/spec/fixtures/kmz_with_images.kmz differ diff --git a/spec/fixtures/long_placemark_name.kml b/spec/fixtures/long_placemark_name.kml index eb8c77b1..1deda343 100644 --- a/spec/fixtures/long_placemark_name.kml +++ b/spec/fixtures/long_placemark_name.kml @@ -1,1104 +1,86 @@ - - - - Habitat Rehabilitation - - - - Habitat Rehabilitation - - - Reshape the riverbed by excavating a single thread channel in braided sections. Some pools will be deepened in areas next to existing large woody debris structures. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Reshape the riverbed by excavating a single thread channel in braided sections. Some pools will be deepened in areas next to existing large woody debris structures.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length608.933544
SHAPE_Area7414.119023
AreaNumber3
Site1
DescriptionReshape the riverbed by excavating a single thread channel in braided sections. Some pools will be deepened in areas next to existing large woody debris structures.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8879090578541,50.21064339197977,0 -114.8880809223482,50.21044660730991,0 -114.8879308385375,50.20998217985107,0 -114.8875476593892,50.20958966913109,0 -114.8873549627254,50.20925060022238,0 -114.8874168170316,50.20902563584355,0 -114.8871471790634,50.20874942420726,0 -114.8867883572849,50.20864531985215,0 -114.8866299211813,50.20878998990126,0 -114.8867532026039,50.20893740615186,0 -114.8871234964431,50.20945343964124,0 -114.8875355347956,50.21003210748727,0 -114.8877177800608,50.21044000325888,0 -114.8874116151487,50.21048679159924,0 -114.8874128217385,50.21049006517734,0 -114.8870294971595,50.21047317685682,0 -114.8870985893313,50.21065530544941,0 -114.8874251259222,50.21070342772259,0 -114.8875101263528,50.21069973885186,0 -114.8879090578541,50.21064339197977,0 - - -
- - Plug the cut side channel at upstream end. Plug to consist of large keystons (0.75-1m diameter) with graded native material behind it. Plug width is ~20m. Plug length TBD (~20-30m). Plan will ikely include a revegetation component (live cuttings or seedlings). - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Plug the cut side channel at upstream end. Plug to consist of large keystons (0.75-1m diameter) with graded native material behind it. Plug width is ~20m. Plug length TBD (~20-30m). Plan will ikely include a revegetation component (live cuttings or seedlings).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length101.839565
SHAPE_Area697.9634
AreaNumber2
Site1
DescriptionPlug the cut side channel at upstream end. Plug to consist of large keystons (0.75-1m diameter) with graded native material behind it. Plug width is ~20m. Plug length TBD (~20-30m). Plan will ikely include a revegetation component (live cuttings or seedlings).
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8874123293503,50.21047062199627,0 -114.887446595464,50.21036653020828,0 -114.8873955648135,50.21024565503284,0 -114.8870758273894,50.21023031927297,0 -114.8870259606027,50.21028462499179,0 -114.8870274581594,50.21045219620299,0 -114.8874123293503,50.21047062199627,0 - - -
- - Regrade the riverbed. Deepen pool at select locations on the outer bend (near Large Woody Debris (LWD) structures).Excavate (shave down) inner bend for a distance of approx 10-15m from edge of water (red zone). - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Regrade the riverbed. Deepen pool at select locations on the outer bend (near Large Woody Debris (LWD) structures).Excavate (shave down) inner bend for a distance of approx 10-15m from edge of water (red zone).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length179.045454
SHAPE_Area967.995812
AreaNumber1
Site1
DescriptionRegrade the riverbed. Deepen pool at select locations on the outer bend (near Large Woody Debris (LWD) structures).Excavate (shave down) inner bend for a distance of approx 10-15m from edge of water (red zone).
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8866817189309,50.21050509779567,0 -114.8867066836611,50.2105039536786,0 -114.8866043774796,50.21040213702632,0 -114.8864749839898,50.21040977743252,0 -114.8862370584545,50.2105068308035,0 -114.8861280541413,50.21070761278311,0 -114.8861382106355,50.21088057750377,0 -114.8862333430335,50.21099368742757,0 -114.8863646927826,50.21094181663717,0 -114.8863575043463,50.21093536334981,0 -114.8863176078822,50.21089955023839,0 -114.8863151631933,50.21080383405473,0 -114.8863545654924,50.21066746996434,0 -114.8864614251565,50.21051519743553,0 -114.8866817189309,50.21050509779567,0 - - -
- - NULL - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length128.875561
SHAPE_Area633.074775
AreaNumber1
Site<Null>
Description<Null>
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8865813484495,50.21063830084091,0 -114.8866416099105,50.21061700388992,0 -114.8867539752748,50.2105919174107,0 -114.8867712398782,50.21055367791861,0 -114.8867446068547,50.21050178786945,0 -114.8866787797811,50.21050899754093,0 -114.8866817189309,50.21050509779567,0 -114.8864614251565,50.21051519743553,0 -114.8863545654924,50.21066746996434,0 -114.8863151631933,50.21080383405473,0 -114.8863176078822,50.21089955023839,0 -114.8863370916574,50.21091703997791,0 -114.8863708485668,50.21091765432367,0 -114.8865813484495,50.21063830084091,0 - - -
- - Reshape the riverbed by excavating a single thread channel in braided sections, to establish a suitable width-to-depth ratio. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Reshape the riverbed by excavating a single thread channel in braided sections, to establish a suitable width-to-depth ratio.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length310.461042
SHAPE_Area3781.510368
AreaNumber1
Site2
DescriptionReshape the riverbed by excavating a single thread channel in braided sections, to establish a suitable width-to-depth ratio.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8860048634804,50.20811224542837,0 -114.8856829166332,50.20776130159517,0 -114.8850785320103,50.20742425229492,0 -114.8847022501716,50.20729602489742,0 -114.8844393040638,50.20737215199323,0 -114.8846345768754,50.20756847992288,0 -114.8850927195239,50.20777435392688,0 -114.8854665095754,50.20804294849499,0 -114.8857254121706,50.20822615283002,0 -114.8860048634804,50.20811224542837,0 - - -
- - Regrade riverbed to create point bar on the inner bend and deepen pools in areas adjacent to Large Woody Debris (LWD) deflectors. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Regrade riverbed to create point bar on the inner bend and deepen pools in areas adjacent to Large Woody Debris (LWD) deflectors.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length163.244109
SHAPE_Area1247.342741
AreaNumber2
Site2
DescriptionRegrade riverbed to create point bar on the inner bend and deepen pools in areas adjacent to Large Woody Debris (LWD) deflectors.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8846818061751,50.20725519466589,0 -114.8845374620019,50.20708121388934,0 -114.8844955228961,50.2070233318482,0 -114.8845394909788,50.20678376465712,0 -114.8842357658108,50.20677585179882,0 -114.8842143674736,50.20709198663589,0 -114.8844226709525,50.20732901110132,0 -114.8846818061751,50.20725519466589,0 - - -
- - Regrade riverbed and add Large Woody Debris (LWD) to remove bifurcation and direct all flow into a single thread channel. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Regrade riverbed and add Large Woody Debris (LWD) to remove bifurcation and direct all flow into a single thread channel.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length126.163096
SHAPE_Area870.962457
AreaNumber3
Site2
DescriptionRegrade riverbed and add Large Woody Debris (LWD) to remove bifurcation and direct all flow into a single thread channel.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8848673440845,50.20641371331626,0 -114.8846107140283,50.20634716189176,0 -114.8843847685213,50.20659293528826,0 -114.8843322883473,50.20668955434751,0 -114.8844179536387,50.20676489142629,0 -114.8845713417439,50.20673436681719,0 -114.8848673440845,50.20641371331626,0 - - -
- - Plug avulsion at upstream end to focus all flow into a single channel. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Plug avulsion at upstream end to focus all flow into a single channel.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length103.19492
SHAPE_Area510.097132
AreaNumber1
Site3
DescriptionPlug avulsion at upstream end to focus all flow into a single channel.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8851361091786,50.20582839553368,0 -114.8851374508042,50.20554640305944,0 -114.8848975231776,50.20556345307796,0 -114.8849875067064,50.20591850527306,0 -114.8851361091786,50.20582839553368,0 - - -
- - Regrade riverbed to create point bar on inner bend and deepen pools in areas adjacent to Large Woody Debris (LWD) deflectors. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Regrade riverbed to create point bar on inner bend and deepen pools in areas adjacent to Large Woody Debris (LWD) deflectors.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length211.727296
SHAPE_Area1622.121435
AreaNumber2
Site3
DescriptionRegrade riverbed to create point bar on inner bend and deepen pools in areas adjacent to Large Woody Debris (LWD) deflectors.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8858272284819,50.20403464389021,0 -114.8855167545753,50.2038861975035,0 -114.8850351414209,50.20383458977382,0 -114.8845750082334,50.20392616528146,0 -114.8847290796691,50.20408961406233,0 -114.885050275449,50.2039955078094,0 -114.8853927936753,50.2040481529307,0 -114.885604021111,50.20417694340933,0 -114.8858272284819,50.20403464389021,0 - - -
- - Reshape the riverbed by excavating a single thred channel in braided sections to establish a suitable width. - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Reshape the riverbed by excavating a single thred channel in braided sections to establish a suitable width.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length232.444054
SHAPE_Area2998.446966
AreaNumber3
Site3
DescriptionReshape the riverbed by excavating a single thred channel in braided sections to establish a suitable width.
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.8838579995572,50.20416656005607,0 -114.8839938843529,50.20348362827393,0 -114.8833476384194,50.20350754948239,0 -114.8834005439027,50.20419749243405,0 -114.8838579995572,50.20416656005607,0 - - -
- - Work is already planned for repair of riffles stone (bank erosion). - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Work is already planned for repair of riffles stone (bank erosion).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHAPEPolygon
SHAPE_Length96.456733
SHAPE_Area547.191218
AreaNumber0
Site1
DescriptionWork is already planned for repair of riffles stone (bank erosion).
- -
- - - - - -]]>
- #PolyStyle00 - - - 0absolute - -114.886800896269,50.21114674730951,0 -114.8865566597758,50.21096362625672,0 -114.8862906628946,50.21107604208942,0 -114.8866312728018,50.2112441663506,0 -114.886800896269,50.21114674730951,0 - - -
-
-
-
+ + + + Long Placemark Names + + Long Placemark Names + + Segment description continuing well past the length of the name column so that the importer has to truncate it before the record can be written to the database (part 1) + AttributeValue 1]]> + + 11,20 12,20 12,21 11,21 11,20 + + + + Short name 2 + AttributeValue 2]]> + + 12,20 13,20 13,21 12,21 12,20 + + + + Segment description continuing well past the length of the name column so that the importer has to truncate it before the record can be written to the database (part 3) + AttributeValue 3]]> + + 13,20 14,20 14,21 13,21 13,20 + + + + Short name 4 + AttributeValue 4]]> + + 14,20 15,20 15,21 14,21 14,20 + + + + Segment description continuing well past the length of the name column so that the importer has to truncate it before the record can be written to the database (part 5) + AttributeValue 5]]> + + 15,20 16,20 16,21 15,21 15,20 + + + + Short name 6 + AttributeValue 6]]> + + 16,20 17,20 17,21 16,21 16,20 + + + + Segment description continuing well past the length of the name column so that the importer has to truncate it before the record can be written to the database (part 7) + AttributeValue 7]]> + + 17,20 18,20 18,21 17,21 17,20 + + + + Short name 8 + AttributeValue 8]]> + + 18,20 19,20 19,21 18,21 18,20 + + + + Segment description continuing well past the length of the name column so that the importer has to truncate it before the record can be written to the database (part 9) + AttributeValue 9]]> + + 19,20 20,20 20,21 19,21 19,20 + + + + Short name 10 + AttributeValue 10]]> + + 20,20 21,20 21,21 20,21 20,20 + + + + Segment description continuing well past the length of the name column so that the importer has to truncate it before the record can be written to the database (part 11) + AttributeValue 11]]> + + 21,20 22,20 22,21 21,21 21,20 + + + + + diff --git a/spec/fixtures/nested_archive_of_shapefiles.zip b/spec/fixtures/nested_archive_of_shapefiles.zip index f38c241d..4a4a9a68 100644 Binary files a/spec/fixtures/nested_archive_of_shapefiles.zip and b/spec/fixtures/nested_archive_of_shapefiles.zip differ diff --git a/spec/fixtures/shapefile.zip b/spec/fixtures/shapefile.zip index 568be7c3..a3ebf50c 100644 Binary files a/spec/fixtures/shapefile.zip and b/spec/fixtures/shapefile.zip differ diff --git a/spec/fixtures/shapefile_with_dot_prefix.zip b/spec/fixtures/shapefile_with_dot_prefix.zip index 51db555b..8ef97aec 100644 Binary files a/spec/fixtures/shapefile_with_dot_prefix.zip and b/spec/fixtures/shapefile_with_dot_prefix.zip differ diff --git a/spec/fixtures/shapefile_with_incorrect_shx_basename.zip b/spec/fixtures/shapefile_with_incorrect_shx_basename.zip index 30858317..2abd1b4f 100644 Binary files a/spec/fixtures/shapefile_with_incorrect_shx_basename.zip and b/spec/fixtures/shapefile_with_incorrect_shx_basename.zip differ diff --git a/spec/fixtures/shapefile_with_macosx_resources.zip b/spec/fixtures/shapefile_with_macosx_resources.zip index 00bc49d3..d93e0e23 100644 Binary files a/spec/fixtures/shapefile_with_macosx_resources.zip and b/spec/fixtures/shapefile_with_macosx_resources.zip differ diff --git a/spec/fixtures/shapefile_with_missing_dbf_file.zip b/spec/fixtures/shapefile_with_missing_dbf_file.zip index ac170f30..a504ec7b 100644 Binary files a/spec/fixtures/shapefile_with_missing_dbf_file.zip and b/spec/fixtures/shapefile_with_missing_dbf_file.zip differ diff --git a/spec/fixtures/shapefile_with_upcase_shp.zip b/spec/fixtures/shapefile_with_upcase_shp.zip index ad749551..432659e8 100644 Binary files a/spec/fixtures/shapefile_with_upcase_shp.zip and b/spec/fixtures/shapefile_with_upcase_shp.zip differ diff --git a/spec/fixtures/shapefile_without_projection.zip b/spec/fixtures/shapefile_without_projection.zip index 3c2070b4..e1ea4793 100644 Binary files a/spec/fixtures/shapefile_without_projection.zip and b/spec/fixtures/shapefile_without_projection.zip differ diff --git a/spec/fixtures/shapefile_without_shape_format.zip b/spec/fixtures/shapefile_without_shape_format.zip index 8a80f352..fc2da694 100644 Binary files a/spec/fixtures/shapefile_without_shape_format.zip and b/spec/fixtures/shapefile_without_shape_format.zip differ diff --git a/spec/fixtures/shapefile_without_shape_index.zip b/spec/fixtures/shapefile_without_shape_index.zip index bf3c92b7..e2a20332 100644 Binary files a/spec/fixtures/shapefile_without_shape_index.zip and b/spec/fixtures/shapefile_without_shape_index.zip differ diff --git a/spec/fixtures/test.kml b/spec/fixtures/test.kml index 9760b120..77f9ce5b 100644 --- a/spec/fixtures/test.kml +++ b/spec/fixtures/test.kml @@ -1,103 +1,19 @@ - - - test.kml - - - - normal - #sn_ylw-pushpin - - - highlight - #sh_ylw-pushpin - - - - - - normal - #s_ylw-pushpin - - - highlight - #s_ylw-pushpin_hl - - - - - - Poly folder - 1 - - Poly 1 - This is a description - #m_ylw-pushpin - 1 - - 1 - - - - -104.2021395645545,60.47065556909579,0 -98.5406390057758,60.08348491099549,0 -98.40794749318894,63.7611693880032,0 -105.6700904293509,63.9562028375275,0 -104.2021395645545,60.47065556909579,0 - - - - - - - Poly 2 - This is a description also - #msn_ylw-pushpin - - 1 - - - - -106.4303166516638,61.16376575099101,0 -100.7627103394292,61.95514185121152,0 -100.2655658472707,65.8275552135498,0 -107.9516815833805,65.21804505333482,0 -106.4303166516638,61.16376575099101,0 - - - - - - - + + + test.kml + + Poly folder + + Poly 1 + This is a description + 10,20 11,20 11,21 10,21 10,20 + + + Poly 2 + This is a description also + 30,40 31,40 31,41 30,41 30,40 + + + diff --git a/spec/fixtures/test.kmz b/spec/fixtures/test.kmz index 363e58e1..7cd21dee 100644 Binary files a/spec/fixtures/test.kmz and b/spec/fixtures/test.kmz differ diff --git a/spec/lib/spatial_features/importers/file_spec.rb b/spec/lib/spatial_features/importers/file_spec.rb index ec713bf4..ef644d95 100644 --- a/spec/lib/spatial_features/importers/file_spec.rb +++ b/spec/lib/spatial_features/importers/file_spec.rb @@ -109,7 +109,7 @@ it 'sets different source identifiers for features from each file' do importers = subject.create_all(url) expect(importers.flat_map(&:features).map(&:source_identifier).uniq) - .to contain_exactly('shapefile.zip/firstnationreserves.shp') + .to contain_exactly('shapefile.zip/polygons.shp') end end @@ -125,8 +125,8 @@ importers = subject.create_all(file) expect(importers.flat_map(&:features).map(&:source_identifier).uniq) .to contain_exactly( - 'archive_with_multiple_shps.zip/crims_alcids_treatyareas.shp', - 'archive_with_multiple_shps.zip/crims_bald_eagles_3n_24june2021.shp' + 'archive_with_multiple_shps.zip/layer_a.shp', + 'archive_with_multiple_shps.zip/layer_b.shp' ) end end diff --git a/spec/lib/spatial_features/importers/kml_file_spec.rb b/spec/lib/spatial_features/importers/kml_file_spec.rb index e329ac5d..1cd60faa 100644 --- a/spec/lib/spatial_features/importers/kml_file_spec.rb +++ b/spec/lib/spatial_features/importers/kml_file_spec.rb @@ -96,7 +96,7 @@ describe '#warnings' do it 'records a warning naming the skipped map image' do subject.features - expect(subject.warnings).to include(a_string_matching(/map image.*Provincial Boundary/i)) + expect(subject.warnings).to include(a_string_matching(/map image.*Basemap Overlay/i)) end end end diff --git a/spec/lib/spatial_features/importers/shapefile_spec.rb b/spec/lib/spatial_features/importers/shapefile_spec.rb index dcbc843e..fbe386b4 100644 --- a/spec/lib/spatial_features/importers/shapefile_spec.rb +++ b/spec/lib/spatial_features/importers/shapefile_spec.rb @@ -33,7 +33,7 @@ let(:subject) { SpatialFeatures::Importers::Shapefile.new(shapefile_without_shape_index) } it 'raises an exception' do - expect { subject.features }.to raise_exception(SpatialFeatures::Importers::IncompleteShapefileArchive, /FirstNationReserves\.shx/i) + expect { subject.features }.to raise_exception(SpatialFeatures::Importers::IncompleteShapefileArchive, /polygons\.shx/i) end end @@ -57,7 +57,7 @@ let(:subject) { SpatialFeatures::Importers::Shapefile.new(shapefile_with_incorrect_shx_basename) } it 'raises an exception' do - expect { subject.features }.to raise_exception(SpatialFeatures::Importers::IncompleteShapefileArchive, /FirstNationReserves\.shx/i) + expect { subject.features }.to raise_exception(SpatialFeatures::Importers::IncompleteShapefileArchive, /polygons\.shx/i) end end @@ -66,7 +66,7 @@ it 'raises an exception if there is no default projection' do allow(SpatialFeatures::Importers::Shapefile).to receive(:default_proj4_projection).and_return(nil) - expect { subject.features }.to raise_exception(SpatialFeatures::Importers::IndeterminateShapefileProjection, /FirstNationReserves\.prj/i) + expect { subject.features }.to raise_exception(SpatialFeatures::Importers::IndeterminateShapefileProjection, /polygons\.prj/i) end it 'uses the `default_proj4_projection` when no projection can be determined from the shapefile' do @@ -123,8 +123,8 @@ context 'when given a zip archive with multiple shapefiles' do let(:data) { archive_with_multiple_shps } let(:shapefile_features) { { - "crims_alcids_treatyareas.shp" => 22, - "crims_bald_eagles_3n_24june2021.shp" => 48 + "layer_a.shp" => 22, + "layer_b.shp" => 48 } } describe '#new' do @@ -139,8 +139,8 @@ importers = SpatialFeatures::Importers::Shapefile.create_all(data) expect(importers.count).to eq(2) - expect(importers[0].features.count).to eq(shapefile_features["crims_alcids_treatyareas.shp"]) - expect(importers[1].features.count).to eq(shapefile_features["crims_bald_eagles_3n_24june2021.shp"]) + expect(importers[0].features.count).to eq(shapefile_features["layer_a.shp"]) + expect(importers[1].features.count).to eq(shapefile_features["layer_b.shp"]) end end end diff --git a/tasks/fixtures.rake b/tasks/fixtures.rake new file mode 100644 index 00000000..a230f0aa --- /dev/null +++ b/tasks/fixtures.rake @@ -0,0 +1,348 @@ +require 'base64' +require 'fileutils' +require 'json' +require 'tmpdir' +require 'zip' + +# Builds every file in `spec/fixtures`. See `spec/fixtures/README.md` for what the fixtures +# are for and which of their properties the specs depend on. +module FixtureGenerator + # Projected rather than geographic, so that importing a shapefile exercises reprojection. + SRS = 'EPSG:26911'.freeze # NAD83 / UTM zone 11N + + # An 8x8 solid, the smallest thing that is still a JPEG. Embedded rather than generated so + # that building the fixtures needs no image tooling. + TINY_JPEG = Base64.decode64(<<~B64).freeze + /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABQODxIPDRQSEBIXFRQYHjIhHhwcHj0sLiQySUBMS0dA + RkVQWnNiUFVtVkVGZIhlbXd7gYKBTmCNl4x9lnN+gXz/2wBDARUXFx4aHjshITt8U0ZTfHx8fHx8 + fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHz/wAARCAAIAAgDASIA + AhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAP/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEB + AQAAAAAAAAAAAAAAAAAABAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCwAC8/ + /9k= + B64 + + class << self + def build_all(dir) + FileUtils.mkdir_p(dir) + Dir.mktmpdir do |work| + build_shapefiles(dir, work) + build_documents(dir, work) + end + end + + # SHAPEFILES + + def build_shapefiles(dir, work) + components = write_layer(work, 'polygons', 17) + complete = components.map { |path| [::File.basename(path), path] } + + write_zip(::File.join(dir, 'shapefile.zip'), complete) + + # Each of these drops exactly one required component. + %w[prj shx shp dbf].zip(%w[without_projection without_shape_index without_shape_format + with_missing_dbf_file]).each do |ext, name| + write_zip(::File.join(dir, "shapefile_#{name}.zip"), + complete.reject { |entry, _| entry.end_with?(".#{ext}") }) + end + + # An upcased .shp, which the importer finds only because it downcases entry names. + write_zip(::File.join(dir, 'shapefile_with_upcase_shp.zip'), + complete.map { |entry, path| entry.end_with?('.shp') ? ['POLYGONS.SHP', path] : [entry, path] }) + + # A .shx whose basename does not match the .shp, so the pair cannot be resolved. + write_zip(::File.join(dir, 'shapefile_with_incorrect_shx_basename.zip'), + [['shapefile/', :directory]] + + complete.map do |entry, path| + entry.end_with?('.shx') ? ['shapefile/mismatched.shx', path] : ["shapefile/#{entry}", path] + end) + + # A dot prefixed duplicate beside the real thing, which must be ignored. + shp = complete.find { |entry, _| entry.end_with?('.shp') }.last + write_zip(::File.join(dir, 'shapefile_with_dot_prefix.zip'), complete + [['.polygons.shp', shp]]) + + # What Finder writes: an AppleDouble sidecar per file, keeping the original extension. + write_zip(::File.join(dir, 'shapefile_with_macosx_resources.zip'), + [['shapefile/', :directory]] + + complete.map { |entry, path| ["shapefile/#{entry}", path] } + + [['shapefile/.DS_Store', :empty], + ['__MACOSX/shapefile/._polygons.shp', :empty], + ['__MACOSX/shapefile/._.DS_Store', :empty]]) + + # Two layers in one archive, named so the alphabetical pick is unambiguous. + a = write_layer(work, 'layer_a', 22) + b = write_layer(work, 'layer_b', 48, :origin_n => 5_600_000) + write_zip(::File.join(dir, 'archive_with_multiple_shps.zip'), + (a + b).map { |path| [::File.basename(path), path] }) + + # An archive of archives, which the importer unwraps one level to find a shapefile. + write_zip(::File.join(dir, 'nested_archive_of_shapefiles.zip'), + [['shapefile.zip', ::File.join(dir, 'shapefile.zip')], + ['shapefile_with_upcase_shp.zip', ::File.join(dir, 'shapefile_with_upcase_shp.zip')]]) + + # Nothing the importer recognises. + write_zip(::File.join(dir, 'archive_without_any_known_file.zip'), + [['notes.whatever', :empty]]) + end + + # Writes `count` square polygons on a regular grid, in metres, as an ESRI Shapefile. + # Returns the component paths, in shp/shx/dbf/prj order. + def write_layer(work, layer, count, origin_e: 500_000, origin_n: 5_500_000, size: 1_000, step: 2_000, cols: 6) + features = Array.new(count) do |i| + e = origin_e + (i % cols) * step + n = origin_n + (i / cols) * step + { :type => 'Feature', + :properties => { :name => format('Area %02d', i + 1), + :category => %w[alpha beta gamma][i % 3], + :value => (i + 1) * 10 }, + :geometry => { :type => 'Polygon', + :coordinates => [[[e, n], [e + size, n], [e + size, n + size], [e, n + size], [e, n]]] } } + end + + geojson = ::File.join(work, "#{layer}.geojson") + ::File.write(geojson, JSON.generate(:type => 'FeatureCollection', :features => features)) + + paths = %w[shp shx dbf prj].map { |ext| ::File.join(work, "#{layer}.#{ext}") } + paths.each { |path| ::File.delete(path) if ::File.exist?(path) } + # Argv form, so nothing here is re-parsed by a shell. + system('ogr2ogr', '-a_srs', SRS, '-nln', layer, paths.first, geojson) || + raise("ogr2ogr failed while writing #{layer}") + ::File.delete(geojson) + + paths + end + + # KML, KMZ AND GEOJSON + + def build_documents(dir, work) + write(::File.join(dir, 'test.kml'), document('test.kml', poly_folder)) + write_kmz(::File.join(dir, 'test.kmz'), document('test.kmz', poly_folder)) + + # Altitudes in exponent notation, which must parse. + write(::File.join(dir, 'kml_file_with_altitude.kml'), + document('test.kml', poly_folder(:altitude => '-5.976855754852295e-05'))) + + # Altitudes that are not numbers at all, which must be tolerated rather than fatal. + write(::File.join(dir, 'kml_file_with_invalid_altitude.kml'), + document('kml_file_with_invalid_altitude', poly_folder(:altitude => 'isNaN'))) + + # Three placemarks, one a LineString with a single vertex, so two survive. + write(::File.join(dir, 'kml_file_with_invalid_placemark.kml'), + document('Invalid Placemark Test', folder('Invalid Placemark Test', + placemark('Point', 'A point', " 10,20\n") + + placemark('Line', 'A line', line_string('10,20 11,21 12,22')) + + placemark('Invalid Line', 'A line with one vertex', line_string('10,20'))))) + + # Structurally valid and completely empty. + write(::File.join(dir, 'kml_file_without_features.kml'), + document('Without Features Test', + folder('Without Features Test', " 1\n"))) + + # Nothing but remote layers, so nothing imports and a warning is raised. + write(::File.join(dir, 'kml_file_with_network_link.kml'), + document('Remote Layers', folder('Remote folder', (1..3).map { |i| + network_link("Remote Layer #{i}", "https://example.com/layers/#{i}.kml") + }.join))) + + # A remote layer beside real placemarks: the placemarks still import. + write(::File.join(dir, 'kml_file_with_network_link_and_features.kml'), + document('kml_file_with_network_link_and_features.kml', + folder('Remote folder', network_link('Remote Layer', 'https://example.com/remote.kmz')) + + poly_folder)) + + # A raster overlay carries no geometry, so it is reported rather than imported. + write(::File.join(dir, 'kml_file_with_ground_overlay.kml'), + document('kml_file_with_ground_overlay.kml', overlay_folder)) + write(::File.join(dir, 'kml_file_with_ground_overlay_and_features.kml'), + document('kml_file_with_ground_overlay_and_features.kml', overlay_folder + poly_folder)) + + # Geometry with no Placemark ancestor, so it imports with no name and no metadata. + write_kmz(::File.join(dir, 'kmz_file_features_without_placemarks.kmz'), + document('Geometry Without Placemarks', + folder('Geometry folder', + " \n" + polygon(square(10, 20)) + + polygon(square(30, 40)) + " \n"))) + + build_images_kmz(::File.join(dir, 'kmz_with_images.kmz'), work) + build_long_names(::File.join(dir, 'long_placemark_name.kml')) + build_multiple_kmls(::File.join(dir, 'archive_with_multiple_kmls.zip'), work) + + # A KMZ wrapped in a ZIP, which the importer unwraps one level. + write_zip(::File.join(dir, 'archive_containing_kmz.zip'), + [['test.kmz', ::File.join(dir, 'test.kmz')]]) + + build_geojson(::File.join(dir, 'geo.json')) + end + + # Embedded photos referenced from ExtendedData: one placemark with one image, one with + # two, and one with none. + def build_images_kmz(target, work) + images = %w[one.jpg two_a.jpg two_b.jpg].map do |name| + path = ::File.join(work, name) + ::File.binwrite(path, TINY_JPEG) + ["images/#{name}", path] + end + + schema = <<~SCHEMA + + + Photos + + SCHEMA + + placemarks = placemark('Placemark 1', 'One image', photos('one.jpg') + point(10, 20)) + + placemark('Placemark 2', 'Two images', photos('two_a.jpg', 'two_b.jpg') + point(12, 22)) + + placemark('Placemark 3', 'No images', polygon(square(30, 40))) + + write_kmz(target, document('Image Layer', schema + folder('Layer', placemarks)), images) + end + + # Names longer than the name column, so the importer has to truncate them. + def build_long_names(target) + long = 'Segment description continuing well past the length of the name column so that the ' \ + 'importer has to truncate it before the record can be written to the database' + marks = (1..11).map do |i| + placemark(i.odd? ? "#{long} (part #{i})" : "Short name #{i}", + "AttributeValue #{i}]]>", + " \n" + polygon(square(10 + i, 20)) + " \n") + end.join + + write(target, document('Long Placemark Names', folder('Long Placemark Names', marks))) + end + + # Two sibling KML files in one archive. + def build_multiple_kmls(target, work) + a = document('Sample A', folder('Sample A', + placemark('Sample A 1', 'First', polygon(square(10, 20))) + + placemark('Sample A 2', 'Second', polygon(square(30, 40))))) + b = document('Sample B', folder('Sample B', + placemark('Sample B 1', 'First', polygon(square(50, 10))))) + + paths = { 'kml_sample_a.kml' => a, 'kml_sample_b.kml' => b }.map do |name, content| + [name, write(::File.join(work, name), content)] + end + write_zip(target, paths) + end + + # Three features, one of which has no geometry and is skipped. + def build_geojson(target) + ring = lambda do |lon, lat| + [[[lon, lat], [lon + 1, lat], [lon + 1, lat + 1], [lon, lat + 1], [lon, lat]]] + end + write(target, JSON.pretty_generate(:type => 'FeatureCollection', :features => [ + { :type => 'Feature', :properties => { :name => 'Feature A', :prop0 => 'value' }, + :geometry => { :type => 'Polygon', :coordinates => ring.call(10, 20) } }, + { :type => 'Feature', :properties => { :name => 'Feature B', :prop0 => 'value' }, + :geometry => { :type => 'Polygon', :coordinates => ring.call(30, 40) } }, + { :type => 'Feature', :properties => { :name => 'Feature C', :prop0 => 'value' }, + :geometry => nil }, + ]) + "\n") + end + + # KML BUILDING BLOCKS + + def document(name, body) + %(\n) + + %(\n) + + " \n #{name}\n#{body} \n\n" + end + + def folder(name, body) + " \n #{name}\n#{body} \n" + end + + def placemark(name, description, geometry) + " \n #{name}\n" \ + " #{description}\n#{geometry} \n" + end + + # The two named, described polygons that most of the KML fixtures are built around. + def poly_folder(altitude: nil) + folder('Poly folder', + placemark('Poly 1', 'This is a description', polygon(square(10, 20, :altitude => altitude))) + + placemark('Poly 2', 'This is a description also', polygon(square(30, 40)))) + end + + def overlay_folder + folder('Overlay folder', + " \n Basemap Overlay\n" \ + " https://example.com/wms?service=wms&request=GetMap\n" \ + " 4139" \ + "3129\n \n") + end + + def network_link(name, href) + " \n #{name}\n" \ + " #{href}onRequest\n" \ + " \n" + end + + def photos(*names) + tags = names.map { |name| %(<img src="images/#{name}">) }.join + %( \n) + + %( #{tags}\n) + + %( \n) + end + + def point(lon, lat) + " #{lon},#{lat}\n" + end + + def line_string(coordinates) + " #{coordinates}\n" + end + + def polygon(coordinates) + " #{coordinates}" \ + "\n" + end + + # Returns a closed square ring as a KML coordinate string. + def square(lon, lat, size: 1, altitude: nil) + points = [[lon, lat], [lon + size, lat], [lon + size, lat + size], [lon, lat + size], [lon, lat]] + points.map { |x, y| altitude ? "#{x},#{y},#{altitude}" : "#{x},#{y}" }.join(' ') + end + + # FILE WRITING + + def write(path, contents) + ::File.write(path, contents) + path + end + + # Writes `entries` - [name, source] pairs - replacing any existing archive. A source of + # `:directory` or `:empty` writes an entry with no content. + def write_zip(target, entries) + ::File.delete(target) if ::File.exist?(target) + Zip::OutputStream.open(target) do |zos| + entries.each do |name, source| + zos.put_next_entry(name) + zos.write(::File.binread(source)) unless source == :directory || source == :empty + end + end + target + end + + # A KMZ is a ZIP whose KML entry has to stay named `doc.kml`. + def write_kmz(target, doc_kml, images = []) + ::File.delete(target) if ::File.exist?(target) + Zip::OutputStream.open(target) do |zos| + zos.put_next_entry('doc.kml') + zos.write(doc_kml) + images.each do |name, path| + zos.put_next_entry(name) + zos.write(::File.binread(path)) + end + end + target + end + end +end + +namespace :fixtures do + desc 'Rebuild every file in spec/fixtures from synthetic data' + task :build do + dir = ::File.expand_path('../spec/fixtures', __dir__) + FixtureGenerator.build_all(dir) + puts "Rebuilt fixtures in #{dir}" + end +end