Skip navigation
All Places > GeoDev Germany > Blog > 2017 > August
2017

Anfang 2017 wurde vom Zentrum Digitalisierung Bayern der Wettbewerb “Mobility Innovation Competition @ Campus”(MICC) ausgeschrieben.  Dieser Wettbewerb richtete sich an Studenten bayerischer Hochschulen und Universitäten. Im Rahmen der Teilnahme sollten sie sich mit der zukünftigen Mobilität als Thematik auseinandersetzen und eine Produktidee entwickeln, die im Rahmen eines Startups umgesetzt werden könnten.

 

Die Competition war in drei Stufen aufgebaut. Zuerst musste ein Proposal eingereicht werden, welches die Projektidee, gewählte Methoden und einen Businessplan beschreibt. Aus den Teams, welche ein Proposal eingereicht hatten, wurden circa zwanzig zu einer Veranstaltung Ende April nach Nürnberg eingeladen. Auf dieser hatte jedes Team drei Minuten Zeit zum pitchen der eigenen Idee. Insgesamt 15 Teams wurden am Ende für die Projektphase zugelassen. Danach hatten sie drei Monate Zeit um erste Teile der Ideen umzusetzen. Auf einer Abschlussveranstaltung im Juli in München wurden die Projekte präsentiert und die Sieger prämiert.

 

 

Linus Lambrecht und ich, Simon Geigenberger, studieren beide im Master Geoinformatik an der Universität Augsburg. Wir wurden auf die Ausschreibung aufmerksam gemacht und waren sofort dazu entschlossen teilzunehmen. Bei der Suche nach einem passenden Thema stießen wir schnell auf das Problem der hohen Feinstaubwerte in deutschen Innenstädten und waren uns einig, dass ein Fahrverbot von Fahrzeugen mit Brennstoffmotoren keine adäquate Lösung darstellt, sondern dass es eine Möglichkeit geben muss dynamisch und nach Bedarf Maßnahmen ergreifen zu können. Unsere Idee war es, eine Plattform namens „Clean Routing“ zu entwickeln. Diese richtet sich speziell an Großstädte mit Feinstaubproblemen. Drei Bausteine stellen die zentralen Elemente dar.

 

 

Primärer und wichtigster Bestandteil ist eine Anwendung für die Stadtverwaltungen, welche eine Vorhersage für Feinstaubwerte darstellt. Die Vorhersagewerte werden unter Berücksichtigung der Wetterdaten mit Hilfe eines Modells berechnet, welches in einem Python Skript implementiert wurde. Diese Berechnung wird dauerhaft durch ein Maschinelles Lernverfahren optimiert. Das aktuelle Modell liefert für Städte, welche sich in unseren Breiten befinden eine Genauigkeit von über 90 Prozent. Die Datensätze, welche zum Trainieren des Modells verwendet wurden, sind aus den Frühlingsmonaten dieses Jahres. Da es noch keine Daten aus Wintermonaten gibt, kann das Modell erst nach dem Winter für diese Monate angepasst werden. Dies ist nötig, weil sich die Feinstaubwerte bei kalten Temperaturen und Schneefall anders entwickeln als in den restlichen Jahreszeiten. Wenn eine Überschreitung des, von der EU festgelegten, Wert für einen Feinstaubalarm vorhergesagt wird, dann hat die Stadt nun früh genug die Möglichkeit Maßnahmen zu ergreifen um den Schwellwert nicht zu übertreffen. Maßnahmen wären beispielsweise, dass gefährdete Gebiete umfahren werden sollen.  

 

Es soll eine weitere Anwendung für Privatpersonen entstehen. Diese App berechnet basierend auf den Feinstaubwerten alternative Routen, die gefährdete Gebiete umfährt oder direkt auf Verkehrsmittel auf dem ÖPNV umsteigt. Da man die Privatperson nicht mit Verboten dazu zwingen will den Öffentlichen Nahverkehr zu nutzen, kann ihm dies mit verschiedenen Angeboten schmackhaft gemacht werden. Diese Angebote wiederum müssen von Seiten der Stadtverwaltung gewählt werden und eine anziehende Wirkung auf die Privatpersonen haben. Beispiele hierfür sind reduzierte Preise für ÖPNV Tickets oder die Möglichkeit einen Park + Ride Parkplatz zu reservieren. Des Weiteren besteht für den Nutzer die Möglichkeit seine Fahrten zu planen und in diese Planung bereits die vorhergesagten Feinstaubwerte und daraus resultierende Überschreitungen von Grenzwerten miteinzubeziehen.

 

Dieser Planungsservice kann auch in einer dritten Anwendung für Unternehmen, wie Handwerksbetriebe oder Speditionen, integriert werden. Diese Unternehmen wissen bereits im Vorhinein ihre Routen und können diese so in den Arbeitsplan einpassen, dass Gebiete in den Zeiten vermieden werden, wenn sich ein Feinstaubalarm anbahnt. Bei den Maßnahmen der Stadt handelt es sich um keine strikten Verbote, sondern lediglich um Vorschläge und Richtlinien. Daher ist es notwendig, dass es neben den bereits genannten Angebote für Privatpersonen weitere Anreize gibt, welche die Verkehrsteilnehmer dazu bewegen das alternative Routing wahrzunehmen.

 

 

Originale Route mit Simulierten Feinstaubwerten

 

Alternative Route (grün) und Route mit ÖPNV (blau)

 

 

 

Bisher wurden die technischen Bausteine für die Vorhersage der Feinstaubwerten und das multimodale Routing implementiert. Somit fehlt noch die Einbindung der verschiedenen Komponenten in eine Benutzeroberfläche um mit den verschiedenen Anwendungen in eine Testphase zu gehen. Da es sich um Skripte, zentrale Datenhaltung und darauf basierenden Anwendungen mit Routingoption handelt bietet sich ArcGIS Online hervorragend als Plattform an, um die Bestandteile zusammenzuführen und technisch Umzusetzen. Ob und wie wir das Produkt weitergestalten ist noch offen, da wir uns beide im Endspurt unseres Studiums befinden. Dennoch ist die Problematik der hohen Feinstaubwerten in deutschen Innenstädten noch zu lösen. Wir sehen unser Produkt als eine optimale Lösung für diese Thematik, da keine Verkehrsteilnehmer benachteiligt werden und dennoch die Zahl der Fahrzeuge mit Brennstoffantrieb in Städten reduziert werden könnte.

 

Leider konnten wir uns mit dem Projekt nicht in den preisgekrönten Rängen positionieren. Trotzdem blicken wir positiv auf die Teilnahme am Wettbewerb zurück. Diese war eine sehr interessante Erfahrung, denn es wurden sehr viele Disziplinen gefordert und es konnte alles im Studium erlernte zusammengeführt werden. Es war sehr spannend und lehrreich für uns aus einer Idee den Weg zu einem Produkt und den dazugehörigen Businessplan zu entwickeln.

 

Bei weiteren Fragen zu unserem Projekt stehen wir gerne zur Verfügung:

 

Simon Geigenberger: simon@geigenberger.info

Linus Lambrecht: linus.lambrecht@gmx.de

StreetMap Premium for ArcGIS Runtime bietet hochwertige, weltweite Straßendaten, die für die Verwendung bei der kartographischen Kartendarstellung, Geokodierung und dem Routing optimiert wurden. Diese Daten können in Apps eingebunden werden, die mit den ArcGIS Runtime SDKs ab Version 100.0 entwickelt wurden. Sie werden lokal auf dem Gerät des Nutzers gespeichert – dass ermöglicht die Nutzung unterwegs und ohne Internetverbindung. Die Daten werden in regelmäßigen Abständen aktualisiert, so dass sie stets auf dem neuesten Stand sind und somit der Fokus der Entwicklungsarbeit ausschließlich auf der Erstellung und Pflege der eigentlichen Apps liegt.

 

 

Folgende Vorteile bietet StreetMap Premium for ArcGIS Runtime:

 

  • Detaillierte Straßengrundkarte
    Die mit StreetMap Premium for ArcGIS Runtime bereitgestellten Daten wurden bereits aufbereitet und sind als sofort einsetzbare Straßengrundkarte verfügbar. Diese hochwertige Grundkarte bietet in den Apps den nötigen räumlichen Kontext.
  • Suchen und Geokodieren von Positionen
    StreetMap Premium for ArcGIS Runtime umfasst einen Locator-Task zur Geokodierung, der sich ganz einfach in Apps einbinden lässt. Mit diesem Locator können Nutzer nach spezifischen Adressen, Kreuzungen oder Points of Interest suchen und diese auf einer Karte darstellen.
  • Lösen von Routen-Problemen
    StreetMap Premium for ArcGIS Runtime bietet auch ein Netzwerk-Dataset für Verkehrsnetze für umfassende Routing-Analysefunktionen in ArcGIS Runtime-Apps. Diese Daten ermöglichen das Ermitteln der optimalen Routen zwischen verschiedenen Orten im Straßennetzwerk.
  • Offline-Verwendung von Straßendaten
    Die in StreetMap Premium for ArcGIS Runtime verfügbaren Daten sind auch dann verfügbar, wenn keine Verbindung zum Internet besteht. So können Nutzer auch im Außendienst oder Bereichen mit schlechter oder keiner Internetverbindung bequem auf die Grundkarten, Geokodierungsfunktionen und Routing-Funktionalität zugreifen. Ein weiterer Vorteil ist die reduzierte Datenmenge, die übertragen werden muss – dies spart Kosten.
  • Apps für den Einsatz in der ganzen Welt
    Die Daten und Karten von StreetMap Premium for ArcGIS Runtime sind für die ganze Welt verfügbar. Mobile Kartenpakete für unterschiedlichste Gebiete, z. B. Regionen oder Länder, lassen sich damit in Apps einbinden.

 

 

Download und Lizenzierung

ArcGIS Entwickler können mit StreetMap Premium for ArcGIS Runtime ab der kostenlosen ArcGIS Developer Subscription „Essentials“ entwickeln und testen. Die Daten können in Form von Mobile Map Packages (.mmpk) für unterschiedliche Länder und Regionen im Downloadbereich des ArcGIS Developers Portals heruntergeladen werden:

 

 

Für das Deployment der fertigen Apps muss StreetMap Premium for ArcGIS Runtime als Extension zu ArcGIS Runtime lizenziert werden.  Der oder die Lizenzschlüssel (lizenziert wird per Region) werden dazu auch im Code wie eine Extension implementiert. Mehr Informationen zur Vorgehensweise hier am Beispiel des ArcGIS Runtime SDK for .NET.

 

Implementierung

Das Implementieren und Nutzen von StreetMap Premium for ArcGIS Runtime ist intuitiv und in allen ArcGIS Runtime SDKs fast identisch:

 

Öffnen des Mobile Map Packages:

var mapPackage = await MobileMapPackage.OpenAsync(Path to mmpk);

 

Nutzen der fertigen Map(s):

var map = mapPackage.Maps[1];
Map = map;

 

Nutzen des Locator Tasks für Geocoding/Reverse Geocoding usw.:

await mapPackage.LocatorTask.LoadAsync();
var suggestions = await mapPackage.LocatorTask.SuggestAsync("Fechnerstrasse 8, Leipzig");

 

Nutzen des Transportation Networks für Routing:

var routeTask = await RouteTask.CreateAsync(map.TransportationNetworks[0]);

 

 

Mit dem ArcGIS Runtime SDK for .Net sieht es so aus (der Einfachheit halber alles in einer Methode):

private async void CreateMmpkMap()
{
    GraphicsOverlays[0].Graphics.Clear();
    try
    {
        var mapPackage = await MobileMapPackage.OpenAsync(_baseDirectory.LocalPath + @"geodata\Germany.mmpk");
        var map = mapPackage.Maps[1];

        // Locator, just a test
        await mapPackage.LocatorTask.LoadAsync();
        var suggestions = await mapPackage.LocatorTask.SuggestAsync("Fechnerstrasse 8, Leipzig");
        //do something with the suggestions

        await map.LoadAsync();

        //make sure the map loaded
        if (map.LoadStatus != LoadStatus.Loaded) return;

        // Routing
        var routeTask = await RouteTask.CreateAsync(map.TransportationNetworks[0]);

        // get the default route parameters
        var routeParams = await routeTask.CreateDefaultParametersAsync();
        // explicitly set values for some params
        routeParams.ReturnDirections = false;
        routeParams.ReturnRoutes = true;
        routeParams.OutputSpatialReference = map.SpatialReference;


        // create a Stop for my location
        var myLocation = new MapPoint(1375470.8306, 6685056.1967, map.SpatialReference);
        var stop1 = new Stop(myLocation);

        var markerSym = new SimpleMarkerSymbol
        {
            Style = SimpleMarkerSymbolStyle.Circle,
            Color = Colors.Red,
            Size = 12
        };
        var pointGraphic = new Graphic(myLocation, markerSym);
        _graphicsOverlays[0].Graphics.Add(pointGraphic);

        // create a Stop for your location
        var yourLocation = new MapPoint(1377231.148, 6687515.5736, map.SpatialReference);
        var stop2 = new Stop(yourLocation);

        pointGraphic = new Graphic(yourLocation, markerSym);
        _graphicsOverlays[0].Graphics.Add(pointGraphic);

        // assign the stops to the route parameters
        var stopPoints = new List<Stop> { stop1, stop2 };
        routeParams.SetStops(stopPoints);

        var routeResult = await routeTask.SolveRouteAsync(routeParams);

        // get the route from the results
        var route = routeResult.Routes[0];

        // create a graphic (with a dashed line symbol) to represent the route
        var routeSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle.DashDotDot, Colors.Red, 5);
        var routeGraphic = new Graphic(route.RouteGeometry, routeSymbol);

        // add the route graphic to the map view and zoom to its extent
        _graphicsOverlays[0].Graphics.Add(routeGraphic);

        map.InitialViewpoint = new Viewpoint(route.RouteGeometry, 0);

        Map = map;

    }
    catch (Exception ex)
    {
        var text = ex.Message;
    }
}

 

Ergebnis

ArcGIS Runtime ist eine Familie aus nativen SDKs zur Entwicklung von Geo-Apps für alle populären Mobile und Desktop Plattformen. Für Desktops gibt es mit dem Runtime Local Server ein zusätzliches SDK zur Implementierung von erweiterter Geo-Funktionalität. Mit diesen wollen wir uns in dem Blog näher beschäftigen:

 

 

Was ist der ArcGIS Runtime Local Server?

Wie der Name schon andeutet ist der Runtime Local Server ein lokal auf dem Gerät laufender Web GIS Server. Dieser wird aber nicht installiert, sondern zur Laufzeit der ArcGIS Runtime Anwendung als Prozess erzeugt und verwaltet. Wird die Anwendung beendet, beendet sich damit auch der Local Server. Pro ausgeführte Anwendung kann nur ein Local Server gestartet werden (Singleton Pattern), es können aber mehrere Anwendungen mit jeweils eigenem Local Server nebeneinander laufen.

 

 

Die lokalen GIS Services für den Local Server werden auch zur Laufzeit erzeugt und verwaltet. Diese basieren auf lokalen Datenpaketen, welche zuvor mit ArcGIS Desktop erzeugt werden müssen. Genau wie in ArcGIS Online und ArcGIS Enterprise werden diese GIS Services über die ArcGIS Server REST API, also über URLs angesprochen. In ArcGIS Runtime ist es somit egal aus welcher Quelle ein GIS Service stammt, es können die gleichen Tasks und Layer auch dafür genutzt werden.

 

Der Schwerpunkt des Runtime Local Server liegt ganz klar auf dem lokalen Geoprocessing. Hunderte Analyse Tools aus der ArcToolbox, mächtige Model Builder Analyse Modelle und auch eigene Python Skripte in ArcGIS Desktop können zu Geoprocessing Packages gekapselt und diese durch lokale Geoprocessing Services genutzt werden. Das ist der Schlüssel für hochspezialisierten ArcGIS Runtime Anwendungen auch für GIS-Experten.

Es können noch weitere Typen lokaler Services erzeugt werden, dazu später mehr.

 

 

Unterstütze Plattformen 

Folgende ArcGIS Runtime SDKs unterstützen den Runtime Local Server:

ArcGIS Runtime SDK for

Local Server Unterstützung für:

Anmerkungen

.NET

·         Windows (32/64 Bit)

Es wird nur die WPF API unterstützt.

Java

·         Windows (32/64 Bit)

·         Linux (64 Bit)

MacOS wird nicht unterstützt.

Qt

·         Windows (32/64 Bit)

·         Linux (64 Bit)

Es wird nur die Qt/C++ API unterstützt.

 

Weitere mögliche Zielplattformen der SDKs in der Tabelle und auch die ArcGIS Runtime SDKs for Android, iOS und macOS werden nicht unterstützt. Dies lässt sich auch nicht mit Cross-Platform Technologie (z.B. Xamarin oder QML API) umgehen.

 

 

Download und Lizensierung

ArcGIS Entwickler können mit dem Local Server ab der kostenlosen ArcGIS Developer Subscription „Essentials“ entwickeln und testen. Das ArcGIS Runtime Local Server SDK gibt es für Windows und Linux und wird im Downloadbereich des ArcGIS Developers Portals heruntergeladen.

 

 

Die Lizenzstufe einer fertigen Anwendung mit Runtime Local Server Technologie hängt von der implementierten Funktionalität ab. Die Mindeststufe ist dabei ArcGIS Runtime License Level Standard. Für manche Funktionen wird zusätzlich noch die Analysis Extension benötigt. Für Geoprocessing Tools werden die benötigten Lizenzstufenstufen hier aufgeschlüsselt.

 

 

Datenquellen und Funktionalität 

Map- und Geoprocessing Packages können im Moment nur in ArcMap erzeugt werden. Dabei ist folgendes zu beachten:

 

 

Folgendes ist möglich (Auszug):

Quelle

Lokaler Service

Funktionen (Auszug)

Map package (.mpk)

MapService/

FeatureService

  • Dynamische Daten
  • Rasterdaten
  • Features & Tabellen
  • Abfragen, Editierung
  • Dynamic Layers (Hier grundlegende Infos des Konzeptes)
  • Zugriff auf (ArcSDE) Datenbanken
  • uvm

Geoprocessing Package (.gpk)

Geoprocessing Service

https://developers.arcgis.com/net/latest/wpf/guide/local-server-geoprocessing-tools-support.htm

 

 

Implementierung

In der Dokumentation der ArcGIS Runtime SDKs for .NET/WPF, Java und Qt/C++ steht die Implementierung des ArcGIS Runtime Local Server ausführlich beschrieben. Hier die Kurzfassung:

 

  1. Download und Installation
  2. Erstellen eines Local Server Deployments für das Projekt (Welche Funktionalität soll genutzt werden!)
    1. Local Server Deployment Builder tool (Java und Qt)
    2. Deployment manifest file im Visual Studio Projekt (.NET)
  3. Im Code:
    1. Starten des Runtime Local Server
    2. Erzeugen und Starten von lokalen Services auf Basis von Packages
    3. Nutzen der lokalen Services in den entsprechenden ArcGIS Runtime Tasks und Layer

 

Mit dem ArcGIS Runtime SDK for .Net am Beispiel eines einfachen Buffer Tools (Download) sieht es so aus:

 

(a) Starten des Runtime Local Server

private LocalServer _localServer;
private LocalGeoprocessingService _localGpService;

private async void StartLocalServer()
{
    try
    {
        // Get the singleton LocalServer object using the static "Instance" property
        _localServer = LocalServer.Instance;
        _localServer.AppDataPath = @"D:\Temp\LocalServerData";

        // Handle the StatusChanged event to react when the server is started
        _localServer.StatusChanged += (sender, statusChangedEventArgs) =>
        {
            // Check if the server started successfully
            if (statusChangedEventArgs.Status == LocalServerStatus.Started)
            {
                // Start specific local services here ...
                InitializeGpService();
            }
        };
        // Start the local server instance
        await _localServer.StartAsync();
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
}

 

(b) Erzeugen und Starten eines lokalen Services auf Basis eines Geoprocessing Packages (Download) 

private async void InitializeGpService()
{
    try
    {
        // Create a local GP service from a geoprocessing package on disk
        _localGpService = new LocalGeoprocessingService(@"data\simple-buffer.gpk");

        // Handle the status changed event to check when it's loaded
        _localGpService.StatusChanged += (svc, args) =>
        {
            // If service started successfully, create a gp task
            if (args.Status == LocalServerStatus.Started)
            {
                //do something, if needed
            }
        };
        // Start the local geoprocessing service
        await _localGpService.StartAsync();
    }
    catch (Exception e)
    {
        Console.WriteLine(e);
    }
}

 

(c) Nutzen des lokalen Services mit einem Geoprocessing Tasks

private async void MyMapViewOnGeoViewTapped(object sender, GeoViewInputEventArgs e)
{
    var gpUri = new Uri(_localServer.Services[0].Url.AbsoluteUri + "/SimpleBuffer");

    // Create parameters, run the task, process results, etc.
    // ...
    var geoprocessorTask = await GeoprocessingTask.CreateAsync(gpUri);

    var parameter =
        new GeoprocessingParameters(GeoprocessingExecutionType.AsynchronousSubmit)
        {
            OutputSpatialReference = MyMapView.SpatialReference
        };

    var myInputFeatures = new FeatureCollectionTable(new List<Field>(), GeometryType.Point, MyMapView.SpatialReference);
    // Create a new feature from the feature collection table. It will not have a coordinate location (x,y) yet
    var myInputFeature = myInputFeatures.CreateFeature();

    // Assign a physical location to the new point feature based upon where the user clicked in the map view
    myInputFeature.Geometry = e.Location;

    // Add the new feature with (x,y) location to the feature collection table
    await myInputFeatures.AddFeatureAsync(myInputFeature);

    parameter.Inputs.Add("InputFeatures", new GeoprocessingFeatures(myInputFeatures));
    parameter.Inputs.Add("Distance", new GeoprocessingLinearUnit(1000, GeoprocessingLinearUnits.Kilometer));

    GeoprocessingJob job = geoprocessorTask.CreateJob(parameter);
    try
    {
        // Execute analysis and wait for the results
        var analysisResult = await job.GetResultAsync();
        var outputFeatures = analysisResult.Outputs["BufferOutput"] as GeoprocessingFeatures;
    }
    catch (TaskCanceledException cancelledException)
    {
        // This is thrown if the task is cancelled.
        MessageBox.Show(cancelledException.Message);
    }
    catch (Exception exception)
    {
        MessageBox.Show(exception.Message);
    }
}

 

Ergebnis

 

Resümee

Mit der Local Server Technologie können fokussierte ArcGIS Runtime Anwendungen mit Werkzeugen aus ArcGIS Desktop für den GIS-Expertenbereich erstellt werden. Weiterhin gibt es die Möglichkeit, direkt auf Daten einer SDE zuzugreifen, viele verschiedene lokale Datentypen zu nutzen und vieles mehr. Und das mit modernster Technologie (64 Bit, Asynchron usw.) für verschiedene Desktop Plattformen. Wenn das nicht Grund genug für ArcGIS Engine/ArcObjects Entwickler ist …

What a blast! The last geodev meetup in Munich was just great! Many thanks to all of you for coming. With over 80 attendees it was our biggest meetup so far . My special thanks go to the three speakers, and of course also Google Germany for hosting the meetup at their campus in Munich.

 

Have you missed the meetup? No problem, we have recorded all talks, see below. You also can find all slides at the end of this blog. And don't worry, after the meetup is before the meetup  You can find all the details on meetup.com or here on GeoNet.

 

See you again soon!

Lars

 

 

 

 

 

Matt Bühler, vrbn - Mapping 3d worlds

Mapping is an inherently graphical & therefore creative task, where everybody may need individual, tailor-made solutions. In this presentation, Matt shows examples of his work of creating 3d maps, where he's leveraging cutting edge technology and scripted workflows for historic reconstructions, city planning scenarios and the creation of entirely fictional environments.

 

 

Prof. Mark Vetter, HS Karlsruhe - How digital maps are changing our spatial orientation skills?

The use of digital maps in smartphones will have an impact on our spatial orientation skills and our map reading competence. Therefore, we conducted a study with Munich high school students in the years 2013 and 2015 to reveal and quantify these changes. What could possibly be done in future school lessons to avoid further loss of this cultural competence?

 

 

 

Thomas Felber, Linde AG - BeeZero. Carsharing service with Zero Emissions

 

BeeZero® is the world's largest and friendliest car sharing with hydrogen. With BeeZero, a new environmentally friendly and modern form of mobility comes into the city. Thanks to the innovative hydrogen technology, we help you to reduce pollutants and traffic noise without sacrificing the convenience of a car. Learn about how we built a fully functional car sharing system within 100 days from scratch. In this talk you will also hear about the telematics used to operate and maintain to cars, the challenges we faced around map visualization and why we decided to start from scratch. Bee part of it, Beezero.

 

Filter Blog

By date: By tag: