clojure-demos/resources/public/documents/examples-maps.org

8.1 KiB

Embedding maps in your apps

Introduction

Install npm dependencies

npm install

Run the project npx shadow-cljs server app

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 are blank and render a warning so paste in your google api key to make this work.

Load in the google maps script by adding it to the dom

(defn load-google-maps-script [api-key]
  (let [script (.createElement js/document "script")]
    ;; copied from googles recommended way of loading google maps
    (set! (.-innerHTML script) (str "(g=>{var h,a,k,p=\"The Google Maps JavaScript API\",c=\"google\",l=\"importLibrary\",q=\"__ib__\",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement(\"script\"));e.set(\"libraries\",[...r]+\"\");for(k in g)e.set(k.replace(/[A-Z]/g,t=>\"_\"+t[0].toLowerCase()),g[k]);e.set(\"callback\",c+\".maps.\"+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+\" could not load.\"));a.nonce=m.querySelector(\"script[nonce]\")?.nonce||\"\";m.head.append(a)}));d[l]?console.warn(p+\" only loads once. Ignoring:\",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: \"" api-key "\", v: \"weekly\"});"))
    (.appendChild (.-head js/document) script)))

Create a map using google and core async

In this example we render google maps using core async, async go blocks drop type hints so we need to pull out any hinting to functions, this is a shame as this version reduces the amount of nesting.

(defn google-map-core-async []
  (let [map-element (reagent/atom nil)
        ;; pulled out of the go block to satisfy the infer warnings, the go block removes hints
        get-map-obj (fn [obj] (.-Map ^js/google.maps.Map obj))
        get-marker-obj (fn [obj] (.-Marker ^js/google.maps.Map obj))]
    (load-google-maps-script "")

    ;; go blocks loose hints so may cause infer warnings
    (async/go (let [gMap (get-map-obj (async-in/<p! (js/google.maps.importLibrary "maps"))) 
                    gMarker (get-marker-obj (async-in/<p! (js/google.maps.importLibrary "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")))
      (fn []
        [:div#google-map-async.m-4
         {:style {:width "400px" :height "400px"} :ref #(reset! map-element %)}
         "core async map here"])))

Create a map using google and shadow js-await

(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-4
       {:style {:width "400px" :height "400px"} :ref #(reset! map-element %)}
       "shadow js-await map here"])))

[google-map-js-await]

Create a map using google and promesa

An example using promesa to render a google map.

(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-4
     {:style {:width "400px" :height "400px"} :ref #(reset! map-element %)}
     "promesa map here"])))

[google-map-promesa]