Irmante Astalavista

Simsalabim!!!
Blog ini bercerita tentang dunia IT, ilmu kebumian, dan lain-lain.

Saturday, June 26, 2010

Mapserver, Extent PostGIS, dan jQuery

Seringkali kita dihadapkan pada keinginan untuk menampilkan peta lokasi berdasarkan pilihan user. Contoh sederhananya, ketika user melakukan klik pada sebuah tabel atau grafik yang memiliki informasi lokasi, maka panel lain akan menampilkan peta lokasi pada baris tabel atau grafiknya.
Well... biasanya sih, bilamana kita menggunakan mapserver berbasis framework, seperti pMapper, ka-map!, fist, dst, maka kita dapat melakukan pencarian lokasi dengan menggunakan tombol Find, Search, dan sebagainya.
Nah framework Mapserver dengan segala tombolnya itu, malah bukan tampilan yang diinginkan user awam a.k.a management :D. User maunya dari sembarang tabel atau grafik, klik… muncul peta lokasinya tanpa embel-embel perlu klik sana-sini. Sesederhana itu dan ga banyak cingcong....
'Klik dan tampilkan lokasi' yang bisa dilakukan pada Mapserver adalah dengan memanfaatkan bahasa PHPMapscript dengan perintah dasar
$oMap->setExtent(xMin, yMin, xMax, yMax)
Artinya tampilkan peta lokasi dengan batas pojok kiri bawah (xMin, yMin) dan batas pojok kanan atas (xMax, yMax). Pada mapserver, nilai extent itu secara default tersimpan pada file *.Map, dimana ukuran nilai extent biasanya meliputi wilayah seluruh peta, bukan lokasi spesifik dari peta.

Itu malah jadi problem buat klik dan tampilkan lokasi....
Perhatikan gambar di atas. Area bergaris biru (----), merupakan nilai kotak extent dari peta kecamatan A, B, C, D, E, F. Area bergaris hijau (----), merupakan nilai kotak extent dari peta kecamatan D, E.
Untuk areal seluruh kecamatan memiliki nilai extent:
xMin-yMin: 581663.9375 - 9621369
xMax-yMax: 1050545 - 9855190
Untuk areal kecamatan D dan E, propinsi DEF memiliki nilai extent:
xMin-yMin: 790040.8125 - 9663481
xMax-yMax: 981676.375 - 9846642

Taunya darimana nilai koordinat pojok kiri dan pojok kanan atas wilayah yang kita ingin tampilkan?
Ada beberapa cara sebenernya, yang paling sederhana adalah buat kotak pada piranti lunak GIS terhadap kedua areal tersebut. Kemudian catat nilai kursor koordinat pojok kiri bawah dan koordinat pojok kanan bawah dari kotak tsb.
Perhatikan! bahwa sistem proyeksi yang mudah diinterpretasi Mapserver dalam menentukan nilai skala adalah sistem proyeksi UTM yah, bukan derajat desimal.
Well…, yang bisa saya pikirkan adalah menggunakan fasilitas ST_EXTENT pada PostGIS. ST_EXTENT merupakan perintah untuk mengambil extent batas terluar dari sekumpulan lokasi peta hasil query, dengan output hanya satu baris!. Misal tabel peta dengan menggunakan query WHERE diperoleh hasil 2 kecamatan, maka fitur ST_EXTENT ini akan menampilkan nilai batas terluar dari kedua kecamatan tersebut, hanya satu baris.Dengan menambahkan beberapa bumbu query, maka kita akan dapat memperoleh nilai xMin, yMin, xMax, yMax dari wilayah terpilih.


Kalo lebih mudah utk mapserver dpt dipecah jadi 2 kolom extent yaitu kolom xymin dan xymax, melalui query berikut:
SELECT REPLACE(SPLIT_PART(ASTEXT(ST_EXTENT(ST_TRANSFORM(geometry,32749))), ',', 1), 'POLYGON((', '') as xymin, SPLIT_PART(ASTEXT(ST_EXTENT(ST_TRANSFORM(geometry,32749))), ',', 3) as xymax FROM wilayahku WHERE propinsi='DEF' AND kabupaten='3';

Artinya: Tentukan area extent dari wilayah propinsi='DEF' AND kabupaten='3'; (perintah WHERE), dengan cara mengambil nilai geometri (kolom GEOMETRY), dalam proyeksi UTM 49S (perintah ST_TRANSFORM(geometry,32749)), lalu terjemahkan sebagai text (perintah ASTEXT), lalu pisahkan nilai tersebut menggunakan parameter split (perintah SPLIT_PART).
Dengan mengubah-ubah parameter WHERE, maka kita bisa mendapatkan nilai extent secara dinamis ya tho… Nilai extent inilah yang menjadi dasar inisiasi Mapserver sehingga user bisa melakukan 'klik dan menampilkan lokasi'.

Masuk ke koding yah...
Langkah pertama adalah membuat query function pada PostGIS untuk mendapatkan area extent berdasarkan inputan propinsi, kabupaten, dan kecamatan. Nama query function itu: _a_mapex(propinsi, kabupaten, kecamatan).
-- Function: _a_mapex(text, text, text)

-- DROP FUNCTION _a_mapex(text, text, text;
CREATE TYPE ms_extent AS
(xmin text,
xmax text);
ALTER TYPE ms_extent OWNER TO postgres;

CREATE OR REPLACE FUNCTION _a_mapex(text, text, text)
RETURNS SETOF ms_extent AS
$BODY$
--returns setof party as $$
DECLARE

BEGIN
IF $1!='0' AND $2!='0' AND $3!='0' THEN
RETURN QUERY
select replace(split_part(astext(st_extent(st_transform(geometry,32749))), ',', 1), 'POLYGON((', '') as xymin,
split_part(astext(st_extent(st_transform(geometry,32749))), ',', 3) as xymax from wilayahku where propinsi = $1 and kabupaten=$2 and kecamatan=$3;

ELSE IF $1!='0' AND $2!='0' AND $3='0' THEN
RETURN QUERY
select replace(split_part(astext(st_extent(st_transform(geometry,32749))), ',', 1), 'POLYGON((', '') as xymin,
split_part(astext(st_extent(st_transform(geometry,32749))), ',', 3) as xymax from wilayahku where propinsi = $1 and kabupaten=$2;

ELSE IF $1!='0' AND $2='0' AND $3='0' THEN
RETURN QUERY
select replace(split_part(astext(st_extent(st_transform(geometry,32749))), ',', 1), 'POLYGON((', '') as xymin,
split_part(astext(st_extent(st_transform(geometry,32749))), ',', 3) as xymax from wilayahku where propinsi = $1;

ELSE IF $1='0' AND $2='0' AND $3='0' THEN
RETURN QUERY
select replace(split_part(astext(st_extent(st_transform(geometry,32749))), ',', 1), 'POLYGON((', '') as xymin,
split_part(astext(st_extent(st_transform(geometry,32749))), ',', 3) as xymax from wilayahku;
END IF;
END IF;
END IF;
END IF;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION _a_mapex(text, text, text) OWNER TO postgres;
Contoh pemanfaatan query function itu dengan mencoba query berikut:
Untuk menampilkan semua wilayah menggunakan perintah:
select * from _a_mapex('0','0','0');
Untuk menampilkan propinsi ‘DEF’ menggunakan perintah:
select * from _a_mapex('DEF','0','0');
Untuk menampilkan propinsi ‘DEF’ dan kabupaten ‘3’ dan kecamatan=’F’ menggunakan perintah:
select * from _a_mapex('DEF','3','F');

PostGIS function udah... ,
Sekarang tinggal urusan MapServer dan jQuery neh…
Langkah keduanya adalah kita buat dulu file wilayahku.map yg menampilkan peta.
Filenya berjudul: wilayahku.map, dengan layer berjudul data_postgis.
MAP
NAME "wilayahku"
STATUS ON
SIZE 550 400
#EXTENT 558219.89536477 9609678.45877826 1073988.99548252 9866880.21769665
EXTENT 581663.9375 9621369 1050545 9855190
UNITS meters
RESOLUTION 72
IMAGETYPE PNG
IMAGECOLOR 255 255 255

SCALEBAR
STYLE 1
UNITS kilometers
END

WEB
IMAGEPATH "/ms4w/tmp/ms_tmp/"
IMAGEURL "/ms_tmp/"
END

PROJECTION
"init=epsg:32749"
#"init=epsg:4326"
#"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
END

#--------------------------------------------------------------------
LAYER
NAME "data_postgis"
STATUS DEFAULT
TYPE POLYGON
CONNECTIONTYPE POSTGIS
CONNECTION "host=localhost port=5432 dbname=msdemo user=postgres password=00000"
DATA "geometry from wilayahku using unique gid using srid=32749"

PROJECTION
"init=epsg:32749"
#"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
END #PROJECTION

CLASS
COLOR 225 255 100
OUTLINECOLOR 0 0 0
END

END #LAYER
END


Langkah ketiga, kita perlu membuat file PHPMapscript mapex.php, yang dapat menghasilkan gambar peta lokasi, berdasarkan serangkaian input parameter.

Dilanjut dengan script berikut:
<?php
//Generate the chart element
$paramd = $_GET['param'];
$eval = explode("_", $paramd);
//Connect to the DB
$conn = pg_connect("host=localhost user=postgres password=00000 dbname=msdemo");
//Fetch all factory records
$strQuery = "select * from _a_mapex('" . $eval[0] . "','" . $eval[1] . "','" . $eval[2] . "');";
// echo $strQuery;
// exit;
$result = pg_query($strQuery) or die('Query failed: ' . pg_last_error());
// printing table rows
if ($result) {
while($ors = pg_fetch_array($result)) {
$mapex = $ors[0] . " " . $ors[1];
}
}
//echo $mapex;

pg_free_result($result);
pg_close($conn);

$map = ms_newMapObj($_GET['map']);
// We create the map object based on the mapfile received as parameter

$size = explode(" ",$_GET['mapsize']);
$map->setSize($size[0], $size[1]);
// and set the image size (resolution) based on mapsize parameter
// Update: The map size must be setted before the extent, otherwise the extent
// will be adjusted to the aspect ratio of the map defined on SIZE parameter
// of MAP object in your mapfile

$extent = explode(" ",$mapex);
$map->setExtent($extent[0], $extent[1], $extent[2], $extent[3]);
// We get the mapext parameter... split it on its 4 parts using
// the space character as splitter

$layerslist=$_GET['layers'];
for ($layer = 0; $layer < $map->numlayers; $layer++) {
$lay = $map->getLayer($layer);
if ((strpos($layerslist,($map->getLayer($layer)->name)) !== false)
or (($map->getLayer($layer)->group != "") and
(strpos($layerslist,($map->getLayer($layer)->group)) !== false))){
// if the name property of actual $lay object is in $layerslist
// or the group property is in $layerslist then the layer was requested
//so we set the status ON... otherwise we set the stat to OFF
$lay->set(status,MS_ON);
} else {
$lay->set(status,MS_OFF);
}
}

// The next lines are the same as previous mapscript
$oImg = $map->draw();
$url = $oImg->saveWebImage(MS_PNG, 0, 0, -1);
echo "<img id=\"mymap\" src=" . $url . " style=\"border:1.5px solid grey;\"></img>";
//echo $map->scaledenom //check skala output yang keluar, soalnya kalo dalam satuan DD ga keluar jeh.

//BILAMANA ADA PERHITUNGAN MAX-MINLABEL, JANGAN GUNAKAN UNIT DD, TAPI GUNAKAN UNIT METERS, SRID LAYER PETA TETAP PROYEKSI DESIMAL DEGREE GAK MASALAH,
//CUMAN OUTPUT PROJECTION *.MAP TETAP HARUS UTM, CONTOH SRID 32739 utk UTM 49S atau 32649 utk UTM 49N
?>
Berikutnya adalah pengetesan nilai parsing dengan cara mencopy paste url ke dalam browser (perhatikan bagian input param):
Contoh untuk menampilkan peta kabupaten 1 propinsi ABC
http://localhost/msdemo/mapex.php?param=ABC_1_0&map=wilayahku.map&mapsize=450%20300&layers=data_postgis

Contoh untuk menampilkan data propinsi DEF
http://localhost/msdemo/mapex.php?param=DEF_0_0&map=wilayahku.map&mapsize=450%20300&layers=data_postgis

Welldone…!!!
Langkah keempat, sekarang kita bermain dengan file HTML-nya yang akan melakukan proses klik dan tampilkan lokasi.
Copy paste script berikut, dan simpan dalam file berjudul mapex_jquery.html:
Perhatikan kalo file ini membutuhkan jQuery file jquery-1.4.2.min.js yg tersimpan dalam direktori jQuery. jQuery ini bertujuan untuk:
  • mengolah dan menampung peta hasil query pada mapex.php kedalam div dengan id=map_tag,

  • proses render halaman bersifat parsial Ajax, karena yang diubah hanya gambarnya saja bukan keseluruhan dari halaman web,

  • Menambahkan efek animasi sederhana, biar keliatan lebih cooool…


<HTML>
<HEAD>
<style type="text/css">
<
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
</style>

<SCRIPT LANGUAGE="JavaScript" src="FusionCharts/jquery-1.4.2.min.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript">
function mymap(mylocation1){
//window.alert(mylocation1);
//perhatikan, kalo yg diubah sekali lagi hanya pada input param yah!
$.ajax({ type: "GET", url: "mapex.php", dataType: 'html', data: "map=wilayahku.map&param=" + mylocation1 + "&mapsize=450%20300&layers=data_postgis", error: function(xhr, textStatus, errorThrown) { alert('An error occurred! ' + errorThrown); }, success: function(html){ $('#map_tag').hide().slideDown().html(html); } }); };
</SCRIPT>
</HEAD>
<BODY>
<p><h1>Klik dan tampilkan lokasi</h1></p>
<table>
<tr>
<td>
<a href="JavaScript:mymap('0_0_0');">Seluruh Area</span></a>
<li><a href="JavaScript:mymap('ABC_0_0');">Propinsi ABC</a></li> <ul> <li><a href="JavaScript:mymap('ABC_1_0');">Kabupaten 1</a></li> <ul> <li><a href="JavaScript:mymap('ABC_1_A');">Kecamatan A</a></li> <li><a href="JavaScript:mymap('ABC_1_C');">Kecamatan C</a></li> </ul> <li><a href="JavaScript:mymap('ABC_2_0');">Kabupaten 2</a></li> <ul> <li><a href="JavaScript:mymap('ABC_2_B');">Kecamatan B</a></li> </ul> </ul> <li><a href="JavaScript:mymap('DEF_0_0');">Propinsi DEF</a></li> <ul> <li><a href="JavaScript:mymap('DEF_3_0');">Kabupaten 3</a></li> <ul> <li><a href="JavaScript:mymap('DEF_3_D');">Kecamatan D</a></li> <li><a href="JavaScript:mymap('DEF_3_E');">Kecamatan E</a></li> </ul> <li><a href="JavaScript:mymap('DEF_4_0');">Kabupaten 4</a></li> <ul> <li><a href="JavaScript:mymap('DEF_4_F');">Kecamatan F</a></li> </ul> </ul>
</td>
<td>
<div id="map_tag"></div>
</td>
</tr>
</table>
</BODY>
</HTML>
</BODY>
</HTML>

Sekarang tinggal testing outputnya:


Downloadnya di sini.

Labels: ,


7 Comments:

Anonymous Anonymous said...

Mas,,,
gimana kalo searching nya menggunakan php/mapscript dan mysql?? apa fasilitas st_extent hanya ada di postgis??

7:54 PM  
Blogger Irmanator said...

Dulu saya memang pake MySQL mas, cm migrasi ke PostgreSQL krn lebih sesuai dengan kebutuhan.
Monggo di googling saja... mustinya ada yg buat fungsi extent pada mysql.

8:44 AM  
Anonymous Anonymous said...

mau tanya menurut anda bisakan Php Mapscript mengantikan mapfile??
jadi konfigurasi mapfile ada didalam script php itu?bs ? trimakasih

12:25 PM  
Blogger Irmanator said...

Bisa saja, cuman belum pernah dicoba. Intinya koding itu kan banyak jalan menuju roma... cari yang paling PAS, itu kuncinya :D

5:11 PM  
Blogger Unknown said...

salam kenal mas...
sy baru 2 minggu ini mempelajari tentang mapserver,jd sy benar2 pemula di bidng ini..yang mau sy tanyakan adalah bagaimana mendapatkan extent dari shapefile??
thanks b4..

9:29 AM  
Blogger Irmanator said...

Kalau mau manual pake ArcGIS atau sembarang piranti lunak mapping. Buat kotak persegi panjang yang meliputi batas atas, kiri, kanan, bawah sebuah atau area poligon yang diinginkan.
Maka nilai pojok kiri bawah merupakan nilai extent 1 (x1, y1) dan nilai pojok kanan atas merupakan nilai extent 2 (x2, y2). Kalo mau otomatis bisa pake FWTools (http://fwtools.maptools.org/) atau ya lewat cara prosedur diatas. Good luck... :D

2:02 PM  
Blogger Unknown said...

mas link download g bisa ! bisa kirim lagi link download nya tq

7:49 PM  

Post a Comment

<< Home