24 July 2018

เมื่อทำ Google Maps แล้วอยากจะเช็คว่าพิกัดนั้นๆอยู่ในพื้นที่ที่กำหนดไว้หรือป่าว?

Updated on

        เรื่องมีอยู๋ว่าเจ้าของบล็อกต้องการจะเช็คว่า LatLng ที่ได้จาก Google Maps เนี่ย มันอยู่ใน Area ที่เจ้าของบล็อกต้องการหรือป่าว ก็เลยเป็นที่มาของบทความนี้นั่นเอง

        ลองนึกภาพว่าผู้ที่หลงเข้ามาอ่านกำลังทำอะไรซักอย่างโดยมีการดึงตำแหน่งละติจูดและลองจิจูดมาเช็คดูว่าตำแหน่งนั้นๆอยู่ในพื้นที่ที่กำหนดไว้หรือป่าว

        ถ้าพื้นที่ที่กำหนดไว้มีลักษณะเป็นวงกลมก็คงจะดีไม่น้อย เพราะว่าเจ้าของบล็อกก็สามารถคำนวณได้จากระยะห่างระหว่างจุดกึ่งกลางของวงกลมได้เลย


        หรือถ้าพื้นที่ที่ว่านั้นเป็นสี่เหลี่ยมผืนผ้าหรือจัตุรัส เจ้าของบล็อกก็สามารถใช้คลาส LatLngBound ที่อยู่ใน Google Maps API เพื่อเช็คได้เลย


        เพราะคลาส LatLngBound สามารถโยนพิกัดเข้าไปได้ตามใจชอบ แล้วสามารถใช้คำสั่ง contains(...) เพื่อเช็คได้ว่าพิกัดที่เจ้าของบล็อกสงสัยนั้นอยู่ในพื้นที่ของสี่เหลียมนี้หรือป่าว

val location: LatLng = ...
val northEast = LatLng(13.71769622, 100.53270958)
val southWest = LatLng(13.70180905, 100.51446519)
val latLngBounds = LatLngBounds.Builder().apply {
    include(northEast)
    include(southWest)
}.build()
val isInside: Boolean = latLngBounds.contains(location)

         แต่ทว่า...

ถ้าพื้นที่ตรงนี้มีรูปทรงที่ไม่แน่นอนล่ะ?

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


        เรื่องมันเศร้าตรงนี้เจ้าของบล็อกหาใน Google Maps API ไม่เจอว่ามีคลาสไหนบ้างในนี้ที่จะช่วยให้เจ้าของบล็อกเช็คได้ว่าพิกัดอยู่ในพื้นที่หน้าตาแบบนี้หรือป่าว

        ในกรณีพื้นที่ดังกล่าวเป็นตำบล, อำเภอ, จังหวัด หรือประเทศ ก็สามารถใช้ Geocoder เข้ามาช่วยได้ (ส่งพิกัดไปแล้วได้เป็นที่อยู่ของตำแหน่งนั้นๆออกมา) และข้อเสียอย่างหนึ่งก็คือต้องเชื่อมต่อกับอินเตอร์เน็ตด้วย แต่ถ้าสามารถเช็คผ่านโค้ดเลยล่ะ ขอแค่มีข้อมูลพื้นที่ดังกล่าวก็พอ มันก็ไวกว่าใช่มั้ยล่ะ? สุดท้ายก็เลยต้องเขียนเช็คเอง

         ถ้าลองมองดีๆแล้วพื้นที่ดังกล่าวหรือพิกัดที่เจ้าของบล็อกต้องการเช็คนั้นก็ไม่ต่างอะไรกับ 2D Object ที่มองว่าพิกัดคือ Point ส่วนพื้นที่นั้นคือ Polygon ดังนั้นเจ้าของบล็อกจึงสามารถใช้วิธีเดียวกับการเช็คว่า Point อยู่ใน Polygon หรือไม่ จึงไปเจอคำตอบอยู่ที่ How to determine if a point is inside a 2D convex polygon? [StackOverflow]

        เมื่อทำการแปลงคำตอบที่ได้ให้อยู่ในรูปที่เหมาะสมกับ Google Maps API และภาษา Kotlin ซะ ก็จะได้ออกมาหน้าตาประมาณนี้

fun isInside(area: List<LatLng>, location: LatLng): Boolean {
    var x = 0
    var y = area.size - 1
    var result = false
    while (x < area.size) {
        if (area[x].longitude > location.longitude != area[y].longitude > location.longitude &&
                location.latitude < (area[y].latitude - area[x].latitude) * (location.longitude - area[x].longitude) / (area[y].longitude - area[x].longitude) + area[x].latitude) {
            result = !result
        }
        y = x++
    }
    return result
}

         โดย area เป็น List<LatLng> ของพื้นที่ที่กำหนด และ location เป็น LatLng ของพิกัดที่ต้องการเช็ค

         เพียงเท่านี้เจ้าของบล็อกก็สามารถเช็คได้ง่ายๆแล้วว่าพิกัดดังกล่าวอยู่ในพื้นที่ที่กำหนดหรือป่าว สามารถนำไปใช้กับพื้นที่แบบไหนก็ได้ขอแค่แปลงข้อมูลให้อยู่ในรูป List<LatLng> ก่อนก็พอ และที่สำคัญก็คือไม่จำเป็นต้องใช้อินเตอร์เน็ตแบบ Geocoder ดังนั้นวิธีนี้จะให้ผลลัพธ์ที่ไวกว่าแน่นอน

        จบจ้า