kml-path-parser 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +21 -0
- data/README.md +106 -0
- data/lib/kml/path/fixtures/reject/empty_archive.kmz +0 -0
- data/lib/kml/path/fixtures/reject/empty_document.kml +4 -0
- data/lib/kml/path/fixtures/reject/empty_linestring.kml +11 -0
- data/lib/kml/path/fixtures/reject/point_only.kml +11 -0
- data/lib/kml/path/fixtures/reject/polygon_only.kml +20 -0
- data/lib/kml/path/fixtures/reject/single_point.kml +11 -0
- data/lib/kml/path/fixtures/valid/cdata_name.kml +14 -0
- data/lib/kml/path/fixtures/valid/coordinates_extra_whitespace.kml +16 -0
- data/lib/kml/path/fixtures/valid/coordinates_no_altitude.kml +15 -0
- data/lib/kml/path/fixtures/valid/coordinates_single_line.kml +11 -0
- data/lib/kml/path/fixtures/valid/crlf_line_endings.kml +15 -0
- data/lib/kml/path/fixtures/valid/document_name_priority.kml +15 -0
- data/lib/kml/path/fixtures/valid/gx_multitrack_first_wins.kml +18 -0
- data/lib/kml/path/fixtures/valid/gx_track.kml +13 -0
- data/lib/kml/path/fixtures/valid/gx_track_with_timestamps.kml +16 -0
- data/lib/kml/path/fixtures/valid/invalid_coords_filtered.kml +16 -0
- data/lib/kml/path/fixtures/valid/kmz_custom_kml_name.kmz +0 -0
- data/lib/kml/path/fixtures/valid/kmz_with_macosx_junk.kmz +0 -0
- data/lib/kml/path/fixtures/valid/linestring_attributes.kml +18 -0
- data/lib/kml/path/fixtures/valid/linestring_before_point.kml +17 -0
- data/lib/kml/path/fixtures/valid/many_points.kml +22 -0
- data/lib/kml/path/fixtures/valid/multi_geometry.kml +22 -0
- data/lib/kml/path/fixtures/valid/multiple_placemarks_first_wins.kml +24 -0
- data/lib/kml/path/fixtures/valid/namespace_less.kml +15 -0
- data/lib/kml/path/fixtures/valid/nested_folder.kml +19 -0
- data/lib/kml/path/fixtures/valid/nested_multigeometry_folder.kml +19 -0
- data/lib/kml/path/fixtures/valid/placemark_name.kml +15 -0
- data/lib/kml/path/fixtures/valid/qgis_export.kml +22 -0
- data/lib/kml/path/fixtures/valid/sample_route.kml +15 -0
- data/lib/kml/path/fixtures/valid/sample_route.kmz +0 -0
- data/lib/kml/path/fixtures/valid/two_points.kml +14 -0
- data/lib/kml/path/fixtures/valid/unicode_name.kml +14 -0
- data/lib/kml/path/fixtures/valid/unnamed.kml +13 -0
- data/lib/kml/path/fixtures.rb +63 -0
- data/lib/kml/path/parse_error.rb +8 -0
- data/lib/kml/path/parser.rb +197 -0
- data/lib/kml/path/result.rb +24 -0
- data/lib/kml/path/version.rb +7 -0
- data/lib/kml.rb +12 -0
- metadata +115 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1ba7b9a23862285aed743abf581d2c6d93d72558ebb7679730560f3626f3051d
|
|
4
|
+
data.tar.gz: 82e88798ec57e4644616fb9718f7dcebd8fdeca773a81c2b2a567d8d479bba3a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 85669be59563792510ac0ea60bfdb5bf81650ec7226503b9dccebb88b8e9c013fda3e19a926862076d444f7c7c94b647b1315ceff0fc7f4b97ce7da3ba2c2ee1
|
|
7
|
+
data.tar.gz: 41b78088cac7f4c57e75492ec631954e403883a4e898312b0aa0bc14706a46085de810a02caaf1c0a415c180c05bb1c4fcf18745968c80f981ea2b1c5ba9f47b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 1.0.0
|
|
2
|
+
|
|
3
|
+
Initial release.
|
|
4
|
+
|
|
5
|
+
- Parse `LineString` and `gx:Track` geometry from KML and KMZ uploads
|
|
6
|
+
- `#parse` returns a `Kml::Path::Result`; `#parse!` raises `Kml::Path::ParseError` on failure
|
|
7
|
+
- Shipped fixture files and `Kml::Path::Fixtures` helper for consumer tests
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Josh McArthur
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# kml-path-parser
|
|
2
|
+
|
|
3
|
+
Extract path geometry from user-uploaded KML and KMZ files.
|
|
4
|
+
|
|
5
|
+
`kml-path-parser` is a focused library for fitness and mapping apps that need to import route tracks from real-world exports (Google Earth, My Maps, QGIS, and similar tools). It is **not** a general-purpose KML codec: it only extracts `LineString` and `gx:Track` paths.
|
|
6
|
+
|
|
7
|
+
Extracted from [VirtualTrails](https://virtualtrails.app).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem "kml-path-parser", "~> 1.0"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
The parser expects a file-like upload object responding to `read`, `rewind`, `original_filename`, and `content_type` (as Rails `ActionDispatch::Http::UploadedFile` and `Rack::Test::UploadedFile` do).
|
|
18
|
+
|
|
19
|
+
### `#parse` — non-exceptional failures
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require "kml"
|
|
23
|
+
|
|
24
|
+
parser = Kml::Path::Parser.new(file: upload)
|
|
25
|
+
result = parser.parse
|
|
26
|
+
|
|
27
|
+
if result.success?
|
|
28
|
+
puts result.name
|
|
29
|
+
result.coordinates.each do |longitude, latitude, altitude|
|
|
30
|
+
puts [longitude, latitude, altitude]
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
puts result.error
|
|
34
|
+
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### `#parse!` — raises on failure
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
result = parser.parse!
|
|
41
|
+
# => Kml::Path::Result
|
|
42
|
+
|
|
43
|
+
# On invalid input:
|
|
44
|
+
# Kml::Path::ParseError: must contain a LineString or gx:Track
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Lazy accessors
|
|
48
|
+
|
|
49
|
+
`#content` returns the extracted KML string (from a `.kml` file or from inside a `.kmz` archive). `#name` resolves the route name without requiring a successful coordinate parse.
|
|
50
|
+
|
|
51
|
+
## Shipped fixtures
|
|
52
|
+
|
|
53
|
+
The gem includes real-world KML/KMZ fixtures for testing and documentation:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
fixture_path = Kml::Path::Fixtures.path("valid/sample_route.kml")
|
|
57
|
+
catalog = Kml::Path::Fixtures::CATALOG
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Fixtures live under `lib/kml/path/fixtures/` (`valid/` and `reject/`).
|
|
61
|
+
|
|
62
|
+
## Supported formats
|
|
63
|
+
|
|
64
|
+
| Format | Support |
|
|
65
|
+
|--------|---------|
|
|
66
|
+
| KML (`.kml`) | Yes |
|
|
67
|
+
| KMZ (`.kmz`) | Yes — uses `doc.kml` when present, otherwise the first `.kml` entry (skips `__MACOSX/`) |
|
|
68
|
+
|
|
69
|
+
## Parsing policies
|
|
70
|
+
|
|
71
|
+
- **Geometry:** first `LineString` in the document wins; otherwise first `gx:Track` (first track in `gx:MultiTrack`)
|
|
72
|
+
- **Name priority:** Document name → Placemark name → any `name` element → upload filename → `"Untitled"`
|
|
73
|
+
- **Coordinates:** returned as `[longitude, latitude, altitude]` arrays; altitude may be `nil`
|
|
74
|
+
- **Invalid coordinates:** lat/lon pairs outside valid ranges are filtered silently
|
|
75
|
+
|
|
76
|
+
## Rejected inputs
|
|
77
|
+
|
|
78
|
+
These return a failed `Result` (or raise `ParseError` via `#parse!`):
|
|
79
|
+
|
|
80
|
+
- Empty documents
|
|
81
|
+
- Point-only geometry
|
|
82
|
+
- Polygon-only geometry
|
|
83
|
+
- Empty `LineString` elements
|
|
84
|
+
- KMZ archives with no KML file
|
|
85
|
+
- Corrupt KMZ archives (`could not be parsed: …`)
|
|
86
|
+
|
|
87
|
+
A single-point `LineString` parses successfully; callers that need a minimum path length should enforce that themselves.
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
bundle install
|
|
93
|
+
bundle exec rake test
|
|
94
|
+
bundle exec rubocop
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Releasing
|
|
98
|
+
|
|
99
|
+
1. Bump `Kml::Path::VERSION` in `lib/kml/path/version.rb`
|
|
100
|
+
2. Update `CHANGELOG.md`
|
|
101
|
+
3. Commit, tag (`v1.0.1`), and push the tag
|
|
102
|
+
4. GitHub Actions publishes to RubyGems when `RUBYGEMS_API_KEY` is configured
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT — see [LICENSE](LICENSE).
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Polygon Only</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<Polygon>
|
|
7
|
+
<outerBoundaryIs>
|
|
8
|
+
<LinearRing>
|
|
9
|
+
<coordinates>
|
|
10
|
+
144.9631,-37.8136,0
|
|
11
|
+
144.9731,-37.8236,0
|
|
12
|
+
144.9831,-37.8336,0
|
|
13
|
+
144.9631,-37.8136,0
|
|
14
|
+
</coordinates>
|
|
15
|
+
</LinearRing>
|
|
16
|
+
</outerBoundaryIs>
|
|
17
|
+
</Polygon>
|
|
18
|
+
</Placemark>
|
|
19
|
+
</Document>
|
|
20
|
+
</kml>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name><![CDATA[Trail & "Path" <special>]]></name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
</coordinates>
|
|
11
|
+
</LineString>
|
|
12
|
+
</Placemark>
|
|
13
|
+
</Document>
|
|
14
|
+
</kml>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Whitespace</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631, -37.8136 , 0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
|
|
11
|
+
144.9831,-37.8336,0
|
|
12
|
+
</coordinates>
|
|
13
|
+
</LineString>
|
|
14
|
+
</Placemark>
|
|
15
|
+
</Document>
|
|
16
|
+
</kml>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>No Altitude</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136
|
|
9
|
+
144.9731,-37.8236
|
|
10
|
+
144.9831,-37.8336
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
</Document>
|
|
15
|
+
</kml>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Single Line</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>144.9631,-37.8136,0 144.9731,-37.8236,0 144.9831,-37.8336,0</coordinates>
|
|
8
|
+
</LineString>
|
|
9
|
+
</Placemark>
|
|
10
|
+
</Document>
|
|
11
|
+
</kml>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Sample Trail</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
144.9831,-37.8336,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
</Document>
|
|
15
|
+
</kml>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Official Name</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<name>Ignored Placemark Name</name>
|
|
7
|
+
<LineString>
|
|
8
|
+
<coordinates>
|
|
9
|
+
144.9631,-37.8136,0
|
|
10
|
+
144.9731,-37.8236,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
</Document>
|
|
15
|
+
</kml>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Multi Track</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<gx:MultiTrack>
|
|
7
|
+
<gx:Track>
|
|
8
|
+
<gx:coord>144.9631 -37.8136 0</gx:coord>
|
|
9
|
+
<gx:coord>144.9731 -37.8236 0</gx:coord>
|
|
10
|
+
</gx:Track>
|
|
11
|
+
<gx:Track>
|
|
12
|
+
<gx:coord>145.0000 -38.0000 0</gx:coord>
|
|
13
|
+
<gx:coord>145.0100 -38.0100 0</gx:coord>
|
|
14
|
+
</gx:Track>
|
|
15
|
+
</gx:MultiTrack>
|
|
16
|
+
</Placemark>
|
|
17
|
+
</Document>
|
|
18
|
+
</kml>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>GPS Track</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<gx:Track>
|
|
7
|
+
<gx:coord>144.9631 -37.8136 0</gx:coord>
|
|
8
|
+
<gx:coord>144.9731 -37.8236 0</gx:coord>
|
|
9
|
+
<gx:coord>144.9831 -37.8336 0</gx:coord>
|
|
10
|
+
</gx:Track>
|
|
11
|
+
</Placemark>
|
|
12
|
+
</Document>
|
|
13
|
+
</kml>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Timestamped Track</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<gx:Track>
|
|
7
|
+
<when>2024-01-01T00:00:00Z</when>
|
|
8
|
+
<when>2024-01-01T00:05:00Z</when>
|
|
9
|
+
<when>2024-01-01T00:10:00Z</when>
|
|
10
|
+
<gx:coord>144.9631 -37.8136 0</gx:coord>
|
|
11
|
+
<gx:coord>144.9731 -37.8236 0</gx:coord>
|
|
12
|
+
<gx:coord>144.9831 -37.8336 0</gx:coord>
|
|
13
|
+
</gx:Track>
|
|
14
|
+
</Placemark>
|
|
15
|
+
</Document>
|
|
16
|
+
</kml>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Filtered Coords</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
200.0000,100.0000,0
|
|
10
|
+
144.9731,-37.8236,0
|
|
11
|
+
144.9831,-37.8336,0
|
|
12
|
+
</coordinates>
|
|
13
|
+
</LineString>
|
|
14
|
+
</Placemark>
|
|
15
|
+
</Document>
|
|
16
|
+
</kml>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Attributed Line</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<extrude>1</extrude>
|
|
8
|
+
<tessellate>1</tessellate>
|
|
9
|
+
<altitudeMode>clampToGround</altitudeMode>
|
|
10
|
+
<coordinates>
|
|
11
|
+
144.9631,-37.8136,0
|
|
12
|
+
144.9731,-37.8236,0
|
|
13
|
+
144.9831,-37.8336,0
|
|
14
|
+
</coordinates>
|
|
15
|
+
</LineString>
|
|
16
|
+
</Placemark>
|
|
17
|
+
</Document>
|
|
18
|
+
</kml>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Line Before Point</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
</coordinates>
|
|
11
|
+
</LineString>
|
|
12
|
+
<Point>
|
|
13
|
+
<coordinates>145.0000,-38.0000,0</coordinates>
|
|
14
|
+
</Point>
|
|
15
|
+
</Placemark>
|
|
16
|
+
</Document>
|
|
17
|
+
</kml>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Many Points</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9600,-37.8100,0
|
|
9
|
+
144.9610,-37.8110,0
|
|
10
|
+
144.9620,-37.8120,0
|
|
11
|
+
144.9630,-37.8130,0
|
|
12
|
+
144.9640,-37.8140,0
|
|
13
|
+
144.9650,-37.8150,0
|
|
14
|
+
144.9660,-37.8160,0
|
|
15
|
+
144.9670,-37.8170,0
|
|
16
|
+
144.9680,-37.8180,0
|
|
17
|
+
144.9690,-37.8190,0
|
|
18
|
+
</coordinates>
|
|
19
|
+
</LineString>
|
|
20
|
+
</Placemark>
|
|
21
|
+
</Document>
|
|
22
|
+
</kml>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Split Route</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<MultiGeometry>
|
|
7
|
+
<LineString>
|
|
8
|
+
<coordinates>
|
|
9
|
+
144.9631,-37.8136,0
|
|
10
|
+
144.9731,-37.8236,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
<LineString>
|
|
14
|
+
<coordinates>
|
|
15
|
+
145.0000,-38.0000,0
|
|
16
|
+
145.0100,-38.0100,0
|
|
17
|
+
</coordinates>
|
|
18
|
+
</LineString>
|
|
19
|
+
</MultiGeometry>
|
|
20
|
+
</Placemark>
|
|
21
|
+
</Document>
|
|
22
|
+
</kml>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Multiple Placemarks</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<name>First Trail</name>
|
|
7
|
+
<LineString>
|
|
8
|
+
<coordinates>
|
|
9
|
+
144.9631,-37.8136,0
|
|
10
|
+
144.9731,-37.8236,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
<Placemark>
|
|
15
|
+
<name>Second Trail</name>
|
|
16
|
+
<LineString>
|
|
17
|
+
<coordinates>
|
|
18
|
+
145.0000,-38.0000,0
|
|
19
|
+
145.0100,-38.0100,0
|
|
20
|
+
</coordinates>
|
|
21
|
+
</LineString>
|
|
22
|
+
</Placemark>
|
|
23
|
+
</Document>
|
|
24
|
+
</kml>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml>
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Legacy Export</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
144.9831,-37.8336,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
</Document>
|
|
15
|
+
</kml>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Nested Folder</name>
|
|
5
|
+
<Folder>
|
|
6
|
+
<name>Trails</name>
|
|
7
|
+
<Placemark>
|
|
8
|
+
<name>Inner Trail</name>
|
|
9
|
+
<LineString>
|
|
10
|
+
<coordinates>
|
|
11
|
+
144.9631,-37.8136,0
|
|
12
|
+
144.9731,-37.8236,0
|
|
13
|
+
144.9831,-37.8336,0
|
|
14
|
+
</coordinates>
|
|
15
|
+
</LineString>
|
|
16
|
+
</Placemark>
|
|
17
|
+
</Folder>
|
|
18
|
+
</Document>
|
|
19
|
+
</kml>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Nested MultiGeometry</name>
|
|
5
|
+
<Folder>
|
|
6
|
+
<Placemark>
|
|
7
|
+
<MultiGeometry>
|
|
8
|
+
<LineString>
|
|
9
|
+
<coordinates>
|
|
10
|
+
144.9631,-37.8136,0
|
|
11
|
+
144.9731,-37.8236,0
|
|
12
|
+
144.9831,-37.8336,0
|
|
13
|
+
</coordinates>
|
|
14
|
+
</LineString>
|
|
15
|
+
</MultiGeometry>
|
|
16
|
+
</Placemark>
|
|
17
|
+
</Folder>
|
|
18
|
+
</Document>
|
|
19
|
+
</kml>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<Placemark>
|
|
5
|
+
<name>Coastal Walk</name>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
144.9831,-37.8336,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
</Document>
|
|
15
|
+
</kml>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>QGIS Export</name>
|
|
5
|
+
<description>Exported from QGIS</description>
|
|
6
|
+
<ExtendedData>
|
|
7
|
+
<Data name="layer">
|
|
8
|
+
<value>trails</value>
|
|
9
|
+
</Data>
|
|
10
|
+
</ExtendedData>
|
|
11
|
+
<Placemark>
|
|
12
|
+
<name>trail_1</name>
|
|
13
|
+
<LineString>
|
|
14
|
+
<coordinates>
|
|
15
|
+
144.9631,-37.8136,0
|
|
16
|
+
144.9731,-37.8236,0
|
|
17
|
+
144.9831,-37.8336,0
|
|
18
|
+
</coordinates>
|
|
19
|
+
</LineString>
|
|
20
|
+
</Placemark>
|
|
21
|
+
</Document>
|
|
22
|
+
</kml>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Sample Trail</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
144.9831,-37.8336,0
|
|
11
|
+
</coordinates>
|
|
12
|
+
</LineString>
|
|
13
|
+
</Placemark>
|
|
14
|
+
</Document>
|
|
15
|
+
</kml>
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Minimum Path</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
</coordinates>
|
|
11
|
+
</LineString>
|
|
12
|
+
</Placemark>
|
|
13
|
+
</Document>
|
|
14
|
+
</kml>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<name>Øresundsstien 日本語</name>
|
|
5
|
+
<Placemark>
|
|
6
|
+
<LineString>
|
|
7
|
+
<coordinates>
|
|
8
|
+
144.9631,-37.8136,0
|
|
9
|
+
144.9731,-37.8236,0
|
|
10
|
+
</coordinates>
|
|
11
|
+
</LineString>
|
|
12
|
+
</Placemark>
|
|
13
|
+
</Document>
|
|
14
|
+
</kml>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<kml xmlns="http://www.opengis.net/kml/2.2">
|
|
3
|
+
<Document>
|
|
4
|
+
<Placemark>
|
|
5
|
+
<LineString>
|
|
6
|
+
<coordinates>
|
|
7
|
+
144.9631,-37.8136,0
|
|
8
|
+
144.9731,-37.8236,0
|
|
9
|
+
</coordinates>
|
|
10
|
+
</LineString>
|
|
11
|
+
</Placemark>
|
|
12
|
+
</Document>
|
|
13
|
+
</kml>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kml
|
|
4
|
+
module Path
|
|
5
|
+
module Fixtures
|
|
6
|
+
ROOT = File.expand_path("fixtures", __dir__).freeze
|
|
7
|
+
|
|
8
|
+
def self.path(relative)
|
|
9
|
+
File.join(ROOT, relative)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.read(relative)
|
|
13
|
+
File.read(path(relative))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
CATALOG = {
|
|
17
|
+
valid: {
|
|
18
|
+
"valid/sample_route.kml" => { name: "Sample Trail", point_count: 3 },
|
|
19
|
+
"valid/sample_route.kmz" => { name: "Sample Trail", point_count: 3 },
|
|
20
|
+
"valid/placemark_name.kml" => { name: "Coastal Walk", point_count: 3 },
|
|
21
|
+
"valid/gx_track.kml" => { name: "GPS Track", point_count: 3 },
|
|
22
|
+
"valid/multi_geometry.kml" => { name: "Split Route", point_count: 2, first_point: [144.9631, -37.8136] },
|
|
23
|
+
"valid/namespace_less.kml" => { name: "Legacy Export", point_count: 3 },
|
|
24
|
+
"valid/document_name_priority.kml" => { name: "Official Name", point_count: 2 },
|
|
25
|
+
"valid/two_points.kml" => { name: "Minimum Path", point_count: 2 },
|
|
26
|
+
"valid/coordinates_no_altitude.kml" => { name: "No Altitude", point_count: 3 },
|
|
27
|
+
"valid/coordinates_single_line.kml" => { name: "Single Line", point_count: 3 },
|
|
28
|
+
"valid/coordinates_extra_whitespace.kml" => { name: "Whitespace", point_count: 3 },
|
|
29
|
+
"valid/nested_folder.kml" => { name: "Nested Folder", point_count: 3 },
|
|
30
|
+
"valid/multiple_placemarks_first_wins.kml" => {
|
|
31
|
+
name: "Multiple Placemarks",
|
|
32
|
+
point_count: 2,
|
|
33
|
+
first_point: [144.9631, -37.8136]
|
|
34
|
+
},
|
|
35
|
+
"valid/unicode_name.kml" => { name: "Øresundsstien 日本語", point_count: 2 },
|
|
36
|
+
"valid/cdata_name.kml" => { name: "Trail & \"Path\" <special>", point_count: 2 },
|
|
37
|
+
"valid/crlf_line_endings.kml" => { name: "Sample Trail", point_count: 3 },
|
|
38
|
+
"valid/gx_track_with_timestamps.kml" => { name: "Timestamped Track", point_count: 3 },
|
|
39
|
+
"valid/gx_multitrack_first_wins.kml" => {
|
|
40
|
+
name: "Multi Track",
|
|
41
|
+
point_count: 2,
|
|
42
|
+
first_point: [144.9631, -37.8136]
|
|
43
|
+
},
|
|
44
|
+
"valid/linestring_attributes.kml" => { name: "Attributed Line", point_count: 3 },
|
|
45
|
+
"valid/qgis_export.kml" => { name: "QGIS Export", point_count: 3 },
|
|
46
|
+
"valid/invalid_coords_filtered.kml" => { name: "Filtered Coords", point_count: 3 },
|
|
47
|
+
"valid/nested_multigeometry_folder.kml" => { name: "Nested MultiGeometry", point_count: 3 },
|
|
48
|
+
"valid/many_points.kml" => { name: "Many Points", point_count: 10 },
|
|
49
|
+
"valid/linestring_before_point.kml" => { name: "Line Before Point", point_count: 2 },
|
|
50
|
+
"valid/kmz_custom_kml_name.kmz" => { name: "Minimum Path", point_count: 2 },
|
|
51
|
+
"valid/kmz_with_macosx_junk.kmz" => { name: "Sample Trail", point_count: 3 }
|
|
52
|
+
}.freeze,
|
|
53
|
+
reject: {
|
|
54
|
+
"reject/empty_document.kml" => "must contain a LineString or gx:Track",
|
|
55
|
+
"reject/point_only.kml" => "must contain a LineString or gx:Track",
|
|
56
|
+
"reject/polygon_only.kml" => "must contain a LineString or gx:Track",
|
|
57
|
+
"reject/empty_linestring.kml" => "must contain a LineString or gx:Track",
|
|
58
|
+
"reject/empty_archive.kmz" => "must contain a KML file"
|
|
59
|
+
}.freeze
|
|
60
|
+
}.freeze
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kml
|
|
4
|
+
module Path
|
|
5
|
+
class Parser
|
|
6
|
+
KMZ_CONTENT_TYPES = %w[application/vnd.google-earth.kmz application/zip].freeze
|
|
7
|
+
KML_NAMESPACES = {
|
|
8
|
+
"kml" => "http://www.opengis.net/kml/2.2",
|
|
9
|
+
"gx" => "http://www.google.com/kml/ext/2.2"
|
|
10
|
+
}.freeze
|
|
11
|
+
NAME_XPATHS = [
|
|
12
|
+
["//kml:Document/kml:name", KML_NAMESPACES],
|
|
13
|
+
["//kml:Placemark/kml:name", KML_NAMESPACES],
|
|
14
|
+
["//kml:name", KML_NAMESPACES],
|
|
15
|
+
["//Document/name", nil],
|
|
16
|
+
["//Placemark/name", nil]
|
|
17
|
+
].freeze
|
|
18
|
+
MISSING_GEOMETRY = "must contain a LineString or gx:Track"
|
|
19
|
+
MISSING_KML = "must contain a KML file"
|
|
20
|
+
|
|
21
|
+
def initialize(file:)
|
|
22
|
+
@file = file
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse
|
|
26
|
+
@parse ||= build_parse_result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def parse!
|
|
30
|
+
result = parse
|
|
31
|
+
raise ParseError, result.error if result.failure?
|
|
32
|
+
|
|
33
|
+
result
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def name
|
|
37
|
+
parse.success? ? parse.name : extract_name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def content
|
|
41
|
+
parse
|
|
42
|
+
@content
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
attr_reader :file
|
|
48
|
+
|
|
49
|
+
def build_parse_result
|
|
50
|
+
kml_content = extract_kml_content
|
|
51
|
+
return failure(MISSING_KML) if kml_content.nil? || kml_content.empty?
|
|
52
|
+
|
|
53
|
+
@content = kml_content
|
|
54
|
+
coordinates = extract_coordinates
|
|
55
|
+
return failure(MISSING_GEOMETRY) if coordinates.nil? || coordinates.empty?
|
|
56
|
+
|
|
57
|
+
Result.new(success: true, name: extract_name, coordinates:, error: nil)
|
|
58
|
+
rescue Zip::Error => e
|
|
59
|
+
failure("could not be parsed: #{e.message}")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def failure(message)
|
|
63
|
+
Result.new(success: false, name: nil, coordinates: nil, error: message)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def extract_kml_content
|
|
67
|
+
file_content = read_file
|
|
68
|
+
return file_content unless kmz_file?
|
|
69
|
+
|
|
70
|
+
extract_kml_from_kmz(file_content)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def read_file
|
|
74
|
+
@read_file ||= begin
|
|
75
|
+
file.rewind if file.respond_to?(:rewind)
|
|
76
|
+
file.read
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def kmz_file?
|
|
81
|
+
filename = file.original_filename.to_s.downcase
|
|
82
|
+
return true if filename.end_with?(".kmz")
|
|
83
|
+
|
|
84
|
+
KMZ_CONTENT_TYPES.include?(file.content_type.to_s)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def extract_kml_from_kmz(content)
|
|
88
|
+
kml = nil
|
|
89
|
+
|
|
90
|
+
Zip::File.open_buffer(content) do |archive|
|
|
91
|
+
entry_name = archive.find_entry("doc.kml")&.name || find_kml_entry_name(archive)
|
|
92
|
+
kml = archive.get_input_stream(entry_name, &:read) if entry_name
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
kml
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def find_kml_entry_name(archive)
|
|
99
|
+
archive.entries.map(&:name).reject do |name|
|
|
100
|
+
name.start_with?("__MACOSX/") || !name.downcase.end_with?(".kml")
|
|
101
|
+
end.first
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def extract_name
|
|
105
|
+
NAME_XPATHS.each do |xpath, namespaces|
|
|
106
|
+
document_name = xpath_value(xpath, namespaces)
|
|
107
|
+
return document_name if document_name
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
present_string(File.basename(file.original_filename.to_s, ".*")) || "Untitled"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def extract_coordinates
|
|
114
|
+
coordinates_node = line_string_coordinates_node || gx_track_coordinate_node
|
|
115
|
+
return unless coordinates_node
|
|
116
|
+
|
|
117
|
+
coordinates_from_node(coordinates_node)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def document
|
|
121
|
+
@document ||= Nokogiri::XML(@content)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def coordinates_from_node(node)
|
|
125
|
+
case node.name
|
|
126
|
+
when "coordinates"
|
|
127
|
+
parse_coordinates_text(node.text)
|
|
128
|
+
when "coord"
|
|
129
|
+
parse_gx_coords(gx_track_coordinate_nodes)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def xpath_value(xpath, namespaces)
|
|
134
|
+
node = namespaces ? document.at_xpath(xpath, namespaces) : document.at_xpath(xpath)
|
|
135
|
+
present_string(node&.text)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def line_string_coordinates_node
|
|
139
|
+
document.at_xpath("//kml:LineString/kml:coordinates", KML_NAMESPACES) ||
|
|
140
|
+
document.at_xpath("//LineString/coordinates")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def gx_track_coordinate_node
|
|
144
|
+
first_gx_track&.at_xpath("gx:coord", KML_NAMESPACES) ||
|
|
145
|
+
first_gx_track&.at_xpath("*[local-name()='coord']")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def gx_track_coordinate_nodes
|
|
149
|
+
track = first_gx_track
|
|
150
|
+
return document.xpath("//none") unless track
|
|
151
|
+
|
|
152
|
+
nodes = track.xpath("gx:coord", KML_NAMESPACES)
|
|
153
|
+
return nodes if nodes.any?
|
|
154
|
+
|
|
155
|
+
track.xpath("*[local-name()='coord']")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def first_gx_track
|
|
159
|
+
document.at_xpath("//gx:Track", KML_NAMESPACES) ||
|
|
160
|
+
document.at_xpath("//*[local-name()='Track']")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def parse_coordinates_text(text)
|
|
164
|
+
normalized = text.gsub(/\s*,\s*/, ",")
|
|
165
|
+
normalized.strip.split(/\s+/).filter_map do |coordinate|
|
|
166
|
+
parse_coordinate_values(coordinate.split(","))
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def parse_gx_coords(nodes)
|
|
171
|
+
nodes.filter_map { |node| parse_coordinate_values(node.text.strip.split) }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def parse_coordinate_values(values)
|
|
175
|
+
return if values.length < 2
|
|
176
|
+
|
|
177
|
+
longitude = values[0].to_f
|
|
178
|
+
latitude = values[1].to_f
|
|
179
|
+
altitude = values[2]&.to_f
|
|
180
|
+
return unless valid_coordinate?(latitude, longitude)
|
|
181
|
+
|
|
182
|
+
[longitude, latitude, altitude]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def valid_coordinate?(latitude, longitude)
|
|
186
|
+
latitude.between?(-90, 90) && longitude.between?(-180, 180)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def present_string(value)
|
|
190
|
+
return if value.nil?
|
|
191
|
+
|
|
192
|
+
stripped = value.to_s.strip
|
|
193
|
+
stripped.empty? ? nil : stripped
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kml
|
|
4
|
+
module Path
|
|
5
|
+
class Result
|
|
6
|
+
attr_reader :name, :coordinates, :error
|
|
7
|
+
|
|
8
|
+
def initialize(success:, name:, coordinates:, error:)
|
|
9
|
+
@success = success
|
|
10
|
+
@name = name
|
|
11
|
+
@coordinates = coordinates
|
|
12
|
+
@error = error
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def success?
|
|
16
|
+
@success
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def failure?
|
|
20
|
+
!success?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/kml.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
require "zip"
|
|
5
|
+
require_relative "kml/path/version"
|
|
6
|
+
require_relative "kml/path/parse_error"
|
|
7
|
+
require_relative "kml/path/result"
|
|
8
|
+
require_relative "kml/path/fixtures"
|
|
9
|
+
require_relative "kml/path/parser"
|
|
10
|
+
|
|
11
|
+
module Kml
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kml-path-parser
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Josh McArthur
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: nokogiri
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.15'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.15'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rubyzip
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.3'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.3'
|
|
40
|
+
description: Parse LineString and gx:Track geometry from real-world KML and KMZ exports
|
|
41
|
+
(Google Earth, My Maps, and similar tools) into path names and coordinate arrays.
|
|
42
|
+
email:
|
|
43
|
+
- joshua.mcarthur@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- CHANGELOG.md
|
|
49
|
+
- LICENSE
|
|
50
|
+
- README.md
|
|
51
|
+
- lib/kml.rb
|
|
52
|
+
- lib/kml/path/fixtures.rb
|
|
53
|
+
- lib/kml/path/fixtures/reject/empty_archive.kmz
|
|
54
|
+
- lib/kml/path/fixtures/reject/empty_document.kml
|
|
55
|
+
- lib/kml/path/fixtures/reject/empty_linestring.kml
|
|
56
|
+
- lib/kml/path/fixtures/reject/point_only.kml
|
|
57
|
+
- lib/kml/path/fixtures/reject/polygon_only.kml
|
|
58
|
+
- lib/kml/path/fixtures/reject/single_point.kml
|
|
59
|
+
- lib/kml/path/fixtures/valid/cdata_name.kml
|
|
60
|
+
- lib/kml/path/fixtures/valid/coordinates_extra_whitespace.kml
|
|
61
|
+
- lib/kml/path/fixtures/valid/coordinates_no_altitude.kml
|
|
62
|
+
- lib/kml/path/fixtures/valid/coordinates_single_line.kml
|
|
63
|
+
- lib/kml/path/fixtures/valid/crlf_line_endings.kml
|
|
64
|
+
- lib/kml/path/fixtures/valid/document_name_priority.kml
|
|
65
|
+
- lib/kml/path/fixtures/valid/gx_multitrack_first_wins.kml
|
|
66
|
+
- lib/kml/path/fixtures/valid/gx_track.kml
|
|
67
|
+
- lib/kml/path/fixtures/valid/gx_track_with_timestamps.kml
|
|
68
|
+
- lib/kml/path/fixtures/valid/invalid_coords_filtered.kml
|
|
69
|
+
- lib/kml/path/fixtures/valid/kmz_custom_kml_name.kmz
|
|
70
|
+
- lib/kml/path/fixtures/valid/kmz_with_macosx_junk.kmz
|
|
71
|
+
- lib/kml/path/fixtures/valid/linestring_attributes.kml
|
|
72
|
+
- lib/kml/path/fixtures/valid/linestring_before_point.kml
|
|
73
|
+
- lib/kml/path/fixtures/valid/many_points.kml
|
|
74
|
+
- lib/kml/path/fixtures/valid/multi_geometry.kml
|
|
75
|
+
- lib/kml/path/fixtures/valid/multiple_placemarks_first_wins.kml
|
|
76
|
+
- lib/kml/path/fixtures/valid/namespace_less.kml
|
|
77
|
+
- lib/kml/path/fixtures/valid/nested_folder.kml
|
|
78
|
+
- lib/kml/path/fixtures/valid/nested_multigeometry_folder.kml
|
|
79
|
+
- lib/kml/path/fixtures/valid/placemark_name.kml
|
|
80
|
+
- lib/kml/path/fixtures/valid/qgis_export.kml
|
|
81
|
+
- lib/kml/path/fixtures/valid/sample_route.kml
|
|
82
|
+
- lib/kml/path/fixtures/valid/sample_route.kmz
|
|
83
|
+
- lib/kml/path/fixtures/valid/two_points.kml
|
|
84
|
+
- lib/kml/path/fixtures/valid/unicode_name.kml
|
|
85
|
+
- lib/kml/path/fixtures/valid/unnamed.kml
|
|
86
|
+
- lib/kml/path/parse_error.rb
|
|
87
|
+
- lib/kml/path/parser.rb
|
|
88
|
+
- lib/kml/path/result.rb
|
|
89
|
+
- lib/kml/path/version.rb
|
|
90
|
+
homepage: https://github.com/joshmcarthur/kml-path-parser
|
|
91
|
+
licenses:
|
|
92
|
+
- MIT
|
|
93
|
+
metadata:
|
|
94
|
+
rubygems_mfa_required: 'true'
|
|
95
|
+
homepage_uri: https://github.com/joshmcarthur/kml-path-parser
|
|
96
|
+
source_code_uri: https://github.com/joshmcarthur/kml-path-parser
|
|
97
|
+
changelog_uri: https://github.com/joshmcarthur/kml-path-parser/blob/main/CHANGELOG.md
|
|
98
|
+
rdoc_options: []
|
|
99
|
+
require_paths:
|
|
100
|
+
- lib
|
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '3.2'
|
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
requirements: []
|
|
112
|
+
rubygems_version: 4.0.10
|
|
113
|
+
specification_version: 4
|
|
114
|
+
summary: Extract paths from user-uploaded KML and KMZ files
|
|
115
|
+
test_files: []
|