Tag Archive | PHP

Leaflet, PostGIS och PHP

Jag tänkte bygga på lite på tidigare inlägg om Leaflet och måndagens inlägg om PostGIS i QGIS.

Först så bygger jag ett javaskript som visar mitt PostGIS lager på Open Street Map via QGIS Server och LeafletJS.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="script/leaflet.css" />
</head>
<body>
    <div id="map" style="width: 800px; height: 600px"></div>
    <script src="script/leaflet.js"></script>
    <script>
        var osm = L.tileLayer('http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', {
            maxZoom: 18, 
            attribution: 'Kartan: &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> bidragsgivare.'
        });
        var signs = new L.TileLayer.WMS("http://192.168.0.25/cgi-bin/qgis_mapserv.fcgi?VERSION=1.3.0&map=/qgis/skyltar.qgs", {
            layers: 'skyltar',
            format: 'image/png',
            transparent: true,
            minZoom: 7
        });
        var map = L.map('map', {
            center: [57.66505,14.95789],
            zoom: 13,
            layers: [osm, signs]
        });
        var grundkartor = {
            "OSM": osm
        };
        var lager = {
            "Skyltar": signs
            //"Bilder": bilder
        };
        L.control.layers(grundkartor, lager).addTo(map);
                var popup = L.popup();
            function onMapClick(e) {
                popup
                    .setLatLng(e.latlng)
                    .setContent("Du klickade på: " + e.latlng.toString())
                    .openOn(map);
            }
            map.on('click', onMapClick);
    </script>
</body>

Jag kopierar helt enkelt ett exempel från http://leafletjs.com/ och modifierar det lite så att jag får med mitt eget WMS lager från QGIS Server.

Skärmbild från 2014-02-09 13:25:27

I stället för att bara visa positionstext så lägger jag även till en länk med POST information till en ny sida:

.setContent("Position: " + e.latlng.toString() + "<br><a href='form.htm?lat=" + e.latlng.lat.toString() + "&lng=" + e.latlng.lng.toString() + "'>Klicka för att skapa punkt</a>")

Detta skickar med positionen till den nya sidan ”form.php”:

<html>
<head>
</head>
<body>
<form action=spara.php method=POST>
<?PHP
echo "Latitud: <input type='text' name='lat' value=" . $_GET["lat"] . "><br>";
echo "Longitud: <input type='text' name='lng' value=" . $_GET["lng"] . "><br>";
?>
Typ: <input type="text" name="typ" value""><br>
Beskrivning: <input type="text" name="beskrivning" value""><br>
Datum: <input type="text" name="datum" value""><br>
<input type="submit" value="Spara">
</form>
</body>
</html>

Skärmbild från 2014-02-09 13:26:31

Sedan så måste alla data in i databasen, och det görs i filen ”spara.php”:

<html>
<head>
</head>
<body>
<?PHP
        $lng = $_GET["lng"];
        $lat = $_GET["lat"];
        $typ = $_GET["typ"];
        $beskrivning = $_GET["beskrivning"];
        $datum = $_GET["datum"];
        $sql = "INSERT INTO skylts (geom, typ, beskrivning, datum) VALUES (ST_Transform(ST_GeomFromText('POINT($lng $lat)',4326), 3006), '$typ', '$beskrivning', '$datum')";
        $databas = pg_connect("host='192.168.0.25' port='5432' dbname='geodata' user='user' password='password'");
         if (!$databas) {
             die("Fel i databasanslutning: " . pg_last_error());
         } 
        $resultat = pg_query($databas, $sql);
         if (!$resultat) {
             die("Fel i SQL-frågan: " . pg_last_error());
        }
        pg_free_result($resultat);
         pg_close($databas);
?>
<A href="skyltar.htm">Återvänd till kartan</A>
</form>
</body>
</html>

Sådär! Eftersom jag får Lat/Long i decimala grader och mina data är lagrade i SWEREF99TM i PostGIS så måste jag använda ”ST_Transform” i SQL-satsen.

Skärmbild från 2014-02-09 13:46:34

Nu kan jag klicka i kartan, redigera positionen och attributen och spara dessa i PostGIS och slutligen visa resultatet i kartan igen… Det går naturligtvis att snygga till och lägga till rätta i koden, och framför allt lägga till felhantering, men det ovanstående fungerar. Prova själv…

PHP och PostGIS

I går så handlade det om att ladda upp bilder till en server och visa var de var tagna på en karta genom att läsa EXIF informationen i bilderna. Nu hade jag tänkt gå vidare med att även skriva denna information till en PostGIS databas och därmed göra den tillgänglig för exempelvis QGIS.

Med utgångspunkt i förra php dokumentet görs nedanstående tillägg:

// Lägg till en punkt i databasen
if ($lat!=0) {   // Förklaring 1     
 $sql = "INSERT INTO foton (geom, namn, longitude, latitude, link) VALUES (ST_GeomFromText('POINT($lon $lat)',4326), '$uppladdad', $lon, $lat, '$newfile')"; // Förklaring 2
 $databas = pg_connect("host='192.168.0.25' port='5432' dbname='geodata' user='user' password='password'"); // Förklaring 3
 if (!$databas) { // Förklaring 5
   die("Fel i databasanslutning: " . pg_last_error());
 } 
 $resultat = pg_query($databas, $sql); // Förklaring 4
 if (!$resultat) { // Förklaring 5
   die("Fel i SQL-frågan: " . pg_last_error());
 }
 pg_free_result($resultat); // Förklaring 6
 pg_close($databas); // Förklaring 6
} // Förklaring 1

Det var det hela…

Nåja, det krävs nog både en och annan förklaring.

  1. Först så testar man om det finns ett värde i $lat. Det är ju onödigt att spara data till databasen som har en felaktig position, det vill säga bilder utan GPS-data.
  2. SQL raden innehåller kommandot för att lägga till en rad i tabellen ”foton”. I första parentesen listas de kolumner i tabellen man vill skriva till. Lägg märke till kolumnen som heter ”geom”. Denna skapas automatiskt när man skapar en ny PostGIS tabell i QGIS. Kontrollera i PgAdminIII vad den heter i ditt fall. Den andra parentesen, efter VALUES, innehåller de värden som skall skrivas till kolumnerna. Dessa skall komma i samma ordning som i den förra parentesen. För att göra om lat/long värdena till geometri så används funktionen ST_GeomFromText. I denna funktion så anges även att koordinaterna är i WGS84 (EPSG:4326). Tänk även på att kolumner av typen text skall vara omslutna av ‘enkelfnuttar’, medan siffervärden inte skall det.
  3. PG_Connect, försöker koppla upp mot en databas och sparar resultatet i variabeln ”$resultat”. Alla parametrarna i anslutningssträngen måste anpassas till de rådande förutsättningarna. I bland kan det vara problem att ansluta med en del användare, men prova med användaren postgres om du har problem.
  4. Pg_Query, utför det sqlkommando som skapades tidigare.
  5. De båda ”if” satserna testar om något gått fel i kommandona och skriver i så fall ut ett felmeddelande.
  6. Avslutningsvis frigörs resultatet och databaskopplingen stängs.

Nu kan jag ladda upp en bild på en webbsida (ladda.htm):

Skärmbild från 2014-02-02 14:57:25

Om bilden har GPS information i EXIF-data så visas den på kartan:

Skärmbild från 2014-02-02 14:59:07

Men jag kan även öppna PostGIS lagret i QIGS och titta på data där:

Skärmbild från 2014-02-02 15:03:47

I bilden ovan så visar jag hur jag även skapat ett ”kommando” som öppnar webbläsaren och visar den bild som är uppladdad i varje punkt.

Om du lyckats ladda hem och få igång php-sidan från igår så lägger du till koden ovan på rad 141 (efter $popkod raderna men innanför ”else” satsens klammerparentes).

Tänk även på att du måste själv skapa en tabell med lämpliga kolumner i PostGIS, samt att sätta rättigheterna på den användare du vill använda i php-skriptet. Sedan måste du anpassa SQL-kommandot så att det passar din tabell.

Ladda upp bilder till geoservern

Ladda upp och visa var bilder är tagna är det många webbsidor som erbjuder som tjänst. Vill man kunna göra det själv så är det faktiskt inte så svårt.

I detta inlägg skall jag visa hur du gör detta med PHP och Javaskript.

PHP tillåter i grunden bara filer upp till 2 Mb att laddas upp med skripten. Detta kan man ändra genom att redigera en fil på servern:

sudo vi /etc/php5/apache2/php.ini

Din php.ini kanske finns någon annanstans, men om du skapar ett enkelt php dokument med nedanstående text och kör det genom servern i en webbläsare, så står det där var filen är sparad på servern.

<html>
 <body>
 <?php
 phpinfo( );
 ?>
 </body>
 </html>

Ändra upload_max_filesize: 2M till något mer passande, jag väljer 8 Mb. När du är klar så startar du om webbservern:

sudo service apache2 restart

Först så skapar man ett enkelt formulär som pekar ut filen man vill ladda upp.

<form action="ladda.php" method="post" enctype="multipart/form-data">
<label for="file">Bild: </label>
<input type="file" name="file" id="file"><br><br>
<input type="submit" name="submit" value="Ladda Upp">
</form>

Skärmbild från 2014-02-01 23:41:33

Sedan behöver man skapa ett PHP dokument som tar hand om filen, testar den och kopierar den till lämplig plats. Här har jag valt katalogen ”upload” på samma plats där webbsidan finns. För att det skall fungera så måste den mappen finnas och användaren måste ha skrivrättigheter i den ( chmod 777 upload/ ).

<html>
<body>
<p>Uppladdad bild...</p>
<?php

//Funktion för att beräkna GPS data
function getGps($exifCoord, $hemi) {
$degrees = count($exifCoord) > 0 ? dela($exifCoord[0]) : 0;
 $minutes = count($exifCoord) > 1 ? dela($exifCoord[1]) : 0;
 $seconds = count($exifCoord) > 2 ? dela($exifCoord[2]) : 0;
$flip = ($hemi == 'W' or $hemi == 'S') ? -1 : 1;
return $flip * ($degrees + $minutes / 60 + $seconds / 3600);
}
//Dela EXIF data med "bråk" värde
function dela($coordPart) {
$parts = explode('/', $coordPart);
if (count($parts) <= 0)
 return 0;
if (count($parts) == 1)
 return $parts[0];
 return floatval($parts[0]) / floatval($parts[1]);
 }

// Tillåtna extensions
$allowedExts = array("jpeg", "jpg", "JPG", "JPEG");
// Kontrollera att det är bilder och med rätt storlek
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);
if ((($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/jpg"))
&& ($_FILES["file"]["size"] < 8000000)
&& in_array($extension, $allowedExts))
  {
  if ($_FILES["file"]["error"] > 0)
    {
    // Skriv felkod om något gick galet
    echo "Return Code: " . $_FILES["file"]["error"] . "<br>";
    }
  else
    {
    echo "Uppladdad: " . $_FILES["file"]["name"] . "<br>";
    echo "Typ: " . $_FILES["file"]["type"] . "<br>";
    echo "Storlek: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
    echo "Temp fil: " . $_FILES["file"]["tmp_name"] . "<br>";
    $newfile = "upload/" . $_FILES["file"]["name"];
    if (file_exists($newfile))
      {
      // Om filen redan finns
      echo $_FILES["file"]["name"] . " finns redan. ";
      }
    else
      {
      // Om allt fungerar
      //överför filen till den nya platsen med det nya namnet, visa felmeddelande om det inte gick.
      $copied = move_uploaded_file($_FILES['file']['tmp_name'],
      $newfile);
      if (!$copied)
      {
        echo '<h1>Kopiering misslyckades!</h1>';
      } else {
        echo "Sparat i: " . $newfile;
        //hämta EXIF informationen från filen
        $exif = read_exif_data( $newfile);
        $emake =$exif['Make'];
        $emodel = $exif['Model'];
        $eexposuretime = $exif['ExposureTime'];
        $efnumber = dela($exif['FNumber']);
        $eiso = $exif['ISOSpeedRatings'];
        $edate = $exif['DateTime'];
        $dir = dela($exif['GPSImgDirection']);
        $alt = dela($exif['GPSAltitude']);
        $lon = getGps($exif['GPSLongitude'], $exif['GPSLongitudeRef']);
        $lat = getGps($exif['GPSLatitude'], $exif['GPSLatitudeRef']);
        echo "<br />\n";
        echo "Bildfil: <a href='$newfile'>Visa bild</a><br />\n";
        echo "Longitud: $lon<br />\n";
        echo "Latitud: $lat<br />\n";
        echo "Höjd: $alt<br />\n";
        echo "Riktning: $dir<br /><br />\n";
        echo "Kamera tillverkare: $emake<br />\n";
        echo "Modell: $emodel<br />\n";
        echo "Exponering: $eexposuretime s<br />\n";
        echo "F-numer: $efnumber<br />\n";
        echo "ISO: $eiso<br />\n";
        echo "Tidpunkt: $edate<br />\n";

      }  
    }
  }
}
else
  {
  // Om det inte är rätt filtyp eller storlek...
  echo "Endast JPG/JPEG filer mindre än 8 Mb är tillåtna...";
  }

?> 
</body>
</html>

I filen ovan så finns även två funktioner som används för att räkna om GPS-information i EXIF data i filerna. Dessutom så kommer en hel del andra EXIF data läsas och skrivas ut på skärmen. Om det finns positionsdata så lagras dessa i variablerna $lat och $lon, vilket gör det enkelt att infoga en leafletkarta panorerade till rätt plats, kanske med en ”pin” som markör.

Lägg bara till html-kod, som i tidigare inlägg, för att skapa webbkartan och ”skjut in” koordinaterna från PHP-skriptet i Javaskriptet som JSON data:

<SCRIPT>
var map = L.map('map', {
  center: [<?php echo json_encode($lat); ?>, <?php echo json_encode($lon); ?>],
  zoom: 10
});
// Här läggs alla lager till
</SCRIPT>

Det är inte en komplett kod som visas ovan, utan endast hur du infogar PHP kod i Javascript. Vill du se ett exempel på hela koden så ladda hem exempelfilerna från GitHub längre ner.

Skärmbild från 2014-02-01 23:42:03

Om du följt med i serien med hur du skapar en egen server för geodatatjänster, eller om du har en linuxserver med PHP installerat så kan du snabbtesta det jag beskrivet här med nedanstående kommandon. Börja med att gå till en katalog på din webbserver (exempelvis: /var/www ).

sudo mkdir upload
sudo chmod 777 upload
sudo wget https://github.com/klakar/geosupportsystem/raw/master/ladda.htm
sudo wget https://github.com/klakar/geosupportsystem/raw/master/ladda.php

Öppna sidan ”ladda.htm” i webbläsaren Nu skall du förhoppningsvis kunna ladda upp bilder upp till 2 Mb utan felmeddelande och om du ändrar i php.ini enligt det som beskrivits ovan så kan du ladda upp 8 Mb. Jag har snyggat till koden lite i de filer du kan ladda hem ovan, och det ser även lite snyggare ut på skärmen.

Lycka till.

Skräddarsytt GIS för användaren

Har ni användare som sällan använder GIS eller hellre använder Google Earth, Eniro (eller motsvarande) för att titta på kartor för att det är enklare? Samtidigt så behöver de göra enkla uppdateringar i olika geografiska register eller göra enkla beräkningar, vilket normalt sett skulle kräva ett avancerat (och/eller dyrt) GIS system?

I så fall så måste du läsa detta!

Här kommer jag att visa hur du relativt enkelt kan skräddarsy ett GIS system för den stora mängden av användare, som ändå kan integrera lite enklare uppdateringstjänster, utan att du behöver betala en enda krona i licensavgifter.

Jag skall även nämna att det går att växa i detta system om man är villig att betala en slant för licenser och utbildning.

Läs mer…

Kamera med GPS, ladda upp bilder till servern

Nu är det dags att göra ett nytt försök med PHP och geotaggade bilder.

I ett tidigare inlägg skapade jag en hemsida där man fyller i bildpositioner och sökväg till bilder som redan är uppladdade för att sedan lagra dessa i en geodatabas, och sedan kunna visa dessa direkt via geoserver.

Nu har jag tänkt att lägga till uppladdning till den egna servern via PHP, men även att läsa information från EXIF data i bilderna. Om det är en kamera med GPS så får man då förhoppningsvis all information på köpet.

Läs mer…

GeomFromText(‘POINT ($east $north)’,3006)

Jag visade i ett tidigare inlägg hur man kan skapa ett enkelt webgränssnitt för att läsa och uppdatera data i en geodatabas (länk).

I denna artikel går jag igenom hur man kan skapa sidor som även lägger till information i geodatabasen från värden i ett formulär. Allt kretsar kring texten i rubriken här ovan.

Läs mer…

Snabbtips om UTF-8 och PHP

Jag har tidigare haft lite problem med konstiga tecken i GeoServer som hängde ihop med konflikter mellan UTF-8 kod och ISO 8859-1 (Latin). Lösningen då var att ändra kodningen i GeoServer till ISO 8859-1.

Nu stöter jag på nya problem när jag skapar hemsidor som läser och skriver data till Postgresql. Där tecken blir konstiga och det blir fel i SQL sökningar.

Efter mycket testande så visade det sig att lösningen var sagolikt enkel:

  1. Spara php filerna (eller html) med UTF-8 kodning (Windows: välj kodning vid spara som i notepad).
  2. Lägg in en meta tagg i <HEAD> taggen på websidorna som talar om att koden är UTF-8
    <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />

Det var det hela!