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

8.6 KiB

Embedding maps in your apps

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.

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

Code pulled from google

(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]