Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions src/geosync/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[clojure.java.io :as io]
[clojure.string :as s]
[geosync.rest-api :as rest]
[geosync.utils :refer [nil-on-error url-path]]
[geosync.utils :refer [get-gpkg-native-layer-name nil-on-error url-path]]
[triangulum.logging :refer [log log-str]]
[triangulum.database :refer [call-sql]]
[taoensso.tufte :as tufte]))
Expand Down Expand Up @@ -225,7 +225,7 @@
[{:keys [geoserver-workspace autostyle-layers]}
existing-stores
existing-styles
{:keys [store-type store-name layer-name file-url style]}]
{:keys [store-type store-name layer-name native-name file-url style]}]
(let [matching-style (get-matching-style layer-name style existing-styles autostyle-layers)]
(when-not (contains? existing-stores store-name)
(case store-type
Expand All @@ -235,8 +235,8 @@

:shapefile (doall
(concat
[(rest/create-data-store geoserver-workspace store-name file-url)
(rest/create-feature-type-via-put geoserver-workspace store-name file-url)]
[(rest/create-data-store geoserver-workspace store-name file-url :shapefile)
(rest/create-feature-type-via-put geoserver-workspace store-name file-url :shapefile)]
(when (not= store-name layer-name)
[(rest/create-feature-type-alias geoserver-workspace
store-name
Expand All @@ -247,6 +247,21 @@
(when matching-style
[(rest/update-layer-style geoserver-workspace store-name matching-style :vector)])))

:geopackage (let [native-name (or native-name layer-name)]
(doall
(concat
[(rest/create-data-store geoserver-workspace store-name file-url :geopackage)
(rest/create-feature-type-via-put geoserver-workspace store-name file-url :geopackage)]
(when (not= native-name layer-name)
[(rest/create-feature-type-alias geoserver-workspace
store-name
native-name
layer-name)
(rest/delete-layer geoserver-workspace native-name)
(rest/delete-feature-type geoserver-workspace store-name native-name)])
(when matching-style
[(rest/update-layer-style geoserver-workspace layer-name matching-style :vector)]))))

:imagemosaic (do (update-properties-file! (str file-url "/datastore.properties") "schema" geoserver-workspace)
(update-properties-file! (str file-url "/indexer.properties") "Name" store-name)
(clean-image-mosaic-folder (s/replace file-url "file://" ""))
Expand Down Expand Up @@ -374,6 +389,7 @@
"external.imagemosaic" :create-coverage-store-image-mosaic
"external.geotiff" :create-coverage-via-put
"external.shp" :create-feature-type-via-put
"external.gpkg" :create-feature-type-via-put
"/coveragestores/" :update-coverage-store
"/gwc/rest/layers/" :update-cached-layer
"/layers/" :update-layer-style
Expand Down Expand Up @@ -540,6 +556,7 @@
(condp re-matches file-path
#"^.*\.tiff?$" :geotiff
#"^.*\.shp$" :shapefile
#"^.*\.gpkg$" :geopackage
#"^.*datastore\.properties$" :imagemosaic
nil))

Expand Down Expand Up @@ -585,6 +602,7 @@
(case store-type
:geotiff raster-style
:shapefile vector-style
:geopackage vector-style
:imagemosaic raster-style
nil)))
styles))))
Expand All @@ -596,14 +614,22 @@
(.exists (io/file data-dir spatial-index-file-path))))

(defn file-paths->file-specs
"Converts a sequence of relative file paths into file-spec maps. For most
formats the published layer name can be derived from the file path alone, but
GeoPackage files store their table names inside the SQLite database, where the
name can be anything — including a SQL SELECT statement when the file contains a
view. For GeoPackage files, :native-name is therefore populated via ogrinfo so
that the internal table name is known before any GeoServer API calls are made."
[data-dir styles file-paths]
(mapv #(let [store-type (get-store-type %)]
(array-map :store-type store-type
:store-name (file-path->store-name %)
:layer-name (file-path->layer-name %)
:file-url (file-path->file-url % data-dir)
:style (get-style % store-type styles)
:indexed? (has-spatial-index? % data-dir)))
(array-map :store-type store-type
:store-name (file-path->store-name %)
:layer-name (file-path->layer-name %)
:native-name (when (= store-type :geopackage)
(get-gpkg-native-layer-name data-dir %))
:file-url (file-path->file-url % data-dir)
:style (get-style % store-type styles)
:indexed? (has-spatial-index? % data-dir)))
file-paths))

(defn gis-file-seq
Expand Down
63 changes: 40 additions & 23 deletions src/geosync/rest_api.clj
Original file line number Diff line number Diff line change
Expand Up @@ -95,30 +95,44 @@
(str "/workspaces/" workspace "/datastores/" store)
nil])

;; NOTE: Only Shapefile stores are currently supported.
;; NOTE: file-url should look like file:///path/to/nyc.shp
(defn create-data-store [workspace store file-url]
;; NOTE: file-url should look like file:///path/to/nyc.shp or file:///path/to/data.gpkg
(defn create-data-store [workspace store file-url store-type]
["POST"
(str "/workspaces/" workspace "/datastores")
(xml
[:dataStore
[:workspace
[:name workspace]]
[:name store]
[:type "Shapefile"]
[:enabled true]
[:connectionParameters
[:entry {:key "url"} file-url]
[:entry {:key "fstype"} "shape"]
[:entry {:key "filetype"} "shapefile"]
[:entry {:key "charset"} "ISO-8859-1"]
[:entry {:key "create spatial index"} true]
[:entry {:key "enable spatial index"} true]
[:entry {:key "memory mapped buffer"} true]
[:entry {:key "cache and reuse memory maps"} true]]])])
(case store-type
:shapefile
(xml
[:dataStore
[:workspace
[:name workspace]]
[:name store]
[:type "Shapefile"]
[:enabled true]
[:connectionParameters
[:entry {:key "url"} file-url]
[:entry {:key "fstype"} "shape"]
[:entry {:key "filetype"} "shapefile"]
[:entry {:key "charset"} "ISO-8859-1"]
[:entry {:key "create spatial index"} true]
[:entry {:key "enable spatial index"} true]
[:entry {:key "memory mapped buffer"} true]
[:entry {:key "cache and reuse memory maps"} true]]])
:geopackage
(xml
[:dataStore
[:workspace
[:name workspace]]
[:name store]
[:type "GeoPackage"]
[:enabled true]
[:connectionParameters
[:entry {:key "database"} file-url]
[:entry {:key "dbtype"} "geopkg"]
[:entry {:key "namespace"} (str "http://" workspace)]]]))])

;; NOTE: Only Shapefile feature types are currently supported.
;; NOTE: file-url should look like file:///path/to/nyc.shp
;; NOTE: currrently unused
(defn create-data-store-via-put [workspace store file-url]
["PUT"
(str "/workspaces/" workspace "/datastores/" store "/external.shp?configure=none")
Expand Down Expand Up @@ -261,11 +275,13 @@
[:numDecimals num-decimals]
[:enabled true]])])

;; NOTE: Only Shapefile feature types are currently supported.
;; NOTE: file-url should look like file:///path/to/nyc.shp
(defn create-feature-type-via-put [workspace store file-url]
;; NOTE: file-url should look like file:///path/to/nyc.shp or file:///path/to/data.gpkg
(defn create-feature-type-via-put [workspace store file-url store-type]
["PUT"
(str "/workspaces/" workspace "/datastores/" store "/external.shp")
(str "/workspaces/" workspace "/datastores/" store
(case store-type
:shapefile "/external.shp"
:geopackage "/external.gpkg"))
file-url])

(defn create-feature-type-alias [workspace store old-feature-type new-feature-type]
Expand All @@ -276,6 +292,7 @@
[:store {:class "dataStore"}
[:name (str workspace ":" store)]]
[:name new-feature-type]
[:title new-feature-type]
[:nativeName old-feature-type]])])

(defn update-feature-type [workspace store feature-type native-name title abstract
Expand Down
12 changes: 12 additions & 0 deletions src/geosync/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@
(throw (ex-info "gdalsrsinfo failed"
{:data-dir data-dir :file-path file-path :error (.getMessage e)})))))

(defn get-gpkg-native-layer-name
"Returns the first feature layer name found inside a GeoPackage file
as reported by ogrinfo. This may differ from the desired published layer
name, e.g. when the GeoPackage contains a SQL view whose internal name
is a SELECT statement."
[data-dir file-path]
(try
(let [result (with-sh-dir data-dir (sh "ogrinfo" file-path))]
(when-not (s/blank? (:out result))
(second (re-find #"(?m)^\d+: (.+?) \(" (:out result)))))
(catch Exception _ nil)))

(defn extract-path [file-url]
(rest (re-find #"^file://(.+)/([^/]+)$" file-url)))

Expand Down