#+TITLE: Embedding maps in your apps #+DESCRIPTION: Example's of embeding maps into your frontend application. #+FILETAGS: clojurescript:frontend:interop * Introduction Below you will find some simple examples of using map api's inside clojure, you can use the inline eval for some of the example where sci supports a specific library. Install npm dependencies ** Setup for downloaded version Install the npm dependencies for react then launch shadow via the terminal or jack in via your IDE. #+BEGIN_SRC html :results silent :exports none :tangle readme.org Install dependencies with. npm install Run the project with the below command. npx shadow-cljs watch app Alternatively jack into the project from your ide. #+END_SRC * Google Maps Below is an example of using google maps, it pulls in the library directly you could alternatively use an npm dependency. You will need to provide your own api key, the examples use no api key and render a warning so paste in your google api key to make this work. #+BEGIN_SRC html :results silent :exports none :tangle resources/public/index.html
js {:center {:lng 131.031 :lat -25.344} :zoom 4}))] (gMarker. (clj->js {:map map :title "test marker 1" :position {:lng 131.031 :lat -24.344}}) "marker 1") (gMarker. (clj->js {:map map :title "test marker 2" :position {:lng 131.031 :lat -25.344}}) "marker 2"))) (fn [] [:div#google-map-async.m-auto {:style {:width "400px" :height "400px"} :ref #(reset! map-element %)} "core async map here"]))) #+END_SRC ** Create a map using google and shadow js-await Shadow CLJS has its one js-await macro we can use in the following fashion, here we just use it to wait for the maps library to have loaded the maps and marker libraries before executing the code. #+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs (defn google-map-js-await [] (let [map-element (reagent/atom nil)] (load-google-maps-script "") (shadow/js-await [js-map (js/google.maps.importLibrary "maps")] (shadow/js-await [js-marker (js/google.maps.importLibrary "marker")] (when @map-element (let [gMap (.-Map ^js/google.maps.Map js-map) gMarker (.-Marker ^js/google.maps.Marker js-marker) map (gMap. @map-element (clj->js {:center {:lng 131.031 :lat -25.344} :zoom 4}))] (gMarker. (clj->js {:map map :title "test marker 1" :position {:lng 131.031 :lat -24.344}}) "marker 1") (gMarker. (clj->js {:map map :title "test marker 2" :position {:lng 131.031 :lat -25.344}}) "marker 2") nil)))) (fn [] [:div#google-map-js-await.m-auto {:style {:width "400px" :height "400px"} :ref #(reset! map-element %)} "shadow js-await map here"]))) [google-map-js-await] #+END_SRC ** Create a map using google and promesa An example using promesa to render a google map. #+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs (defn google-map-promesa [] (let [map-element (reagent/atom nil)] (load-google-maps-script "") (promesa/let [js-map (js/google.maps.importLibrary "maps") js-marker (js/google.maps.importLibrary "marker")] (let [gMap (.-Map ^js/google.maps.Map js-map) gMarker (.-Marker ^js/google.maps.Marker js-marker) map (gMap. @map-element (clj->js {:center {:lng 131.031 :lat -25.344} :zoom 4}))] (gMarker. (clj->js {:map map :title "test" :position {:lng 131.031 :lat -24.344}}) "marker 1") (gMarker. (clj->js {:map map :title "test" :position {:lng 131.031 :lat -25.344}}) "marker 1"))) (fn [] [:div#google-map-promesa.m-auto {:style {:width "400px" :height "400px"} :ref #(reset! map-element %)} "promesa map here"]))) [google-map-promesa] #+END_SRC #+BEGIN_SRC clojure :exports none :tangle src/clojure_demo/core.cljs (defn current-page [] [:div [google-map-core-async] [google-map-promesa] [google-map-js-await]]) (defn mount-root-page [] ;; this select the main node from the html file and injects your page content (.render (createRoot (.getElementById js/document "app")) (reagent/as-element [current-page]))) (def startup! (mount-root-page)) #+END_SRC * LibraMaps Create a dummy function to load the js dynamically, this could be imported in other ways like npm. #+BEGIN_SRC clojurescript :results value :tangle src/clojure_demo/core.cljs (defn load-libra-maps [] (let [script (.createElement js/document "script")] (.setAttribute script "src" "https://unpkg.com/maplibre-gl/dist/maplibre-gl.js") (.appendChild (.-head js/document) script))) (load-libra-maps) #+END_SRC We use =:ref= to grab a reference and call our function when it is created we can then get elements id and construct our map. To customize see https://maplibre.org/maplibre-gl-js/docs/ #+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs (defn libra-map-promesa [] (load-libra-maps) (let [create-map (fn map-element [element] (new js/maplibregl.Map (clj->js {:container (.-id element) #_"libra-map" :style "https://demotiles.maplibre.org/style.json" :zoom 1})))] (fn [] [:div#libra-map.m-auto {:style {:width "400px" :height "400px"} :ref create-map #_(reset! map-element %)} "libra map here"]))) [libra-map-promesa] #+END_SRC