geo_combine 0.11.0 → 0.12.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 +4 -4
- data/.github/workflows/ruby.yml +35 -12
- data/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +2 -10
- data/Gemfile +3 -0
- data/README.md +106 -63
- data/geo_combine.gemspec +1 -1
- data/lib/geo_combine/exceptions.rb +3 -0
- data/lib/geo_combine/fgdc.rb +7 -0
- data/lib/geo_combine/geoblacklight_aardvark.rb +70 -0
- data/lib/geo_combine/harvester.rb +104 -38
- data/lib/geo_combine/indexer.rb +39 -2
- data/lib/geo_combine/iso19139.rb +7 -0
- data/lib/geo_combine/migrators/v1_aardvark_migrator.rb +1 -2
- data/lib/geo_combine/schema.rb +32 -0
- data/lib/geo_combine/version.rb +1 -1
- data/lib/geo_combine/withdrawals.rb +125 -0
- data/lib/geo_combine.rb +25 -1
- data/lib/tasks/geo_combine.rake +11 -12
- data/lib/xslt/fgdc2Aardvark.xsl +1512 -0
- data/lib/xslt/iso2Aardvark.xsl +1634 -0
- data/spec/fixtures/docs/arizona_reservations_iso.xml +1183 -0
- data/spec/fixtures/docs/arizona_topo_iso.xml +560 -0
- data/spec/fixtures/docs/cornell_agdistricts_fgdc.xml +572 -0
- data/spec/fixtures/docs/duplicate_values_fgdc.xml +116 -0
- data/spec/fixtures/docs/gbl_keywords_fgdc.xml +105 -0
- data/spec/fixtures/docs/harvard_euratlas_fgdc.xml +449 -0
- data/spec/fixtures/docs/harvard_glb_fgdc.xml +455 -0
- data/spec/fixtures/docs/harvard_kng_fgdc.xml +403 -0
- data/spec/fixtures/docs/harvard_mapa_fgdc.xml +356 -0
- data/spec/fixtures/docs/harvard_nhgis_fgdc.xml +1706 -0
- data/spec/fixtures/docs/harvard_usgs_fgdc.xml +393 -0
- data/spec/fixtures/docs/nyu_colleges_iso.xml +616 -0
- data/spec/fixtures/docs/services_fgdc.xml +227 -0
- data/spec/fixtures/docs/services_iso.xml +433 -0
- data/spec/fixtures/indexing/no_schema.json +5 -0
- data/spec/fixtures/withdrawal/edu.example-a/metadata-aardvark/a-contradicted.json +8 -0
- data/spec/fixtures/withdrawal/edu.example-a/metadata-aardvark/a-live.json +8 -0
- data/spec/fixtures/withdrawal/edu.example-a/withdrawn.json +13 -0
- data/spec/fixtures/withdrawal/edu.example-b/metadata-aardvark/b-live.json +8 -0
- data/spec/fixtures/withdrawal/edu.example-b/withdrawn.json +7 -0
- data/spec/fixtures/withdrawal/edu.example-broken/withdrawn.json +4 -0
- data/spec/fixtures/withdrawal/edu.example-skipped/withdrawn.json +7 -0
- data/spec/fixtures/xml_docs.rb +110 -0
- data/spec/integration/withdrawals_spec.rb +47 -0
- data/spec/lib/geo_combine/ckan_metadata_spec.rb +44 -0
- data/spec/lib/geo_combine/fgdc_spec.rb +520 -0
- data/spec/lib/geo_combine/geoblacklight_aardvark_spec.rb +85 -0
- data/spec/lib/geo_combine/harvester_spec.rb +221 -11
- data/spec/lib/geo_combine/indexer_spec.rb +140 -2
- data/spec/lib/geo_combine/iso19139_spec.rb +540 -1
- data/spec/lib/geo_combine/logger_spec.rb +38 -0
- data/spec/lib/geo_combine/ogp_spec.rb +37 -0
- data/spec/lib/geo_combine/schema_spec.rb +74 -0
- data/spec/lib/geo_combine/withdrawals_spec.rb +294 -0
- data/spec/lib/tasks/geo_combine_spec.rb +51 -0
- data/spec/spec_helper.rb +4 -1
- metadata +68 -12
- data/lib/.DS_Store +0 -0
- data/lib/geo_combine/geo_blacklight_harvester.rb +0 -216
- data/lib/xslt/isoAardvark.xsl +0 -738
- data/spec/lib/geo_combine/geo_blacklight_harvester_spec.rb +0 -195
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d06fb38092cd8d8db20f1c9dfa14c82c19162d23415d1c34d92a2edf3826ff56
|
|
4
|
+
data.tar.gz: c2f2e35183978ec29e04d976438c9c5c567dc05341b128074366359d6164b027
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13720c277a1d399d83a882df36eaad9f6c5cbef537fd2753f8391cc8ec73ab9f524308b2adc9c2b1b3bcefff44b928bd4d6b3fa5d05973b492e90bf79c0d524e
|
|
7
|
+
data.tar.gz: b9a42c999193d944a4c0b9fb309bcbb27a0f66bd8a022ea894cf7594acaf4370d66d1ed2a6d8b7601a1f8b6af1965dc343b823ebcf920790e5fe5e2a14de748d
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -6,11 +6,11 @@ jobs:
|
|
|
6
6
|
rubocop:
|
|
7
7
|
runs-on: ubuntu-latest
|
|
8
8
|
steps:
|
|
9
|
-
- uses: actions/checkout@
|
|
9
|
+
- uses: actions/checkout@v7
|
|
10
10
|
- name: Set up Ruby and install dependencies
|
|
11
11
|
uses: ruby/setup-ruby@v1
|
|
12
12
|
with:
|
|
13
|
-
ruby-version: 3.
|
|
13
|
+
ruby-version: 3.3
|
|
14
14
|
bundler-cache: true
|
|
15
15
|
- name: Run linter
|
|
16
16
|
run: bundle exec rubocop
|
|
@@ -18,27 +18,50 @@ jobs:
|
|
|
18
18
|
test:
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
strategy:
|
|
21
|
+
fail-fast: false
|
|
21
22
|
matrix:
|
|
22
|
-
ruby: [3.
|
|
23
|
-
faraday_version: ['', '~> 1.0'] # Defaults to whatever's the most recent version.
|
|
23
|
+
ruby: [3.3, 3.4, 4.0]
|
|
24
24
|
steps:
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
26
|
|
|
27
27
|
- name: Set up Ruby and install dependencies
|
|
28
28
|
uses: ruby/setup-ruby@v1
|
|
29
29
|
with:
|
|
30
30
|
ruby-version: ${{ matrix.ruby }}
|
|
31
31
|
bundler-cache: true
|
|
32
|
-
env:
|
|
33
|
-
FARADAY_VERSION: ${{ matrix.faraday_version }}
|
|
34
32
|
|
|
35
|
-
- name: "Run tests for Ruby ${{ matrix.ruby }}
|
|
33
|
+
- name: "Run tests for Ruby ${{ matrix.ruby }}"
|
|
36
34
|
run: bundle exec rake spec
|
|
37
|
-
env:
|
|
38
|
-
FARADAY_VERSION: ${{ matrix.faraday_version }}
|
|
39
35
|
|
|
40
36
|
- name: Upload coverage artifacts
|
|
41
|
-
uses: actions/upload-artifact@
|
|
37
|
+
uses: actions/upload-artifact@v7
|
|
42
38
|
with:
|
|
43
|
-
name: "coverage-${{ matrix.ruby }}
|
|
39
|
+
name: "coverage-${{ matrix.ruby }}"
|
|
44
40
|
path: coverage/
|
|
41
|
+
|
|
42
|
+
# Runs the specs that need a real Solr, using the GeoBlacklight schema
|
|
43
|
+
integration:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
env:
|
|
46
|
+
GEOBLACKLIGHT_VERSION: v5.4.0
|
|
47
|
+
SOLR_INTEGRATION_URL: http://localhost:8983/solr/blacklight-core
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v7
|
|
50
|
+
|
|
51
|
+
- name: Start Solr with the GeoBlacklight ${{ env.GEOBLACKLIGHT_VERSION }} configuration
|
|
52
|
+
run: |
|
|
53
|
+
mkdir -p tmp/solr-conf
|
|
54
|
+
curl -sfL "https://github.com/geoblacklight/geoblacklight/archive/refs/tags/${GEOBLACKLIGHT_VERSION}.tar.gz" \
|
|
55
|
+
| tar -xz -C tmp/solr-conf --strip-components=3 "geoblacklight-${GEOBLACKLIGHT_VERSION#v}/solr/conf"
|
|
56
|
+
rm tmp/solr-conf/core.properties
|
|
57
|
+
docker run -d -p 8983:8983 -v "$PWD/tmp/solr-conf:/gbl-conf:ro" solr:9 solr-precreate blacklight-core /gbl-conf
|
|
58
|
+
timeout 120 sh -c 'until curl -sf "$SOLR_INTEGRATION_URL/get?id=ready" > /dev/null; do sleep 2; done'
|
|
59
|
+
|
|
60
|
+
- name: Set up Ruby and install dependencies
|
|
61
|
+
uses: ruby/setup-ruby@v1
|
|
62
|
+
with:
|
|
63
|
+
ruby-version: 3.4
|
|
64
|
+
bundler-cache: true
|
|
65
|
+
|
|
66
|
+
- name: Run tests, including integration tests against Solr
|
|
67
|
+
run: bundle exec rake spec
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
|
@@ -64,7 +64,6 @@ RSpec/ExampleLength:
|
|
|
64
64
|
# Offense count: 4
|
|
65
65
|
RSpec/ExpectInHook:
|
|
66
66
|
Exclude:
|
|
67
|
-
- 'spec/lib/geo_combine/geo_blacklight_harvester_spec.rb'
|
|
68
67
|
- 'spec/lib/geo_combine/geoblacklight_spec.rb'
|
|
69
68
|
|
|
70
69
|
# Offense count: 23
|
|
@@ -73,7 +72,6 @@ RSpec/ExpectInHook:
|
|
|
73
72
|
RSpec/MessageSpies:
|
|
74
73
|
Exclude:
|
|
75
74
|
- 'spec/lib/geo_combine/esri_open_data_spec.rb'
|
|
76
|
-
- 'spec/lib/geo_combine/geo_blacklight_harvester_spec.rb'
|
|
77
75
|
- 'spec/lib/geo_combine/geoblacklight_spec.rb'
|
|
78
76
|
- 'spec/lib/geo_combine/ogp_spec.rb'
|
|
79
77
|
- 'spec/lib/geo_combine_spec.rb'
|
|
@@ -121,7 +119,6 @@ RSpec/SpecFilePathFormat:
|
|
|
121
119
|
RSpec/StubbedMock:
|
|
122
120
|
Exclude:
|
|
123
121
|
- 'spec/lib/geo_combine/esri_open_data_spec.rb'
|
|
124
|
-
- 'spec/lib/geo_combine/geo_blacklight_harvester_spec.rb'
|
|
125
122
|
- 'spec/lib/geo_combine/geoblacklight_spec.rb'
|
|
126
123
|
- 'spec/lib/geo_combine/ogp_spec.rb'
|
|
127
124
|
- 'spec/lib/geo_combine_spec.rb'
|
|
@@ -131,16 +128,11 @@ RSpec/SubjectStub:
|
|
|
131
128
|
Exclude:
|
|
132
129
|
- 'spec/lib/geo_combine/ogp_spec.rb'
|
|
133
130
|
|
|
134
|
-
# Offense count: 1
|
|
135
|
-
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
|
|
136
|
-
RSpec/VerifiedDoubles:
|
|
137
|
-
Exclude:
|
|
138
|
-
- 'spec/lib/geo_combine/geo_blacklight_harvester_spec.rb'
|
|
139
|
-
|
|
140
131
|
# Offense count: 1
|
|
141
132
|
Security/Open:
|
|
142
133
|
Exclude:
|
|
143
134
|
- 'lib/geo_combine/geoblacklight.rb'
|
|
135
|
+
- 'lib/geo_combine/geoblacklight_aardvark.rb'
|
|
144
136
|
|
|
145
137
|
# Offense count: 6
|
|
146
138
|
# Configuration parameters: AllowedConstants.
|
|
@@ -150,8 +142,8 @@ Style/Documentation:
|
|
|
150
142
|
- 'test/**/*'
|
|
151
143
|
- 'lib/geo_combine/bounding_box.rb'
|
|
152
144
|
- 'lib/geo_combine/ckan_metadata.rb'
|
|
153
|
-
- 'lib/geo_combine/geo_blacklight_harvester.rb'
|
|
154
145
|
- 'lib/geo_combine/geoblacklight.rb'
|
|
146
|
+
- 'lib/geo_combine/geoblacklight_aardvark.rb'
|
|
155
147
|
- 'lib/geo_combine/geometry_types.rb'
|
|
156
148
|
- 'lib/geo_combine/iso19139.rb'
|
|
157
149
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# GeoCombine
|
|
2
2
|
|
|
3
3
|

|
|
4
|
-
[]()
|
|
5
5
|
[](https://github.com/OpenGeoMetadata/GeoCombine/releases)
|
|
6
6
|
|
|
7
7
|
A Ruby toolkit for managing geospatial metadata, including:
|
|
8
8
|
|
|
9
9
|
- harvesting and indexing records from OpenGeoMetadata
|
|
10
|
-
- harvesting records directly from a GeoBlacklight instance
|
|
11
10
|
- converting geospatial metadata records between standards
|
|
11
|
+
- migrating OpenGeoMetadata records from older to newer schema versions
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -54,6 +54,14 @@ By default, GeoCombine will only fetch and index records that are not marked as
|
|
|
54
54
|
export OGM_SKIP_RESTRICTED=false
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
#### GitHub API token
|
|
58
|
+
|
|
59
|
+
GeoCombine uses the GitHub API to find OpenGeoMetadata repositories. Unauthenticated requests are limited to 60 per hour, and `geocombine:clone` makes one for each repository. If you hit the limit, set the `GITHUB_TOKEN` environment variable to a GitHub personal access token to raise it; the token only needs read access to public repositories:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
export GITHUB_TOKEN=your-token
|
|
63
|
+
```
|
|
64
|
+
|
|
57
65
|
#### Clone OpenGeoMetadata repositories locally
|
|
58
66
|
|
|
59
67
|
```sh
|
|
@@ -118,6 +126,36 @@ You can also set the Solr instance URL using `SOLR_URL`:
|
|
|
118
126
|
SOLR_URL=http://www.example.com:1234/solr/collection bundle exec rake geocombine:index
|
|
119
127
|
```
|
|
120
128
|
|
|
129
|
+
#### Withdrawn records
|
|
130
|
+
|
|
131
|
+
An OpenGeoMetadata repository can withdraw records by listing them in a `withdrawn.json` file at its root:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"$schema": "https://opengeometadata.org/schema/ogm-withdrawals-1.0.json",
|
|
136
|
+
"ogm_version": "1.0",
|
|
137
|
+
"withdrawn": [
|
|
138
|
+
{ "id": "stanford-bb058zh0946", "date": "2026-03-14", "reason": "superseded" }
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
After indexing, `geocombine:index` deletes the records these files list from your Solr index. It is careful about what it deletes:
|
|
144
|
+
|
|
145
|
+
- A record is only deleted if a `withdrawn.json` lists its `id` (or `layer_slug_s`, for `SCHEMA_VERSION=1.0`). Removing a record's file from a repository does not delete it from your index.
|
|
146
|
+
- A record that is still present in the repository is never deleted, even if it is also listed as withdrawn. GeoCombine logs a warning instead.
|
|
147
|
+
- A `withdrawn.json` that can't be read is logged and skipped; it doesn't stop indexing.
|
|
148
|
+
- Repositories listed in `OGM_SKIP_REPOS` can't delete anything.
|
|
149
|
+
- If the harvest stops before it has walked every record, nothing is deleted.
|
|
150
|
+
|
|
151
|
+
GeoCombine keeps no record of what it has deleted. Every run deletes every withdrawn record again, which changes nothing for records that are already gone. Deletes match by `id` alone, so a withdrawn `id` that matches a record you indexed from somewhere other than OpenGeoMetadata will delete that record too.
|
|
152
|
+
|
|
153
|
+
To delete withdrawn records without reindexing everything, run:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
bundle exec rake geocombine:withdraw
|
|
157
|
+
```
|
|
158
|
+
|
|
121
159
|
#### Logging
|
|
122
160
|
|
|
123
161
|
Some of the tools and scripts in this gem use Ruby's `Logger` class to print information to `$stderr`. By default, the log level is set to `Logger::INFO`. For more verbose information, you can set the `LOG_LEVEL` environment variable to `DEBUG`:
|
|
@@ -152,67 +190,6 @@ Or:
|
|
|
152
190
|
OGM_PATH=/path/to/your/file.json rake geocombine:index
|
|
153
191
|
```
|
|
154
192
|
|
|
155
|
-
### Harvesting and indexing documents from GeoBlacklight sites
|
|
156
|
-
|
|
157
|
-
GeoCombine provides a Harvester class and rake task to harvest and index content from GeoBlacklight sites (or any site that follows the Blacklight API format). Given that the configurations can change from consumer to consumer and site to site, the class provides a relatively simple configuration API. This can be configured in an initializer, a wrapping rake task, or any other ruby context where the rake task our class would be invoked.
|
|
158
|
-
|
|
159
|
-
```sh
|
|
160
|
-
bundle exec rake geocombine:geoblacklight_harvester:index[YOUR_CONFIGURED_SITE_KEY]
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
#### Harvester configuration
|
|
164
|
-
|
|
165
|
-
Only the sites themselves are required to be configured but there are various configuration options that can (optionally) be supplied to modify the harvester's behavior.
|
|
166
|
-
|
|
167
|
-
```ruby
|
|
168
|
-
GeoCombine::GeoBlacklightHarvester.configure do
|
|
169
|
-
{
|
|
170
|
-
commit_within: '10000',
|
|
171
|
-
crawl_delay: 1, # All sites
|
|
172
|
-
debug: true,
|
|
173
|
-
SITE1: {
|
|
174
|
-
crawl_delay: 2, # SITE1 only
|
|
175
|
-
host: 'https://geoblacklight.example.edu',
|
|
176
|
-
params: {
|
|
177
|
-
f: {
|
|
178
|
-
dct_provenance_s: ['Institution']
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
SITE2: {
|
|
183
|
-
host: 'https://geoportal.example.edu',
|
|
184
|
-
params: {
|
|
185
|
-
q: '*'
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
end
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
##### Crawl Delays (default: none)
|
|
193
|
-
|
|
194
|
-
Crawl delays can be configured (in seconds) either globally for all sites or on a per-site basis. This will cause a delay for that number of seconds between each search results page (note that Blacklight 7 necessitates a lot of requests per results page and this only causes the delay per page of results)
|
|
195
|
-
|
|
196
|
-
##### Solr's commitWithin (default: 5000 milliseconds)
|
|
197
|
-
|
|
198
|
-
Solr's commitWithin option can be configured (in milliseconds) by passing a value under the commit_within key.
|
|
199
|
-
|
|
200
|
-
#### Transforming Documents
|
|
201
|
-
|
|
202
|
-
You may need to transform documents that are harvested for various purposes (removing fields, adding fields, omitting a document all together, etc). You can configure some ruby code (a proc) that will take the document in, transform it, and return the transformed document. By default the indexer will remove the `score`, `timestamp`, and `_version_` fields from the documents harvested. If you provide your own transformer, you'll likely want to remove these fields in addition to the other transformations you provide.
|
|
203
|
-
|
|
204
|
-
```ruby
|
|
205
|
-
GeoCombine::GeoBlacklightIndexer.document_transformer = -> (document) do
|
|
206
|
-
# Removes "bogus_field" from the content we're harvesting
|
|
207
|
-
# in addition to some other solr fields we don't want
|
|
208
|
-
%w[_version_ score timestamp bogus_field].each do |field|
|
|
209
|
-
document.delete(field)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
document
|
|
213
|
-
end
|
|
214
|
-
```
|
|
215
|
-
|
|
216
193
|
### Converting metadata
|
|
217
194
|
|
|
218
195
|
#### Converting metadata into GeoBlacklight JSON
|
|
@@ -250,6 +227,66 @@ Some formats also support conversion into HTML for display in a web browser:
|
|
|
250
227
|
> iso_metadata.to_html
|
|
251
228
|
```
|
|
252
229
|
|
|
230
|
+
#### Converting metadata into Aardvark JSON
|
|
231
|
+
|
|
232
|
+
`GeoCombine::Fgdc` and `GeoCombine::Iso19139` also implement `#to_aardvark`, which converts FGDC or ISO 19139 XML into the [OGM Aardvark schema](https://opengeometadata.org/ogm-aardvark/) and returns a `GeoCombine::GeoblacklightAardvark`:
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# Create a new Fgdc object
|
|
236
|
+
> fgdc_metadata = GeoCombine::Fgdc.new('./tmp/opengeometadata/edu.tufts/0/108/220/208/fgdc.xml')
|
|
237
|
+
|
|
238
|
+
# Convert to the Aardvark schema
|
|
239
|
+
> aardvark = fgdc_metadata.to_aardvark
|
|
240
|
+
|
|
241
|
+
# Validate it against the Aardvark JSON schema
|
|
242
|
+
> aardvark.valid?
|
|
243
|
+
=> true
|
|
244
|
+
|
|
245
|
+
# Output it as JSON instead of a Ruby hash
|
|
246
|
+
> aardvark.to_json
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Aardvark defines some fields that FGDC and ISO have no source for, such as `gbl_suppressed_b`, and the links in a record may not be the ones you want to publish. Pass fields in and they are merged into the transformed record, replacing any derived values:
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
> fgdc_metadata.to_aardvark(
|
|
253
|
+
'id' => 'tufts-ecuador-drilling-towers',
|
|
254
|
+
'schema_provider_s' => 'Tufts',
|
|
255
|
+
'dct_references_s' => { 'http://schema.org/url' => 'https://example.edu/catalog/tufts-1' }.to_json
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Note about `schema_provider_s` and `id`:
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
# Without a provider, id and provider are derived from the record
|
|
263
|
+
> fgdc_metadata.to_aardvark.metadata['id']
|
|
264
|
+
=> "tufts-university-gis-center-ecuador50kdrillingtower11"
|
|
265
|
+
|
|
266
|
+
# With one, the provider is used as the id prefix
|
|
267
|
+
> fgdc_metadata.to_aardvark('schema_provider_s' => 'Tufts').metadata['id']
|
|
268
|
+
=> "tufts-ecuador50kdrillingtower11"
|
|
269
|
+
|
|
270
|
+
# Or set the id yourself
|
|
271
|
+
> fgdc_metadata.to_aardvark('id' => 'tufts-ecuador-drilling-towers').metadata['id']
|
|
272
|
+
=> "tufts-ecuador-drilling-towers"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The FGDC transformation fills in `dct_references_s` from the citation's online linkage and the network resources of each digital form, classified by URL and by the format they're distributed in: a landing page, downloads (a labeled list when there's more than one), FGDC and HTML metadata, and OGC (WMS, WFS, WCS, WMTS) and Esri service endpoints. A browse graphic URL becomes the thumbnail, and `gbl_wxsIdentifier_s` is the layer an OGC request asks for. References you pass in replace the derived ones.
|
|
276
|
+
|
|
277
|
+
ISO 19139 records (including ISO 19115-2 `gmi:MI_Metadata`) work the same way. Their ids use the last segment of the `fileIdentifier`, falling back to the `dataSetURI`, the citation identifier and the title:
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
> iso_metadata = GeoCombine::Iso19139.new('./tmp/opengeometadata/edu.stanford.purl/bb/338/jh/0716/iso19139.xml')
|
|
281
|
+
|
|
282
|
+
> iso_metadata.to_aardvark('schema_provider_s' => 'Stanford').metadata['id']
|
|
283
|
+
=> "stanford-bb338jh0716"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The ISO transformation also fills in `dct_references_s`, from the dataset URI, downloads, and OGC (WMS, WFS, WCS, WMTS) and Esri service links, along with `gbl_wxsIdentifier_s` from the name of an OGC link. References you pass in replace the derived ones.
|
|
287
|
+
|
|
288
|
+
Some values can't be derived reliably and are left for you to pass in: `gbl_resourceType_sm` for vector data whose geometry is only described as `composite` (used for both lines and polygons), and `pcdm_memberOf_sm` and `dct_isPartOf_sm`, which take the ids of other Aardvark records.
|
|
289
|
+
|
|
253
290
|
### Migrating metadata
|
|
254
291
|
|
|
255
292
|
You can use the `GeoCombine::Migrators` to migrate metadata from one schema to another.
|
|
@@ -284,6 +321,12 @@ To run the tests, use:
|
|
|
284
321
|
bundle exec rake spec
|
|
285
322
|
```
|
|
286
323
|
|
|
324
|
+
Some tests need a real Solr core running the GeoBlacklight schema. They delete documents, so they only run when `SOLR_INTEGRATION_URL` points at a core you don't mind them changing:
|
|
325
|
+
|
|
326
|
+
```sh
|
|
327
|
+
SOLR_INTEGRATION_URL=http://localhost:8983/solr/blacklight-core bundle exec rake spec
|
|
328
|
+
```
|
|
329
|
+
|
|
287
330
|
## Contributing
|
|
288
331
|
|
|
289
332
|
1. Fork it ( https://github.com/[my-github-username]/GeoCombine/fork )
|
data/geo_combine.gemspec
CHANGED
|
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.add_dependency 'sanitize'
|
|
26
26
|
spec.add_dependency 'thor'
|
|
27
27
|
spec.add_dependency 'faraday-net_http_persistent', '~> 2.0'
|
|
28
|
-
spec.add_dependency 'git'
|
|
28
|
+
spec.add_dependency 'git', '~> 6.0'
|
|
29
29
|
spec.add_dependency 'faraday-retry', '~> 2.2'
|
|
30
30
|
|
|
31
31
|
spec.add_development_dependency 'bundler'
|
data/lib/geo_combine/fgdc.rb
CHANGED
|
@@ -11,6 +11,13 @@ module GeoCombine
|
|
|
11
11
|
Nokogiri::XSLT(File.open(File.join(File.dirname(__FILE__), '../xslt/fgdc2geoBL.xsl')))
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
##
|
|
15
|
+
# Returns a Nokogiri::XSLT object containing the FGDC to Aardvark XSL
|
|
16
|
+
# @return [Nokogiri::XSLT]
|
|
17
|
+
def xsl_aardvark
|
|
18
|
+
Nokogiri::XSLT(File.open(File.join(File.dirname(__FILE__), '../xslt/fgdc2Aardvark.xsl')))
|
|
19
|
+
end
|
|
20
|
+
|
|
14
21
|
##
|
|
15
22
|
# Returns a Nokogiri::XSLT object containing the ISO19139 to HTML XSL
|
|
16
23
|
# @return [Nokogiri:XSLT]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open-uri'
|
|
4
|
+
|
|
5
|
+
module GeoCombine
|
|
6
|
+
class GeoblacklightAardvark
|
|
7
|
+
attr_reader :metadata
|
|
8
|
+
|
|
9
|
+
SCHEMA_VERSION = 'Aardvark'
|
|
10
|
+
SCHEMA_JSON_URL = 'https://raw.githubusercontent.com/OpenGeoMetadata/opengeometadata.github.io/main/docs/schema/geoblacklight-schema-aardvark.json'
|
|
11
|
+
SCHEMA_DRAFT_URL = 'http://json-schema.org/draft-04/schema#'
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# @param [String] metadata a JSON string document in the Aardvark schema
|
|
15
|
+
# @param [Hash] fields values merged with the parsed document, used for
|
|
16
|
+
# fields Aardvark defines but the source metadata cannot supply
|
|
17
|
+
def initialize(metadata, fields = {})
|
|
18
|
+
@metadata = JSON.parse(metadata).merge(fields)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Returns a string of JSON from an Aardvark hash
|
|
23
|
+
# @return [String]
|
|
24
|
+
def to_json(options = {})
|
|
25
|
+
metadata.to_json(options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# True if the document is a valid Aardvark record
|
|
30
|
+
# @return [Boolean]
|
|
31
|
+
def valid?
|
|
32
|
+
validate!
|
|
33
|
+
true
|
|
34
|
+
rescue StandardError
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Validates an Aardvark json document
|
|
40
|
+
def validate!
|
|
41
|
+
JSON::Validator.validate!(schema, to_json)
|
|
42
|
+
validate_version!
|
|
43
|
+
validate_spatial!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
##
|
|
47
|
+
# Validate gbl_mdVersion_s
|
|
48
|
+
def validate_version!
|
|
49
|
+
return if metadata['gbl_mdVersion_s'] == SCHEMA_VERSION
|
|
50
|
+
|
|
51
|
+
raise GeoCombine::Exceptions::InvalidSchemaVersion,
|
|
52
|
+
"gbl_mdVersion_s must be #{SCHEMA_VERSION.inspect}, got #{metadata['gbl_mdVersion_s'].inspect}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# Validate locn_geometry
|
|
57
|
+
def validate_spatial!
|
|
58
|
+
geometry = metadata['locn_geometry']
|
|
59
|
+
return unless geometry =~ /ENVELOPE\(/
|
|
60
|
+
|
|
61
|
+
raise GeoCombine::Exceptions::InvalidGeometry unless GeoCombine::BoundingBox.from_envelope(geometry).valid?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def schema
|
|
67
|
+
@schema ||= JSON.parse(URI.open(SCHEMA_JSON_URL).read).merge('$schema' => SCHEMA_DRAFT_URL)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|