Interaktive Karte mit Leaflet, R und shiny

Bildschirmfoto 2016-03-09 um 17.44.56Mit einem Zusatzpaket kann man Leaflet-Karten direkt in der R-Konsole erstellen. Dabei gibt es viele Anpassungsmöglichkeiten wie beispielsweise individuell angepasste Stecknadelicons und auf HTML basierende popups.

Eine einfach gehaltene Karte ist die folgende, in die ich schon einige der wichtigen Anlaufstellen für ddj-Interessierte im deutschsprachigen Raum eingetragen habe. Der Übersicht wegen sind die sogar in einzelne Gruppen unterteilt, die man aus- und einblenden kann (multilayer).

Aus der lokalen Karte habe ich mit dem RStudio Framework „shiny“ eine shiny-webapp gemacht, die ich hier mit einem gratis Account über shinyapps.io hochgeladen habe. Da ich nichts dafür bezahle kann es sein, dass die Ladezeit der Karte etwas länger ist, außerdem wird sie nur eine gewisse Stundenanzahl angezeigt. Auf dem Handy funktioniert der frame leider sehr schlecht, zumindest bei mir. Ich empfehle allgemein deshalb eher, sich für Grafiken Platz auf einem Server zu kaufen.

Trotzdem: wenn ihr sowas auch mal probieren wollt könnt ihr hier nun lesen, wie ich das gemacht habe. Code-Genies bedenken bitte, dass ich mir alles an R, dass über Histogramme zeichnen und einfache Datenanalysen hinaus geht, selber angeeignet habe. Im Klartext: der Code funktioniert, stellt aber nicht unbedingt den effizientesten Weg zum Ziel dar. Für Anregungen und Tipps bin ich immer dankbar!

Ich empfehle grundsätzlich, das Userinterface „RStudio“ zu nutzen. Gerade in Zusammenarbeit mit shiny hat es gute Features und ist auch ansonsten sehr übersichtlich und praktisch.

Nachtrag: Im folgenden Code erstelle ich in R alle Daten und setze sie zusammen. Verena Haunschmid (@ExpectAPatronum) hat mich soeben richtigerweise darauf aufmerksam gemacht, dass es viel praktischer wäre, die Daten als csv auszulagern und dann einfach einzulesen!

 

Los geht`s

Legt, zum Beispiel auf eurem Desktop, einen ganz normalen Ordner an. Nennt ihn zb „meineshinyapp“. Dann öffnet RStudio.
Als erstes müsst ihr das Paket shiny installieren. Macht das ruhig direkt in der Konsole, der Installationsteil sollte später nicht in dem Kartencode-file dabei sein.

Nun öffnet ein neues RScript in RStudio und fügt folgenden Code ein:

library(leaflet) # das braucht R um zu merken, dass ihr das Paket jetzt auch benutzen wollt

m <- leaflet() %>% # ihr nennt die leaflet Karte "m"
  addProviderTiles("Stamen.TonerBackground")  
m

Für addProviderTiles könnt ihr irgendeine von Leaflet unterstützte Karte wählen. Da ihr das weiter unten im Code eh noch anpasst, ist es an dieser Stelle egal, welche ihr nehmt. Übernehmt einfach erstmal meinen Code.

Jetzt kommt der Teil, den ihr nicht einfach übernehmen könnt. Wenn ihr auch individuelle Icons verwenden wollt, speichert sie als Bilddatei in eurem „meineshinyapp“-Ordner. Ersetzt dann in meinem Code meine Bilddateinamen und Zuordnungsnamen durch eure eigenen. Um zu sehen, wie groß ihr die icons wählen müsst, hilft später nur ein Blick auf die Karte.

# benennt die icons nachvollziehbar!
mapicon <- iconList(
  br = makeIcon(iconUrl = "br.png", 30, 30), 
  srf = makeIcon(iconUrl = "srf.jpg", 40, 40),
  mopo = makeIcon(iconUrl = "mopo.jpg", 30, 30),
  zeit = makeIcon(iconUrl = "zeit.png", 60, 60),
  journo = makeIcon(iconUrl = "journo.png", 30, 30),
  spon = makeIcon(iconUrl = "spon.jpg", 30, 30), 
  ok = makeIcon(iconUrl= "ok.png", 15, 15),
  tu = makeIcon(iconUrl= "tu.png", 20, 20),
  nzz = makeIcon(iconUrl= "nzz.jpg", 20, 20)
# die Zahlen bestimmen die Größe des icons
)

Als nächstes kümmern wir uns um die Geodaten und die popups, getrennt nach Gruppen (w wird später zu Redaktionen, x zu Meetups etc.):

## cbind setzt später jeweils die erste Zahl des ersten Vektors mit der zweiten Zahl des zweiten 
## Vektors etc. zusammen
## die 8.560453 ist also long1, die 47.417563 lat1 und beide gehören zu "srf" und dem ersten popup

w <- sp::SpatialPointsDataFrame(
  cbind(  
    c(8.560453, 11.554090, 13.398040, 13.381030, 10.004017, 8.547561), #long
    c(47.417563, 48.144240, 52.507620, 52.505190, 53.545762, 47.364939) #lat
  ),

# sorgfältig bei der Angabe der Geodaten auf die Reihenfolge achten, damit nachher alles richtig
# zugewiesen wird
 
 data.frame(name = factor(
    c("srf", "br", "mopo", "zeit", "spon", "nzz") 
  ),
  popup = c("<a href='http://www.srf.ch/news/srf-data'>SRF Data</a> <br> Zürich", 
            "<a href='http://www.br.de/extra/br-data/index.html'>BR Data</a> <br> München",
            "<a href='http://www.morgenpost.de/interaktiv/'>MoPo Interaktiv-Team</a> <br> Berlin",
            "<a href='http://www.zeit.de/datenjournalismus'>Zeit Online Datenjournalismus</a> <br> Berlin",
            "<a href='http://www.spiegel.de/thema/daten/'>Spiegel Online Datenlese</a> <br> Hamburg",
            "<a href='http://www.nzz.ch/data/'>NZZ Data</a> <br> Zürich"
            )
  )  

## popup Aufbau: "<a href='hyperlink'>erste Zeile</a> <br> zweite Zeile"

)
x <- sp::SpatialPointsDataFrame(
  cbind(  
    c(7.417445, 9.993682, 13.382337, 11.629084, 7.630538, 6.748874, 6.638324, 6.773096, 
      7.144582, 6.900711, 7.094403, 8.690906, 9.479753, 11.576037, 12.379037, 8.681832, 
      13.747688, 12.940579, 11.011690, 9.227360, 9.173458, 9.954148, 8.405627, 11.568393,
      7.850041), #long
    c(51.494049, 53.551085, 52.498534, 52.119520, 51.964971, 51.569299, 51.481628, 
      51.227698, 51.266420, 50.960162, 50.737410, 50.587295, 51.312384, 50.922167, 
      51.331725, 50.116395, 51.046866, 50.829905, 49.590018, 49.134316, 48.776427, 
      48.424874, 49.006257, 48.144718, 47.983412) #lat
  ),
  data.frame(namex = factor(   

## achtung! hier heißen die Namen "namex", nicht mehr "name"! Das ist später für die Zuordnung wichtig

    c("journo", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok"
      , "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok", "ok") 

## für die OK Labs verwende ich immer das "ok"-Icon. Diesen Vektor könnte ich mit
## rep() vermutlich verkürzen, das macht bei diesem noch recht kleinen Datensatz
## jetzt aber nichts

  ),
  popup = c("<a href='http://journocode.com/'>journocode</a> <br> Dortmund",
            "<a href='http://codefor.de/hamburg/'>OK Lab</a> <br> Hamburg",
            "<a href='http://codefor.de/berlin/'>OK Lab</a> <br> Berlin",
            "<a href='http://codefor.de/magdeburg/'>OK Lab</a> <br> Magdeburg",
            "<a href='http://codefor.de/muenster/'>OK Lab</a> <br> Münster",
            "<a href='http://codefor.de/ruhrgebiet/'>OK Lab</a> <br> Ruhrgebiet",
            "<a href='http://codefor.de/niederrhein/'>OK Lab</a> <br> Niederrhein",
            "<a href='http://codefor.de/duesseldorf/'>OK Lab</a> <br> Düsseldorf",
            "<a href='http://codefor.de/wuppertal/'>OK Lab</a> <br> Wuppertal",
            "<a href='http://codefor.de/koeln/'>OK Lab</a> <br> Köln",
            "<a href='http://codefor.de/bonn/'>OK Lab</a> <br> Bonn",
            "<a href='http://codefor.de/giessen/'>OK Lab</a> <br> Gießen",
            "<a href='http://codefor.de/kassel/'>OK Lab</a> <br> Kassel",
            "<a href='http://codefor.de/jena/'>OK Lab</a> <br> Jena",
            "<a href='http://codefor.de/leipzig/'>OK Lab</a> <br> Leipzig",
            "<a href='http://codefor.de/frankfurt/'>OK Lab</a> <br> Frankfurt",
            "<a href='http://codefor.de/dresden/'>OK Lab</a> <br> Dresden",
            "<a href='http://codefor.de/chemnitz/'>OK Lab</a> <br> Chemnitz",
            "<a href='http://codefor.de/erlangen/'>OK Lab</a> <br> Erlangen",
            "<a href='http://codefor.de/heilbronn/'>OK Lab</a> <br> Heilbronn",
            "<a href='http://codefor.de/stuttgart/'>OK Lab</a> <br> Stuttgart",
            "<a href='http://codefor.de/ulm/'>OK Lab</a> <br> Ulm",
            "<a href='http://codefor.de/karlsruhe/'>OK Lab</a> <br> Karlsruhe",
            "<a href='http://codefor.de/muenchen/'>OK Lab</a> <br> München",
            "<a href='http://codefor.de/freiburg/'>OK Lab</a> <br> Freiburg"
  )
  )
)

## und als letztes noch meine Uni:

y <- sp::SpatialPointsDataFrame(
  cbind(  
    c(7.417798), #long
    c(51.494564) #lat
  ),
  data.frame(namey = factor(
    c("tu")
  ),
  popup = c("<a href='http://www.wissenschaftsjournalismus.org/ba-zweitfach-datenanalyse.html'>Studiengang 
            ddj</a> <br> Dortmund"
  )
  )
)

So! Nun noch alles zusammen in die Leaflet-Karte packen! Statt „Esri.WorldGrayCanvas“ könnt ihr zum Beispiel diese Kartendesigns wählen.

n <- leaflet() %>% 
  addProviderTiles("Esri.WorldGrayCanvas")%>% 
  addMarkers(data=w, icon = ~mapicon[name], popup= ~as.character(popup), group="ddj-Redaktionen")%>%  

# Marker hinzufügen: mit den Daten, die wir "w" genannt haben, den icons aus dem Vektor "mapicons" 
# zu dem gleich heißenden "name"-Eintrag gehören. Deshalb muss das icon für BR Data genau so benannt werden wie 
# der "name"-Eintrag der passenden Geo- und Popupdaten! Inhalt der popups soll der Text aus der 
# passenden Zeile des popup-Vektors sein. Und auch die Gruppe benennen wir hier. 

addMarkers(data=x, icon = ~mapicon[namex], popup= ~as.character(popup), group="Meetups")%>% 
addMarkers(data=y, icon = ~mapicon[namey], popup= ~as.character(popup), group="ddj-Ausbildung")%>% 

# hier entsteht das Kontrollfeld für die Gruppenauswahl 
addLayersControl( 
overlayGroups = c("ddj-Redaktionen", "Meetups", "ddj-Ausbildung"), 
options = layersControlOptions(collapsed = FALSE) ) 
print(n) # Tadaaa! Eure Karte!

Und eine shiny-webapp macht ihr so daraus:

library(shiny)

app <- shinyApp(
  ui <- fluidPage(leafletOutput('myMap', width = "100%", height = 650)),
  server <- function(input, output) {
    map <- n
    output$myMap <- renderLeaflet(map)
  }
)


shinyApp(ui = ui, server = server)

Um das alles später über shinyapps.io hochzuladen solltet ihr das gesamte Skript nun als app.R abspeichern und zusammen mit allen icon-Bildern in den „meineshinyapp“-Ordner packen!

Die Karte ist fertig, wenn auch durchaus ausbaufähig. Beispielsweise ist die Überschneidung von Markern der selben Gruppe sehr unpraktisch, allerdings liegt das wohl auch ein bisschen an den von mir gewählten icons. Außerdem kann man sicher noch mehr am Kartendesign anpassen, beispielsweise den deutschsprachigen Raum hervorheben etc.
Wie das deploying mit shinyapps.io funktioniert erkläre ich im nächsten post! Jetzt erst einmal fröhliches ausprobieren!
Wenn ihr weitere coole Möglichkeiten entdeckt oder Anregungen habt, gebt mir gerne bescheid.
Weitere Infos über die Verknüpfung von Leaflet und R hier.

Facebooktwitter

1 comment for “Interaktive Karte mit Leaflet, R und shiny

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *