Random Thoughts on Random Things by Tanomsak
Random header image... Refresh for more!

Tutorial Google maps ใช้รูปภาพแทนหมุด (Custom Marker Icon)

07 Jun 2009

หลังจากหายหน้าไปนาน ขอต่อเรื่อง Google Maps ด้วยการใช้รูปภาพแทนหมุดกันครับ ถ้าจะค้นหาบทความเพิ่มเติม ลองค้นคำว่า Custom Marker Icon นะครับ เรามาเริ่มเรื่องของเรากันเลยดีกว่า

สังเกตุไหมครับว่าจากตัวอย่างที่ผ่านๆมาทั้งหมด หมุดของเราเป็นหมุดแดงๆ แล้วมีจุดดำๆตรงกลาง ซึ่งเป็นมาตรฐานของ Google Maps เค้า บา่งทีมันอาจดูไม่เนียน ไม่เข้ากับเวปเราเท่าไหร่ สามารถเปลี่ยนได้นะครับ โดยใช้ Customer Marker Icon วิธีกา่รไม่ยากครับ ผมว่าที่ยากที่สุดก็ตรงทำรูป Icon ที่ดูดีอย่างที่ต้องการมากกว่า (ของ ddproperty.com ผมกับเพื่อน ก็ดูแบบจากหลายๆที่มารวมๆกัน ซึ่งทำนานกว่าเขียน script อีก)

ddproperty-marker.png

Plazes-marker.png Yelp.png

การปรับ Options ของ Gmarker

ตามเอกสาร Google Maps API (Gmarker) ฟังชั่น Gmarker รับ parameter แบบนี้ครับ

Google Code.png

โดยปกติถ้าเราระบุแค่ latlng เราก็จะได้ Marker ที่ตำแหน่งที่ต้องการโดย icon จะเป็น G_DEFAULT_ICON ซึ่งก็คือ หมุดแดงที่มีจุดดำครับ

ถ้าเราต้องการเปลี่ยนหมุดให้เป็นรูปสวยงาม (??) ตามต้องการเราก็ต้องระบุ options (GMarkerOptions) ซึ่งมีตัวเลือกให้เยอะมากครับและก็มีเพิ่มขึ้นเรื่อยๆเมื่อมีการ update Google Maps API ที่เราสนใจในตอนนี้คือ options ชื่อ icon ครับ โดยเราต้องสร้าง GIcon (การใช้งาน GIcon) ขึ้นมาแล้วนำไปใช้

ใครถนัดอ่านภาษาอังกฤษผมแนะนำให้อ่าน Making your own custom markers ของคุณ Mike Williams ผมว่าเขียนไว้ได้ครบถ้วนดีครับ ผมจะพยายามแปลให้เข้าใจง่ายๆดูนะครับ ย้ำว่าส่วนต่อจากนี้ผมแปลบทความของคุณ Mike เป็นหลัก ผสมกับการอธิบายของผมตามที่ติดว่าจะเข้าใจง่ายขึ้นครับ

var Icon = new GIcon();
Icon.image = "mymarker.png";
Icon.iconSize = new GSize(20, 34);
Icon.shadow = "myshadow.png";
Icon.shadowSize = new GSize(36, 34);
Icon.iconAnchor = new GPoint(5, 34);
Icon.infoWindowAnchor = new GPoint(5, 2);
Icon.transparent = "mytran.png";
Icon.imageMap=[9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0];
Icon.printImage = "mymarkerie.gif";
Icon.mozPrintImage = "mymarkerff.gif";
Icon.printShadow = "myshadow.gif";

script ข้างบนเป็นการสร้าง GIcon เพื่อนำไปใช้สำหรับ options ในการสร้าง GMarker อีกทีนึง ที่เห็นยาวเหยียดจริงๆไม่ได้จำเป็นหมดทุกคำสั่งหรอกนะครับ แต่ว่าอันนี้เป็น parameter ที่มีเกือบทั้งหมดแล้ว ลองมาทำความเข้าใจไปทีละส่วนด้วยกันนะครับ

1. เริ่มสร้าง GIcon อันนี้ตรงไปตรงมาไม่มีอะไร

var Icon = new GIcon();

2. ต่อมาก็เริ่ม กำหนดรูปภาพที่ต้องการนำมาใช้เป็นรูปหมุด และบอกขนาดของรูปที่นำมาใช้ (กว้่าง x สูง)

ตรงนี้ต้องระวังเรื่อง path ของรูป ต้องให้ script สามารถมองเห็นรูปได้ไม่งั้นไม่ work และต้องกำหนด iconSize ถ้าไม่กำหนดจะไม่แสดง icon นะครับ การกำหนดขนาดของ icon กำหนดเป็น GSize นะครับ

Icon.image = "mymarker.png";
Icon.iconSize = new GSize(20, 34);

3. ถ้าใครต้องการทำเงาให้หมุดด้วยก็ต้องกำหนดรูปเงาและขนาดของเงาดังนี้ครับ (เป็น options ไม่ต้องมีก็ได้ครับ)

Icon.shadow = "myshadow.png";
Icon.shadowSize = new GSize(36, 34);

4. ต่อไปเป็นการกำหนดจุดอ้างอิงของหมุด ซึ่งมี 2 จุด (รูปนิ้วประกอบนำมาจากเวปคุณ Mike Williams นะครับ)

fingerbig.png

  • iconAnchor คือตรงปลายแหลมของหมุดที่จะปักลงบนแผน (จุดสีแดงในตัวอย่าง) ถ้ากำหนดผิดจุดเวลาแสดงผลมันจะดูหลอกๆ เพราะตรงปลายแหลมไม่ใช้จุดที่ปักลงแผนที่
  • infoWindowAnchor คือตำแหน่งที่จะให้ infoWindow จะโผล่ออกจากหมุด (จะให้ตรงปลายแหลมของ infoWindows โผล่ออกมาจากตรงไหนของหมุด) ถ้ากำหนดผิดจะทำให้เวลาแสดง infoWindow มันจะดูไม่เนียน เช่นห่างหมุดมากไป หรือทะลุออกมาจากกลางหมุด โดยปกติเราจะอยากให้ infoWindow ออกมาจากด้านบนของหมุด (จุดสีเขียวในตัวอย่าง)

การกำหนดจุดอ้างอิงทั้งสองทำได้โดย

Icon.iconAnchor = new GPoint(5, 34);
Icon.infoWindowAnchor = new GPoint(5, 2);

ให้สังเกตุว่าการนับตำแหน่งของจุดเริ่มจาก (1,1) ตรงมุมบนซ้ายของรูปหมุดของเรา และก็ใช้ GPoint ในการกำหนดจุดครับ

5. การป้องกันปัญหา การกดบนหมุดไม่ได้ (กดแล้วไม่เกิด event onClick) เวลามี infoWindow อยู่ใกล้ๆ

Icon.transparent = "mytran.png";
Icon.imageMap=[9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0];

  • transparent เป็นรูปภาพโปล่งใสของรูปหมุดของเรา (ดูวิธีการสร้างตอนท้าย) กำหนดเพื่อป้องกันการกดหมุดไม่ได้ใน Internet Explorer
  • imageMap กำหนดเพื่อป้องกันการกดหมุดไม่ได้ใน Firefox ชุดตัวเลขที่เห็นเป็นพิกัด x,y บนรูปหมุดของเรา เช่น 9,0 หมายถึงจุด x=9, y=0 ไล่ไปเรื่อยๆ ซุ่มๆกำหนดให้ทั่วๆหมุดลองเรา (ผมเองไม่ได้กำหนดตัวนี้เพราะว่างงเหมือนกัน ใครเข้าใจบอกด้วยครับ )

6. ส่วนสุดท้ายเป็นการเตรียมตัวสำหรับการพิมพ์ (options ใครไม่เน้นเรื่องพิมพ์ไม่ต้องกำหนดครับ)

Icon.printImage = “mymarkerie.gif”;
Icon.mozPrintImage = “mymarkerff.gif”;
Icon.printShadow = “myshadow.gif”;

เนื่องจาก browser ส่วนใหญ่ไม่สนับสนุนการพิมพ์รูปภาพแบบที่โปร่งใสบางส่วน Google Maps API เลยอนุญาติให้กำหนดรูปแทนได้เวลาจะพิมพ์

  • printImage เป็นรูปแทนของหมุดสำหรับ Internet Explorer
  • mozPrintImage เป็นรูปแทนของหมุดสำหรับ Firefox
  • printShadow เป็นรูปแทนของเงา

ถ้าไม่กำหนดค่า printImage และ mozPrintImage IE6 จะพิมพ์รูปหมุดหลักที่เรากำหนดไว้ก่อนหน้า (ซึ่งส่วนที่โปร่งใสจะออกมาทึบๆ) แต่ Firefox จะไม่พิมพ์หมุดเลยครับ

7. สุดท้าย พอเราสร้าง GIcon ใหม่เรียบร้อยแล้ว เราก็ต้องนำไปใช้ในการสร้างหมุด (GMarker) แบบนี้ครับ

var marker = new GMaker(latlng, {icon: Icon});

ลองดู ตัวอย่างการใช้รูปภาพแทนหมุด ที่เราทำข้างบนนะครับ

แถม 1: การสร้างเงาสำหรับหมุด

การสร้างเงา เริ่มแรกก็ใช้โปรแกรมจำพวก Photoshop เปิดรูปหมุดของเราขึ้นมาครับ แล้วก็เปลี่ยนสีของหมุดทั้งหมดให้เป็นสีดำไปเลย จากนั้นก็ปรับ opacity ลงซัก 50% หมุดดำของเราก็จะออกเป็นสีเทาๆ จากนั้นก็เอียงหมุด (Edit -> Transform -> Skew) ประมาณ 45% แล้วก็ทำพวก blur พองาม จากนั้นก็ Save for web เป็น PNG-24 เท่านี้ก็เรียบร้อยครับ

แถม 2: การสร้างภาพโปร่งใส (transparent) ของหมุด

การทำภาพโปร่งใสง่ายมากครับ แค่เปิดรูปหมุดในโปรแกรม Photoshop แล้วก็ลด opacity ลงเหลือ 1% เราจะมองไม่เห็นหมุดเลยครับ จากนั้นก็ Save for web เป็น PNG-24 เท่านี้ก็เรียบร้อยครับ

ทดลองทำกันดูนะครับ ทำเรียบร้อยแล้วก็ลองเอามาโชว์กันบ้างนะครับ ใครมีคำถามหรือว่าอยากแนะนำอะไร comment คุยกันได้นะครับ

  • Pingback: Tutorial แบบบ้านๆ ตอนที่ 1 - Basic Google Maps API — tanomsak.com

  • Pingback: การทำ Custom marker icon สำหรับ Google Static Maps — tanomsak.com

  • pesadking

    ถ้าไม่อยากให้มีเงาทำยังไงเหรอคะ
    อ๋อถามอีกหน่อยคร้า คือมีค่าใน database เป็นพันเรคคอร์ด พอวาดปุ๊บ ie error แก้ไงคะ

  • tanomsak

    ถ้าไม่อยากมีเงาไม่ต้องทำข้อ 3 ครับ คือไม่ต้องใส่ 2 บรรทัดนี้

    Icon.shadow = “myshadow.png”;
    Icon.shadowSize = new GSize(36, 34);

    error ใน IE เป็นยังไงครับ

  • pesadking

    error ว่า สคริปในเพจนี้ ทำให้ ie รันช้า ประมาณนี้ค่ะ พอกด yes ปุ๊บก้อวาดจุดได้ แต่ยังมะได้เช็คว่ามันวาดครบหรือเปล่าค่ะ

  • tanomsak

    ถ้าวาดทั้งพันจุดพร้อมๆกัน ก็อาจจะมีปัญหาครับเพราะใช้เมมโมรี่เยอะมากครับ

    ตั้งแสดงเฉพาะอันที่มองเห็นในแผนที่ครับ ลองดูแนวทางใน entry นี้ครับ http://www.tanomsak.com/index.php/2008/12/googl…

  • ladda

    ตอนนี้กำลังทำเกี่ยวกับแผนที่ประเทศไทย เป็นแผนที่ที่สร้างขึ้นมาเองค่ะ ก็เอาจาก google map มาตัดต่อเอาแต่ประเทศไทย จะปักหมุดลงบนแผนที่ค่ะ เราสามารถใช้โค้ดของ google map มาใช้ได้เลยหรือเปล่าคะ? อย่างไรขอคำแนะนำด้วยนะคะ ขอบคุณมากค่ะ

  • pesadking

    ขอบคุณมากคร้า

  • tanomsak

    ผมคิดว่าการเอาแผนที่จาก Google maps มาไว้ใน server เราน่าจะผิด Term of Service นะครับ ส่วนเรื่องการใช้ API ผมคิดว่าคงจะแก้เยอะเหมือนกันครับ

  • ladda

    พวกโค้ดนี้คือ เราสามารถนำมาใช้ได้เลยใช่ไหมคะ หรือว่าเป็น โค้ดนี้ต้องใช้ร่วมกับ map ของ google เท่านั้นคะ

  • tibby

    อยากทราบว่า
    var message = ["abc"];

    ต้องเพิ่มตรงนี้ถึงจะกดได้>>>>> mark.value = number;

    GEvent.addListener(marker,”click”, function() {
    var myHtml = message; >>>> แบบนี้กดไม่ได้ ต้องมี number + message; ถึงจะได้ค่ะ
    map.openInfoWindowHtml(latlng, myHtml);
    });

    แล้วกด marker ไม่ทำงานค่ะ นอกจากจะใช้ event-closure (คือ ส่ง random ส่งไปหลายค่าพร้อม number) จะกดได้ค่ะ
    ขอบคุณมากค่ะ..

  • tanomsak

    ทำไม message เป็น Array ล่ะครับ ลองเปลี่ยนเป็น

    var message = “abc”; ดูไหมครับ

    หรือว่า เปลี่ยน var myHtml = message; => var myHtml = message[0];

    ขอแนะนำให้ใช้ Firefox + Firebug Add-on ครับ ช่วย Debug javascript ดีมากๆครับ

  • Ba_nana55

    ขอบคุณสำหรับความรู้ดีๆค่ะ

    พอดีเหนเว๊บๆ นึง http://yoink.com

    เขาใช้การรวม marker ว่าตำแหน่งนั้นๆ มีกี่จุด

    เลยอยากได้วิธีคิด และ วิธีทำอะค่ะ

    ยังงัยรบกวนช่วยดูให้ทีนะค่ะ ว่าพอจะมีวิธีทำหรือป่าว ขอบคุณค่ะ

  • tanomsak

    เค้าเรียกว่า MarkerCluster ครับ ถ้าใช้ Google Maps API V3 ลองดูที่นี่ครับ

    http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/docs/

  • Tongta Tongjai

    เวลาปักมุด ครั้งนึงบนแผนที่ได้กี่อันหรอครับ

    พอดีเอาไปใช้ แล้วเวลามานเกิน 50 อัน แผนที่มานหายไปเลยอะครับ

    อยู่ที่ api ด้วยหรือเปล่า ครับ เพราะใช้แบบ ฟรีอยู่

  • tanomsak

    ผมว่าไม่จำกัดนะครับ แต่ที่หายไปอาจจะเนื่องจาก script มันใช้ RAM เยอะมากจนหมดหน้าเวปมีปัญหาหรือเปล่าครับทดลองกับอะไรครับ IE หรือว่า Firefox, Chrome

    อีกอย่างถ้าจะแสดงหมุดเยอะๆ ลองใช้ AJAX แสดงเฉพาะที่อยู่ในแผนที่ที่เราเห็นจะเปลือง RAM น้อยกว่าครับ

  • High_tactic

     รบกวนถามหน่อยนะครับบบ

    Google Maps By Olo_WaNtfunction initialize() {var map = new GMap2(document.getElementById(“map_canvas”));//ใส่ค่าละติจูด , ลองติจูด , ระยะการซูมmap.setCenter(new GLatLng(14.077817097589845, 100.60328722000122), 11);map.setUIToDefault();map.addControl(new GSmallMapControl());map.addControl(new GMapTypeControl());// Create a base icon for all of our markers that specifies the// shadow, icon dimensions, etc.var baseIcon = new GIcon(G_DEFAULT_ICON);baseIcon.shadow = “http://www.google.com/mapfiles/shadow50.png”;baseIcon.iconSize = new GSize(20, 34);baseIcon.shadowSize = new GSize(37, 34);baseIcon.iconAnchor = new GPoint(9, 34);baseIcon.infoWindowAnchor = new GPoint(9, 2);// Creates a marker whose info window displays the letter corresponding// to the given index.function createMarker(point, index) {  // Create a lettered icon for this point using our icon class  var letter = String.fromCharCode(“A”.charCodeAt(0) + index);  var letteredIcon = new GIcon(baseIcon);  letteredIcon.image = “http://www.google.com/mapfiles/marker” + letter + “.png”;  // Set up our GMarkerOptions object  markerOptions = { icon:letteredIcon };  var marker = new GMarker(point, markerOptions);  GEvent.addListener(marker, “click”, function() {    marker.openInfoWindowHtml(“Marker ” + letter + ““);  });  return marker;}// Add 10 markers to the map at random locationsvar bounds = map.getBounds();var southWest = bounds.getSouthWest();var northEast = bounds.getNorthEast();var lngSpan = northEast.lng() – southWest.lng();var latSpan = northEast.lat() – southWest.lat();for (var i = 0; i < 10; i++) {  var point = new GLatLng(southWest.lat() + latSpan * Math.random(),                          southWest.lng() + lngSpan * Math.random());  map.addOverlay(createMarker(point, i));}}คือจากโค้ดนีนะครับมันสามารถสร้างมาร์กเกอร์ได้ แต่ผมต้องการที่จะกำหนดไปเลยว่ามาร์กเกอร์ตัวนี้ให้จุดนี้ๆๆ ผมต้องแก้อย่างไรครับ ขอบคุณล่วงหน้าครับ