MetaModels Tutorial Part 8

von René Aye

8.0 Automatic Address to Geocoordinate Conversion

Welcome to the 8th part of this MetaModels Project calles Assets.

In this part we are going to setup a handy feature: the address of an asset gets automatcally converted into geocoordinates. So we do not need to copy the address to a geo convert tool on a different website, copy the coordinates and paste them back to our assets imnput screen. Cool!

First things first: make sure that you have the dlh_googlemaps module written by Christian de la Haye installed in your Contao system. You can use the Extension catalog or Composer to install it.

The dlh_googlemaps extension does also install the dlh_geocode extension. This module is used to convert addresses into geocoordinates – in the next paragraphs I will show you how.

To create this automatic geocoordinate calculation feature we have to do two things:

  1. configuring the dcaconfig.php
  2. writing a converter method that gets called everytime an asset gets saved

8.1 The dcaconfig.php

In case you have absolutely no idea what the dcaconfig file is, I recommend you read about it in the  official Contao documentation.

the dcaconfig.php is a configuration file used to make custom modifications to Contao. Since the dcaconfig is loaded at the end of a page load it is possible to inject or overwrite some settings for your own need.

Now you need access to the dcaconfig. If your Contao is hosted on an external webserver you need a FTP or a SSH connection to that server to edit the dcaconfig file.

  • Goto the root of your Contao installation.
  • Open the file in /system/config/dcaconfig.php.
  • Add the following lines of code.
// tell Contao which function to call when the geolocation field gets saved
$GLOBALS['TL_DCA']['mm_assets']['fields']['geolocation']['save_callback'][] = array('Assets','convertAddressToGeocode');

Let me explain the code a bit.

$GLOBALS['TL_DCA']['mm_assets']['fields']['geolocation']['save_callback'][]

This part of line 2 says Contao that everytime a field called geolocation in our asset input screen gets saved, a function is called and the result of that function should be returned into that geolocation field.

= array('Assets','convertAddressToGeocode');

And this part tells Contao exactly which function to use. It's in the class Assets and the function is called convertAddressToGeocode.

This whole construct of fireing a function when a save action occurs is called a save callback and the function that gets called is our save callback function.

8.2 The Callback Function

I'm going to show you how I implemented a callback function. It is not the most effective and elegant function, I focused to make the code easy to understand and write the most minimal working code (I did not use a namespace e.g). Feel free to optimize it.

In 8.1. we configured the dcaconfig file to search the callback function in a class called Assets. So let's create this class locally on your desktop computer at first. And then we upload it to Contao.

  • Create a new folder called pyro_assets. You can call it whatever you want. All my extension folders begin with pyro_ and then a short name to know at first glance what this function is about.
  • In that folder create a folder called classes.
  • In the folder classes create a text file and call it Assets.php. In Contao the filename of the classes have to match the class name.
  • Open the Assets.php and insert the following code listing.
<?php
/* 
   Version 2 – 18-09-2014
   This version is far more simplified than the first version. 
   Thanks to a MetaMadels update it's much easier to modify the form data now!
    
   Thanks to Stefan Pröll by Design23 – http://www.design-23.de 
   He helped me out to get this new version working.
*/
 
 
class Assets
{
 
    public function convertAddressToGeocode()
    {
           
        // METAMODEL ENTRY
         
             
        // get the address data from form
         
        $strAddress = \Input::post("address");
         
        // get the gelocation data from form
         
        $strGeocode = \Input::post("geolocation");
         
        // address && empty geocode -> calculate new geocode and return it
         
        if ( ! empty($strAddress) && empty($strGeocode) )
        { 
            // convert any linebreak in the address to a comma
            $strAddress = preg_replace( "/\r|\n/", ", ", $strAddress );
            return \delahaye\GeoCode::getCoordinates($strAddress, '', ''); 
        }
         
        // address && geocode -> return the geocode (no overwrite geocode string)
        if ( ! empty($strAddress) && ! empty($strGeocode) ){ return $strGeocode; }
         
        // empty address && geocode -> return the geocode (no overwite geocode string)
        if ( empty($strAddress) && ! empty($strGeocode) ){ return $strGeocode; }
 
     
        return '';
    }
}

Some explanations of the code:

line 23–27

We need the contents of the address and the geolocation field of the asset we are editing or creating. Therefore we need the name of that form fields. The name corresponds to the column names of the attributes we created in part 3.

Here we actually get the contents of the address and the geolocation field. When the asset gets saved its data is send via POST to the server. What we do here is reading the POST data regarding the address and geolocation field.

The /Input::post() method is a Contao function which does exactly that. As parameter you have to set the name of the form field to read it's value. In this case the formfield name corresponds the column name of our attribute.

So \Input::post("address") would return the contents of the address field.

line 31

If the user entered a geocode manually into the geolocation field we return the geocode so it does not get overwritten. Remember: this is the save callback function for the geolocation field. So anything this function returns gets written into the geolocation field of our asset.

Since the user entered a geocode manually we do not need to calculate a geocode for this new asset (we are in the if block when a new asset is created).

line 31–36

Here comes the action: the user entered an addres (it is not empty) but the geolocation field is empty. This is the condition where we have to calculate a geocode of the address.

On line 34 we remove all line breaks from the address string and convert them to ", ". If the address string contains line breaks the geocode conversion method returns wrong coordinates.

In line 35 we use Monsieur de la Haye's dlh_geocode extension. 
\delahaye\GeoCode::getCoordinates($param1, $param2, $param3);

$param1 = Address to convert to a geocoordinate
$param2 = Country if the addres of $param1 (abbreviation, default 'de')
$param3 = Language of the $param1 (abbreviation, default 'de')

As you can see, I do not use the second and third parameter. I found out that if you insert the countryname in the first parameter, it is working fine. I think Googles API is smart enough to return the correct results if you omit $param2 and add the countryname within $param1. That is why you must add the country name in the address field when you create an asset.

I'm not sure what the $param3 is for. I think it's an indicator what language the address of $param1 is. So if your are using english country names you set $param3 to en. I have used german country names so I left $param3 blank because the default value is de.

line 38–42

Only when the geolocation field is empty and an address is given we calculate a new geocoordinate. Otherwise we return the content of the geolocation field to not overwrite it.

line 45

If the function reaches this line it returns an empty string. However I don't know what condition this should be.

8.3 Installing the Asset Class

Now that we have written our save callback function it's time to install it into the Contao system.

  • Upload the pyro_assets folder to /system/modules/
  • Login to the Contao backend.
  • We have to create an autoloader for our pyro_assets extension. Click on Autoload creator in the Main menu.
  • In the upcoming list of extensions checkmark pyro_assets.
  • Click Create the autoload files.

And thats the way the cookie crumbles (hah, can you guess which movie?). When you now create a new asset the geolocation field should be filled automatically as soon as you press the Save button and you entered a valid address.

That is all for this part. You learned how to customize the dcaconfig to use a save callback. You wrote your own callback class and I showed you how to install it in Contao.

Please use the comments section and let me know if all worked out for you. Here is the next part 9 where we finally create a working Google Maps marker example.


Say "Thank You" with a Like

This website has been created by pyropixel.de – the label I am working as a freelancer. Making these tutorials was hard long work.
Please consider to say "Thank you" with a like.
Thank you!


Zurück

Einen Kommentar schreiben

Kommentar von Bastian |

Hallo,
sehr gut beschriebenes Tutorial.
Ich habe nun sehr viele Einträge die ich öffnen und speichern müsste damit das Feld geolocation befüllt wird.
Ist es möglich diesen Schritt zu automatisieren?

Gruß
Bastian

Antwort von René Aye

Hallo Bastian,

dafür habe ich keine "Contao-Lösung" parat. Ich kenen aber das Problem, das nachträglich die Geolocations erzeugt werden müssen. Ich habe das dann mit einem PHP-Skript gemacht, dass alle Adressen aus der DB einliest, über die Google API die Geokoordinaten erzeugt und dann in die DB schreibt.

Das PHP-Skript habe ich dann einfach direkt im Browser aufgerufen.

Hilft dir der Ansatz so weiter?
Wäre aber vielleicht auch einen kleinen Artikel wert :)

Schöne Grüße
René

Kommentar von Bastian |

Hallo René,
das mit den Geokoordinaten hat sich erledigt.
Ich habe die Einträge einzeln geöffnet und gespeichert.
Ich denke das mit dem PHP-Skript hätte ich nicht so schnell hinbekommen.

Ich hätte aber noch eine andere Frage...

Kann ich bei der Umkreissuche die Entfernung der einzelnen Items ausgeben lassen?

Die Ergebniss-Liste nach Entfernung sortieren?

Die Geokoordinaten, der in der Suche eingetragenen Adresse, auslesen?

Da es über die MetaModels Umkreissuche nur sehr wenig Informationen gibt, wäre ich über deine Hilfe sehr dankbar.

Gruß Bastian

Antwort von René Aye

Hallo Bastian,

ganz ehrlich: bisher hat das Umkreissuche-Modul von MM nie bei mir funktioniert. Ist aber schon eine Weile her, vielleicht versuche ich es nochmal :)

Auf der Seite der Friseurklassifizierung-Deutschland habe ich eine eigene Umkreissuche geschrieben. Ist aber Aufwand.
Wenn du willst, kann ich die groben Schritte dazu erklären.

Gruß
René

Kommentar von Bastian |

Hallo René,
ganz ehrlich: die gleiche Erfahrung musste ich bisher auch mit der Umkreissuche von MM machen.
Aber jetzt bei meinem neuesten Projekt (und deinen sehr ausführlich geschriebenen Tutorials) scheint alles tadellos zu funktionieren.
Nur die Ausgabe der Entfernung bekomme ich nicht hin.
Dürfte ja aber eigentlich nicht so schwer sein. Berechnet wird die Entfernung ja.

Gruß
Bastian

Kommentar von Stef |

Hi,

Thanks for the tutorial, i have a message in the BE when i do an entry, i save and i have a error message "OVER_QUERY_LIMIT (You have exceeded your daily request quota for this API. We recommend registering for a key at the Google Developers Console: https://console.developers.google.com/apis/credentials?project=_)" and the geolocation field is empty.

Thx
Stef

Kommentar von Marco |

Bekomme beim Speichern eines Datensatzes folgende Fehlermeldung

Execute callback Assets::convertAddressToGeocode failed - Exception message: Class Assets does not exist

An was könntes das liegen?

Danke + Gruß Marco

Kommentar von Ralf |

Hallo René,

vielen Dank für das Tutorial, genial!
Wie der Psot vor mir, gleiches Problem, die Klasse wird nicht geladen:

Klasse nennet sich GSGeoCode, Datei unter system/modules/myclass/classes/GSGeoCode.php (ja auch den Klassennamen in der .php auf GSGeoCode geändert). Klasse in autoload aktiviert, die ./config/autoload.php und autoload.ini wurden erzeugt.

Der Callback-Aufruf in der dcaconfig ..array('GSGeoCode','convertAddressToGeocode');

sollte auch stimmen, aber im Backend bekomme ich beim Editieren immer "Execute callback GSGeoCode::convertAddressToGeocode failed - Exception message: Class GSGeoCode does not exist"