Skip to content

Commit

Permalink
Testing out some ideas for modern Openlayers interop
Browse files Browse the repository at this point in the history
  • Loading branch information
Deraen committed Oct 22, 2024
1 parent 81bc5a8 commit d39d970
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 11 deletions.
106 changes: 100 additions & 6 deletions webapp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@hello-pangea/dnd": "^16.6.0",
"@mapbox/togeojson": "^0.16.0",
"@mapbox/togeojson": "^0.16.2",
"@mui/material": "^5.15.19",
"@turf/area": "^6.5.0",
"@turf/bbox": "^6.0.1",
Expand All @@ -37,8 +37,9 @@
"highlight.js": "^11.7.0",
"mdi-material-ui": "7.9.1",
"ol": "^7.5.2",
"ol-ext": "^4.0.4",
"proj4": "^2.5.0",
"ol-new": "npm:ol@^10.2.1",
"ol-ext": "^4.0.24",
"proj4": "^2.12.1",
"rc-slider": "^9.7.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
8 changes: 6 additions & 2 deletions webapp/src/cljs/lipas/ui/map/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
[lipas.ui.loi.views :as loi]
[lipas.ui.map.events :as events]
[lipas.ui.map.import :as import]
[lipas.ui.map2.map :as map2]
[lipas.ui.map.map :as ol-map]
[lipas.ui.map.subs :as subs]
[lipas.ui.mui :as mui]
Expand Down Expand Up @@ -1926,8 +1927,11 @@
{:popup-ref popup-ref}]

;; The map
[ol-map/map-outer
{:popup-ref popup-ref}]]))
; [ol-map/map-outer
; {:popup-ref popup-ref}]

($ map2/map-view)
]))

(defn main []
[:f> map-view])
153 changes: 153 additions & 0 deletions webapp/src/cljs/lipas/ui/map2/map.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
(ns lipas.ui.map2.map
(:require ["@mui/material/Stack$default" :as Stack]
["ol-new" :as ol]
["ol-new/extent" :as extent]
["ol-new/layer/Tile$default" :as TileLayer]
["ol-new/proj" :as proj]
["ol-new/proj/proj4" :refer [register]]
["ol-new/source/WMTS$default" :as WMTSSource]
["ol-new/tilegrid/WMTS$default" :as WMTSTileGrid]
["proj4" :as proj4]
[lipas.ui.map.subs :as subs]
[lipas.ui.map2.subs :as subs2]
[lipas.ui.uix.hooks :refer [use-subscribe]]
[uix.core :as uix :refer [$ defui]]))

(def mml-resolutions
#js [8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.25])

(def mml-matrix-ids (clj->js (range (count mml-resolutions))))

(defn ->wmts-url [layer-name]
(str "/mapproxy/wmts/"
layer-name
"/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png"))

(def urls
{:taustakartta (->wmts-url "mml_taustakartta")
:maastokartta (->wmts-url "mml_maastokartta")
:ortokuva (->wmts-url "mml_ortokuva")
:kiinteisto (->wmts-url "mml_kiinteisto")
:kiinteistotunnukset (->wmts-url "mml_kiinteistotunnukset")
:kuntarajat (->wmts-url "mml_kuntarajat")})

(def epsg3067-extent #js [-548576.0 6291456.0 1548576.0 8388608.0])

;; initialize proj
(def epsg3067-defs
(str "+proj=utm"
"+zone=35"
"+ellps=GRS80"
"+towgs84=0,0,0,0,0,0,0"
"+units=m"
"+no_defs"))

(defn init! []
(proj4/defs "EPSG:3067" epsg3067-defs)
(register proj4)

(let [proj (proj/get "EPSG:3067")]
(.setExtent proj epsg3067-extent)
{:proj4 proj4 :epsg3067 proj}))

(def proj (init!))

(def ^js epsg3067 (:epsg3067 proj))
(def epsg3067-top-left (extent/getTopLeft (.getExtent epsg3067)))


(def MapContext (uix/create-context))
(def MapContextProvider (.-Provider MapContext))

(defui WmtsLayer
[{:keys [url layer-name visible? base-layer? min-res max-res
resolutions matrix-ids]
:or {visible? false
base-layer? true
max-res 8192
min-res 0.25
resolutions mml-resolutions
matrix-ids mml-matrix-ids}}]
(let [layer-ref (uix/use-ref)
{:keys [ol-ref]} (uix/use-context MapContext)]
;; TODO: Same comments about not using use-effect for the initial initialization as in map component
(uix/use-effect (fn []
(let [layer (TileLayer. #js {:visible visible?
:opacity 1.0
:minResolution min-res
:maxResolution max-res
:source (WMTSSource. #js {:url url
:layer layer-name
:projection "EPSG:3067"
:matrixSet "mml_grid"
:tileGrid (WMTSTileGrid. #js {:origin epsg3067-top-left
:extent epsg3067-extent
:resolutions resolutions
:matrixIds matrix-ids})
:format "png"
:requestEncoding "REST"
:isBaseLayer base-layer?})})
ol (.-current ol-ref)]
;; (js/console.log "init layer" ol-ref ol)
(set! (.-current layer-ref) layer)
(.addLayer ol layer)
(fn []
(.removeLayer ol layer))))
^:lint/disable
[])
nil))

(defui map-container [{:keys [center zoom]}]
(let [map-el-ref (uix/use-ref)
ol-ref (uix/use-ref)
view-ref (uix/use-ref)
ctx (uix/use-memo (fn []
{:map-el-ref map-el-ref
:ol-ref ol-ref})
[])]
;; FIXME: This isn't really correct, but
;; regular effect would run AFTER child component effects.
;; Need to consider better way for these interop effects.
(uix/use-layout-effect (fn []
(let [view (ol/View. #js {:center #js [(:lon center) (:lat center)]
:extent epsg3067-extent
:showFullExtent true
:constrainOnlyCenter true
:zoom zoom
:projection "EPSG:3067"
:resolutions mml-resolutions
:units "m"
:enableRotation false})
opts #js {:target (.-current map-el-ref)
:layers #js []
:controls #js []
:overlays #js []
:view view}
ol (ol/Map. opts)]
;; (js/console.log "init ol" ol)
(set! (.-current ol-ref) ol)))
;; FIXME: This effect just handles the initial setup,
;; handling the property changes is outside of this effect.
;; Maybe this shouldn't be an effect at all?
^:lint/disable
[])
($ MapContextProvider
{:value ctx}
($ Stack
{:ref map-el-ref
:sx #js {:flex 1}
:tabIndex -1})
($ WmtsLayer
{:url (:taustakartta urls)
:layer-name "MML-Taustakartta"
:visible? true}))))

(defui map-view []
;; Subscribe to re-frame data here, then just pass to the pure components?
(let [geoms (use-subscribe [::subs2/geometries])
center (use-subscribe [::subs/center])
zoom (use-subscribe [::subs/zoom])]
(js/console.log geoms)
($ map-container
{:center center
:zoom zoom})))
47 changes: 47 additions & 0 deletions webapp/src/cljs/lipas/ui/map2/subs.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
(ns lipas.ui.map2.subs
(:require [cljs-bean.core :refer [->clj ->js]]
[re-frame.core :as rf]))

;; TODO: Fully coercing the search results into GeoJSON features might not be needed.
;; We might just render each result as React component, and let them add (and remove) themselves to the Vector Source.
(rf/reg-sub ::geometries
;; NOTE: This is JSON.parse JS result from the ajax call
:<- [:lipas.ui.search.subs/search-results-fast]
(fn [results _]
(let [results (->clj results)
;; TODO:
lipas-id' nil]
(->> results
:hits
:hits
(keep
(fn [obj]
(let [obj (:_source obj)
;; Hmm, consider cljs-bean here? Should be nearly as fast
geoms (or
;; Full geoms
(-> obj :location :geometries :features)
;; Simplified geoms
(-> obj :search-meta :location :simple-geoms :features))
type-code (-> obj :type :type-code)
lipas-id (:lipas-id obj)
name (:name obj)
status (:status obj)
travel-direction (:travel-direction obj)]

;; To avoid displaying duplicates when editing
(when-not (= lipas-id' lipas-id)
#js {:type "FeatureCollection"
:features (->> geoms
(map-indexed (fn [idx geom]
(->js (assoc geom
:id (str lipas-id "-" idx)
:properties #js {:lipas-id lipas-id
:name name
:type-code type-code
:status status
:travel-direction travel-direction}))))
into-array)}))))
not-empty))))


0 comments on commit d39d970

Please sign in to comment.