16 มกราคม 2556

[Android Code] การวาด Polyline และ Polygon ลงบน Google Maps Android API v2


* Google Maps Android API v2 ไม่สามารถรันทดสอบบน AVD ได้ *


คราวนี้ขอมาต่อกับเรื่องการวาด Polyline กับ Polygon กันบ้าง
เป็นการวาดเส้นหรือวาดพื้นผิวลงบน Google Maps นั่นเอง
เพื่อให้ผู้ที่หลงเข้ามาอ่านสามารถวาด Overlay ลงบนแผนที่ได้


ทีนี้มาดูกันก่อนว่า Polyline กับ Polygon คืออะไร ต่างกันยังไง


สำหรับ Polyline ก็จะมีลักษณะเป็นเส้นที่ลากไปเป็นจุดๆ
ส่วน Polygon จะเป็นลักษณะพื้นผิว มีทั้งพื้นผิวและเส้นขอบ
โดยที่เส้นขอบของ Polygon จะต้องเป็นเส้นปิด ถึงจะมีพื้นผิว
ถ้ากำหนดเส้นของ Polygon เส้นเปิดจะทำให้มีแต่เส้นขอบ


ดังนั้นการวาด Polygon ใน GMapsV2 จะต้องวาดให้เป็นเส้นปิด
ส่วน Polyline เป็นการลากเส้น จึงไม่มีพื้นผิวถึงแม้ว่าจะเป็นเส้นปิด

ซึ่งการวาดเส้น Polyline และ Polygon จะกำหนดพิกัดบนแผนที่ ดังนี้
GoogleMap mMap = ((SupportMapFragment)getSupportFragmentManager() .findFragmentById(R.id.map)).getMap(); PolylineOptions rectLine = new PolylineOptions() .add(new LatLng(35.1638, 136.90435)) .add(new LatLng(35.1637, 136.90435)) .add(new LatLng(35.1637, 136.90445)) .add(new LatLng(35.1638, 136.90445)) .add(new LatLng(35.1638, 136.90435)); mMap.addPolyline(rectLine);
จากตัวอย่างข้างบนนี้จะเป็นการวาดเส้น Polyline ลงบนแผนที่
จะเห็นว่าการลากเส้นจะใช้คำสั่ง add แล้วกำหนดพิกัดไปเรื่อยๆ
สำหรับบางคนอาจจะพึ่งเคยเห็นการพิมคำสั่งต่อๆกันแบบนี้
ซึ่งจริงๆแล้วการทำงานของคำสั่งก็เหมือนกับคำสั่งนี้เลย
GoogleMap mMap = ((SupportMapFragment)getSupportFragmentManager() .findFragmentById(R.id.map)).getMap(); PolylineOptions rectLine = new PolylineOptions(); rectLine.add(new LatLng(35.1638, 136.90435)); rectLine.add(new LatLng(35.1637, 136.90435)); rectLine.add(new LatLng(35.1637, 136.90445)); rectLine.add(new LatLng(35.1638, 136.90445)); rectLine.add(new LatLng(35.1638, 136.90435)); mMap.addPolyline(rectLine);
ถ้าผู้ที่หลงเข้ามาอ่านสงสัย ก็ถามหลังไมค์เอาเนอะ ขี้เกียจพิม

ทีนี้เส้น Polyline ที่กำหนดขึ้นมาก็จะออกมามีลักษณะแบบนี้



ผู้ที่หลงเข้ามาอ่านอาจจะสงสัยว่าสี่เหลี่ยมทำไมใช้คำสั่ง add ตั้ง 5 ครั้ง
ให้ลองดูภาพลำดับขั้นตอนการวาด Polyline จากภาพข้างล่างนี้ดู



แต่เนื่องจากเจ้าของบล็อกใช้คำสั่ง Polyline สีเหลี่ยมจึงมีแต่เส้นขอบ
และนอกจากนี้ยังกำหนดคุณสมบัติอื่นๆของ Polyline ได้อีกด้วย เช่น
PolylineOptions rectLine = new PolylineOptions() .add(new LatLng(35.1638, 136.90435)) .add(new LatLng(35.1637, 136.90435)) .add(new LatLng(35.1637, 136.90445)) .add(new LatLng(35.1638, 136.90445)) .add(new LatLng(35.1638, 136.90435)) .width(3).zIndex(17) .color(Color.rgb(0x23, 0x92, 0x99));
คำสั่งดังกล่าวจะกำหนดเส้นหนา 3 Pixel และ zIndex เท่ากับ 17
โดยที่ zIndex ก็คือลำดับชั้นของ Polyline หรือ Polygon 
ซึ่งเหล่านี้จะเรียงลำดับกันเหมือนกับเลเยอร์ซ้อนกันเป็นชั้นๆ
ถ้ากำหนด zIndex มีค่าเยอะ ก็จะอยู่ชั้นบนสุด ค่าน้อยอยู่ชั้นล่าง


ส่วน color(Color.rgb(0x23, 0x92, 0x99)) ก็คือการกำหนดสีเส้นนั่นเอง
ซึ่งก็คือสี #239299 นั่นแหละ แต่ว่าการกำหนดในนี้ต้องใช้ Color.rgb ช่วย
หรือจะกำหนดเป็นสีที่มีให้อยู่แล้วก็ได้เช่น Color.RED, Color.BLUE เป็นต้น


ทีนี้มาดูที่คำสั่ง Polygon กันบ้าง ซึ่งหลักๆจะเป็นการวาดเส้นนั้นแหละ
เมื่อวาดเส้นปิดครบหมดแล้วก็จะมีพื้นผิวแสดงขึ้นมาให้ทันที
ซึ่งการวาด Polygon จะต้องลากเส้นทวนเข็มนาฬิกาเท่านั้นนะ
ถ้าวาดตามเข็มนาฬิกาจะมีแค่เส้นขอบ แต่พื้นผิวจะไม่แสดง
rectGon = new PolygonOptions() .add(new LatLng(35.163915, 136.904080), new LatLng(35.163889, 136.904053), new LatLng(35.163843, 136.904038), new LatLng(35.163779, 136.904101), new LatLng(35.163798, 136.904201), new LatLng(35.163872, 136.904218), new LatLng(35.163923, 136.904182), new LatLng(35.163915, 136.904080));


และ Polygon ก็สามารถกำหนดคุณสมบัติต่างๆได้เช่นกัน เช่น
PolygonOptions rectGon = new PolygonOptions() .add(new LatLng(35.1639, 136.90455), new LatLng(35.1636, 136.90455), new LatLng(35.1636, 136.90475), new LatLng(35.1639, 136.90475), new LatLng(35.1639, 136.90455)) .strokeColor(Color.BLACK) .fillColor(Color.argb(0x50, 0x23, 0x92, 0x99)) .strokeWidth(3);
จากข้างบนนี้เจ้าของบล็อกลากเส้นตามจุดเดิมเลย
แต่กำหนดสีของเส้นขอบเป็นสีดำ สีพื้นเป็น #50239299
คราวนี้จะเห็นว่าเป็น argb แล้ว จากเดิมที่ใช้เป็น rgb
โดยที่ a ที่เพิ่มเข้ามาก็คือ alpha หรือความโปร่งใสนั่นเอง
ในทีนี้ก็กำหนดไว้ประมาณ 31.37% (0 - 255 หรือ 0 - 0xFF)
และความหนาของเส้นขอบ 3 Pixel ก็จะออกมาตามนี้เลย



และสำหรับ Polygon ยังมีความพิเศษอีกอย่างหนึ่งคือ
สามารถเจาะรูบนพื้นผิวที่เป็น Polygon ได้ด้วยแหละ
เอาไว้สำหรับกำหนดพื้นที่ที่มีบางส่วนไม่ต้องการ
ซึ่งการเจาะรูบน Polygon ก็จะใช้คำสั่ง addHole 
แล้วก็กำหนดเหมือนตอนลากเส้น Polygon เลย
rectGon = new PolygonOptions() .add(new LatLng(35.163915, 136.904080), new LatLng(35.163889, 136.904053), new LatLng(35.163843, 136.904038), new LatLng(35.163779, 136.904101), new LatLng(35.163798, 136.904201), new LatLng(35.163872, 136.904218), new LatLng(35.163923, 136.904182), new LatLng(35.163915, 136.904080)) .addHole(Arrays.asList(new LatLng(35.163849, 136.904112), new LatLng(35.163812, 136.904131), new LatLng(35.163851, 136.904155), new LatLng(35.163872, 136.904129), new LatLng(35.163849, 136.904112)));



เอาคร่าวๆเท่านี้ล่ะกัน ต่อไปก็เป็นตัวอย่างในบทความนี้ล่ะ

Main.java



1. ประกาศ mMap จากคลาส GoogleMap แล้วกำหนดค่าเป็น R.id.map ที่สร้างไว้ใน Layout

2. เลื่อนมุมกล้องไปยังพิกัด 35.16375, 136.9044 แล้วซูมไปที่ระดับ 19

3. สร้าง Polyline โดยกำหนดเส้นเป็นสี่เหลี่ยม เส้นหนา 3px, zIndex เท่ากับ 17
และกำหนดสีของเส้น Polyline เป็น RGB รหัส #239299 (สีฟ้าอมเขียวเข้ม)

4. กำหนด Polyline ให้กับ mMap เป็น rectLine ที่ได้สร้างขึ้นในข้อ 3

5. สร้าง Polygon เป็นรูปสี่เหลี่ยม เส้นหนา 3px เส้นขอบสีดำ
และกำหนดสีพื้นผิวเป็น RGB รหัส #50239299 (สีฟ้าหม่นโปร่งใส)

6. กำหนด Polygon ให้กับ mMap เป็น rectGon ที่ได้สร้างขึ้นในข้อ 5

7. สร้าง Polygon โดยใช้ rectGon ที่เคยประกาศไว้แล้ว นำมาสร้างใหม่
เป็นรูป 7 เหลี่ยม พื้นผิวสีแดง และเส้นขอบสีดำมีความหนา 3 px
และเจาะรูเป็นสี่เหลี่ยมบนพื้นผิวของรูป 7 เหลี่ยม

8. กำหนด Polygon ให้กับ mMap เป็น rectGon ที่ได้สร้างขึ้นในข้อ 7


พอลองทดสอบดูก็จะเห็น Polyline และ Polygon ที่ได้สร้างขึ้นมา




Main.java
package app.akexorcist.googlemapsv2poly;

import java.util.Arrays;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import android.os.Bundle;
import android.graphics.Color;
import android.support.v4.app.FragmentActivity;

public class Main extends FragmentActivity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        GoogleMap mMap = ((SupportMapFragment)getSupportFragmentManager()
                .findFragmentById(R.id.map)).getMap();

        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                new LatLng(35.16375, 136.9044), 19));
        
        
        //************** Polyline **************//
        
        PolylineOptions rectLine = new PolylineOptions()
                .add(new LatLng(35.1638, 136.90435))
                .add(new LatLng(35.1637, 136.90435)) 
                .add(new LatLng(35.1637, 136.90445))
                .add(new LatLng(35.1638, 136.90445))
                .add(new LatLng(35.1638, 136.90435))
                .width(3).zIndex(17)
                .color(Color.rgb(0x23, 0x92, 0x99));

        mMap.addPolyline(rectLine);
        
        
        //************** Polygon **************//
        
        PolygonOptions rectGon = new PolygonOptions()
                .add(new LatLng(35.1639, 136.90455),
                new LatLng(35.1636, 136.90455),
                new LatLng(35.1636, 136.90475),
                new LatLng(35.1639, 136.90475),
                new LatLng(35.1639, 136.90455))
                .strokeColor(Color.BLACK)
                .fillColor(Color.argb(0x50, 0x23, 0x92, 0x99))
                .strokeWidth(3);
        
        mMap.addPolygon(rectGon);
        
        rectGon = new PolygonOptions()
                .add(new LatLng(35.163915, 136.904080),
                new LatLng(35.163889, 136.904053),
                new LatLng(35.163843, 136.904038),
                new LatLng(35.163779, 136.904101),
                new LatLng(35.163798, 136.904201),
                new LatLng(35.163872, 136.904218),
                new LatLng(35.163923, 136.904182),
                new LatLng(35.163915, 136.904080))
                .strokeColor(Color.BLACK)
                .fillColor(Color.RED)
                .strokeWidth(3)
                .addHole(Arrays.asList(new LatLng(35.163849, 136.904112),
                        new LatLng(35.163812, 136.904131),
                        new LatLng(35.163851, 136.904155),
                        new LatLng(35.163872, 136.904129),
                        new LatLng(35.163849, 136.904112)));

        mMap.addPolygon(rectGon);
    }
}

main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.SupportMapFragment"/>

</RelativeLayout>

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="app.akexorcist.googlemapsv2poly"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />
    
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name=
        "com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name=
        "app.akexorcist.googlemapsv2poly.permission.MAPS_RECEIVE"/>
      
    <permission 
        android:name="app.akexorcist.googlemapsv2poly.permission.MAPS_RECEIVE"
        android:protectionLevel="signature"/>
    
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="app.akexorcist.googlemapsv2poly.Main"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
           android:name="com.google.android.maps.v2.API_KEY"
           android:value="your_api_key"/>
    </application>
</manifest>


สำหรับผู้ที่หลงเข้ามาอ่านคนใดที่ต้องการไฟล์ตัวย่าง
สามารถดาวน์โหลดได้จาก Google Maps V2 Poly

เมื่อดาวน์โหลดตัวอย่างไป Import แล้วให้ตรวจสอบตามนี้
• ใส่ API Key ที่ไปขอจาก Google API ลงใน AndroidManifest.xml
• เลือก Library ของ google-play-services_lib ให้ตรงกับเครื่อง

อาจจะมีปัญหาพอสมควรสำหรับผู้ที่ดาวน์โหลดไปทดสอบ
เพราะว่าการใช้ Google Maps นี้มีการขอ API Key และ Library
ดังนั้นจึงควรตรวจสอบให้แน่ใจว่าได้กำหนดไว้ถูกต้องแล้ว 
และก็อย่าลืมว่าทดสอบบน AVD หรือ Emulator ไม่ได้นะ
เพราะต้องมี Play Store ซึ่ง AVD ไม่มี ทำให้แอป Force Close





เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium