لایه آنلاین – کاتلین

مستندات جامع اندروید

نسخه 1.0.3

هدف از این بخش از پروژه اضافه کردن یک لایه آنلاین به نقشه است.

فهرست مطالب این صفحه

لایه آنلاین مورد استفاده در این پروژه در آدرس زیر قرار دارد:

اطلاعات موجود در این فایل geojson به صورت زیر است:

            "type": "FeatureCollection",
"name": "points",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.368444458834389, 35.722048959450113 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.384140003141582, 35.778596997768005 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.456522296148684, 35.663707775556659 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.373137257878632, 35.656614228822541 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.527715399382672, 35.737503116119001 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.25037627592824, 35.74785430829283 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.297536031713946, 35.676263156219797 ] } },
{ "type": "Feature", "properties": { }, "geometry": { "type": "Point", "coordinates": [ 51.461240697178525, 35.725292478576648 ] } }
]
}
        

activity_online_layer.xml:

در این صفحه علاوه بر المان نقشه نشان، یک ToggleButton وجود دارد که با هر بار لمس شدنش، متد toggleOnlineLayer صدا زده می‌شود و لایه آنلاین نمایش داده می‌شود و یا در صورتی که در حال نمایش باشد حذف می‌شود.

            <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".activity.OnlineLayer">

    <org.neshan.mapsdk.MapView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/map"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

    <ToggleButton
        android:checked="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/toggle_button_text_color"
        android:textOff="لایه آنلاین"
        android:textOn="لایه آنلاین"
        android:elevation="8dp"
        android:paddingStart="8dp"
        android:paddingEnd="8dp"
        android:drawableStart="@drawable/ic_online_layer"
        android:drawableTint="@color/toggle_button_text_color"
        android:drawablePadding="8dp"
        android:background="@drawable/toggle_button_bg"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        tools:targetApi="m"
        android:onClick="toggleOnlineLayer"/>

</android.support.constraint.ConstraintLayout>
        

DownloadTask.kt:

این task که AsyncTask را extend می‌کند برای بارگیری فایل geojson نقاط نوشته شده‌است.

جزئیات نحوه پیاده‌سازی این task به صورت کامنت در متن کد نوشته شده‌است.

            package org.neshan.kotlinsample.task

import android.os.AsyncTask
import org.json.JSONObject
import org.json.JSONException
import java.io.BufferedReader
import java.io.InputStreamReader
import java.lang.Exception
import java.lang.StringBuilder
import java.net.HttpURLConnection
import java.net.URL

class DownloadTask(// hold reference of activity to emit messages
    private val callback: Callback) : AsyncTask<String?, Void?, JSONObject?>() {

    override fun doInBackground(vararg p0: String?): JSONObject? {
        val response = downloadRawFile(p0[0])
        try {
            // convert raw to JSON object
            return JSONObject(response)
        } catch (e: JSONException) {
            e.printStackTrace()
        }
        return null
    }

    override fun onPostExecute(jsonObject: JSONObject?) {
        super.onPostExecute(jsonObject)
        callback.onJsonDownloaded(jsonObject)
    }

    // download file from link (in this sample link is https://api.neshan.org/points.geojson)
    private fun downloadRawFile(link: String?): String? {
        val response = StringBuilder()
        try {
            //Prepare the URL and the connection
            val u = URL(link)
            val conn = u.openConnection() as HttpURLConnection
            if (conn.responseCode == HttpURLConnection.HTTP_OK) {
                //Get the Stream reader ready
                val input = BufferedReader(InputStreamReader(conn.inputStream), 8192)
                //Loop through the return data and copy it over to the response object to be processed
                var line: String?
                while (input.readLine().also { line = it } != null) {
                    response.append(line)
                }
                input.close()
            }
            return response.toString()
        } catch (ex: Exception) {
            ex.printStackTrace()
        }
        return null
    }

    interface Callback {
        fun onJsonDownloaded(jsonObject: JSONObject?)
    }
}
        

OnlineLayerActivity.kt:

متد initLayoutRefrences جهت مقداردهی اولیه کردن به تمامی المان‌های مربوط به رابط کاربری نوشته شده‌است. به دلیل این که لازم است تا المان اندرویدی نقشه نشان ابتدا به طور کامل ایجاد شود و سپس با آن تعامل برقرار شود (متدهای مختلف بر روی المان نقشه اجرا شود)، تمامی متدهای مربوط به رابط کاربری باید در متد onStart انجام شوند.

            override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_online_layer)
    }

    override fun onStart() {
        super.onStart()
        // everything related to ui is initialized here
        initLayoutReferences()
    }
        

پس از مقداردهی اولیه Viewهای رابط کاربری و نقشه، با استفاده از متد checkInternet – که در ادامه توضیح داده خواهد شد – درستی ارتباط با اینترنت بررسی می‌شود و در صورت وجود ارتباط صحیح، یک شی DownloadTask ایحاد می‌شود و متد execute بر روی آن اجرا می‌شود. به عنوان ورودی به این متد، آدرس فایل geojson داده می‌شود.

            // Initializing layout references (views, map and map events)
    private fun initLayoutReferences() {
        // Initializing views
        initViews()
        // Initializing mapView element
        initMap()

        if (checkInternet()) {
            val downloadTask = DownloadTask(this@OnlineLayerActivity)
            downloadTask.execute("https://api.neshan.org/points.geojson")
        }
    }
        

این متد در دسترس بودن اتصال اینترنت را بررسی می‌کند.

            // Check for Internet connectivity.
    private fun checkInternet(): Boolean {
        val connectivityManager =
            (this.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager)
        val networkInfo = connectivityManager.activeNetworkInfo
        return networkInfo != null
    }
        

این متد در مختصات داده‌شده به آن، یک نشان‌گر نمایش می‌دهد. توضیحات مربوط به این متد در صفحه لایه ترافیک داده شده است.

            // This method gets a LatLng as input and adds a marker on that position
    private fun addMarker(loc: LatLng): Marker {
        // Creating animation for marker. We should use an object of type AnimationStyleBuilder, set
        // all animation features on it and then call buildStyle() method that returns an object of type
        // AnimationStyle
        val animStBl = AnimationStyleBuilder()
        animStBl.fadeAnimationType = AnimationType.ANIMATION_TYPE_SMOOTHSTEP
        animStBl.sizeAnimationType = AnimationType.ANIMATION_TYPE_SPRING
        animStBl.phaseInDuration = 0.5f
        animStBl.phaseOutDuration = 0.5f
        val animSt = animStBl.buildStyle()

        // Creating marker style. We should use an object of type MarkerStyleCreator, set all features on it
        // and then call buildStyle method on it. This method returns an object of type MarkerStyle
        val markStCr = MarkerStyleBuilder()
        markStCr.size = 30f
        markStCr.bitmap = BitmapUtils.createBitmapFromAndroidBitmap(
            BitmapFactory.decodeResource(
                resources, R.drawable.ic_marker
            )
        )
        // AnimationStyle object - that was created before - is used here
        markStCr.animationStyle = animSt
        val markSt = markStCr.buildStyle()

        // Creating marker
        val marker = Marker(loc, markSt)

        // Adding marker to markerLayer, or showing marker on map!
        map.addMarker(marker)
        return marker
    }
        

بعد از اتمام دانلود فایل geojson، json موجود در آن پردازش شده و به ازای هر مختصات موجود در آن، یک بار متد addMarker صدا زده می‌شود و نشان‌گری بر روی نقشه در آن نقطه نمایش داده می‌شود.

برای محدود کردن ناحیه‌ای از نقشه که قرار است نمایش داده شود، متد moveToCameraBounds بر روی map صدا زده می‌شود. ورودی اول این متد، یک شی از نوع LatLngBounds است که مختصات نقطه شمال شرق و جنوب غرب به عنوان ورودی به آن داده می‌شود. دومین آرگومان ورودی این متد، یک ScreenBounds است که دو ورودی از نوع ScreenPos به آن داده می‌شود که اولین ورودی نقطه ۰ و ۰ است و دومین ورودی اندازه عرض و طول نقشه است. به این ترتیب زوم و مکان نقشه به شکلی تنظیم میشود که محدوده مختصات مشخص شده ( پارامتر اول ) در کل بخش نمایشی نقشه ( پارامتر دوم ) به نمایش در بیاید.

                override fun onJsonDownloaded(jsonObject: JSONObject?) {
        try {
            val features = jsonObject?.getJSONArray("features")
            // variable for creating bound
            // min = south-west
            // max = north-east
            var minLat = Double.MAX_VALUE
            var minLng = Double.MAX_VALUE
            var maxLat = Double.MIN_VALUE
            var maxLng = Double.MIN_VALUE
            for (i in 0 until features!!.length()) {
                val geometry = features.getJSONObject(i)?.getJSONObject("geometry")
                val coordinates = geometry?.getJSONArray("coordinates")
                val LatLng = LatLng(coordinates?.getDouble(1)!!, coordinates.getDouble(0))

                // validating min and max
                minLat = Math.min(LatLng.latitude, minLat)
                minLng = Math.min(LatLng.longitude, minLng)
                maxLat = Math.max(LatLng.latitude, maxLat)
                maxLng = Math.max(LatLng.longitude, maxLng)
                markers.add(addMarker(LatLng))
            }
            map.moveToCameraBounds(
                LatLngBounds(LatLng(minLat, minLng), LatLng(maxLat, maxLng)),
                ScreenBounds(
                    ScreenPos(0f, 0f),
                    ScreenPos(map.width.toFloat(), map.height.toFloat())
                ),
                true, 0.25f
            )
        } catch (e: JSONException) {
            e.printStackTrace()
        }
    }

        

متدtoggleOnlineLayer زمانی فراخوانی می‌شود که کاربر ToggleButton موجود در رابط کاربری را لمس کند.

در صورتی که این دکمه انتخاب شده باشد، پس از بررسی در دسترس بودن اینترنت با استفاده از متد checkInternet یک DownloadTask ایجاد شده و متد execute با آرگومان ورودی آدرس فایل geojson به آن داده می‌شود. در صورتی که این دکمه از حالت انتخاب خارج شود، لایه مربوط به نشانگرها از نقشه حذف می‌شود.

            //Download markers and add to map
    fun toggleOnlineLayer(view: View) {
        val toggleButton = view as ToggleButton
        if (toggleButton.isChecked) {
            if (checkInternet()) {
                val downloadTask = DownloadTask(this@OnlineLayerActivity)
                downloadTask.execute("https://api.neshan.org/points.geojson")
            }
        } else {
            for (marker in markers) {
                map.removeMarker(marker)
            }
        }
    }