Создание источника данных (пакет SDK для Android)

Пакет SDK Azure Maps для Android хранит данные в источниках данных. Использование источников данных оптимизирует операции с данными при выполнении запросов и отрисовке. В настоящее время существует два типа источников данных:

  • Источник GeoJSON: локально управляет необработанными данными о местоположении в формате GeoJSON. Подходит для небольших и средних наборов данных (до сотен тысяч фигур).
  • Источник векторных листов: загружает данные, отформатированные в виде векторных фрагментов для текущего представления карты, на основе системы разбиения карты на фрагменты. Идеально подходит для больших и огромных наборов данных (миллионы или миллиарды фигур).

Примечание.

Прекращение использования пакета SDK для Android для Azure Карты

Пакет SDK для машинного кода Azure для Android Карты теперь устарел и будет прекращен 3.31.25. Чтобы избежать сбоев в работе служб, перейдите в веб-пакет SDK для Azure Карты на 3.31.25. Дополнительные сведения см. в руководстве по миграции пакета SDK для Android Карты Azure.

Источник данных GeoJSON

Azure Maps использует GeoJSON в качестве одной из основных моделей данных. GeoJSON — это открытый геопространственный стандарт для представления геопространственных данных в формате JSON. В пакете SDK Azure Maps для Android доступны классы GeoJSON, которые упрощают создание и сериализацию данных GeoJSON. Загружайте и храните данные GeoJSON в классе DataSource и отрисовывайте их с помощью слоев. В примере кода ниже показано, как создавать объекты GeoJSON в Azure Maps.

/*
    Raw GeoJSON feature

    {
         "type": "Feature",
         "geometry": {
             "type": "Point",
             "coordinates": [-100, 45]
         },
         "properties": {
             "custom-property": "value"
         }
    }

*/

//Create a point feature.
Feature feature = Feature.fromGeometry(Point.fromLngLat(-100, 45));

//Add a property to the feature.
feature.addStringProperty("custom-property", "value");

//Add the feature to the data source.
source.add(feature);
/*
    Raw GeoJSON feature

    {
         "type": "Feature",
         "geometry": {
             "type": "Point",
             "coordinates": [-100, 45]
         },
         "properties": {
             "custom-property": "value"
         }
    }

*/

//Create a point feature.
val feature = Feature.fromGeometry(Point.fromLngLat(-100, 45))

//Add a property to the feature.
feature.addStringProperty("custom-property", "value")

//Add the feature to the data source.
source.add(feature)

Совет

Данные GeoJSON можно добавить в экземпляр DataSource с помощью любого из трех методов: add, importDataFromUrl и setShapes. Метод setShapes предоставляет эффективный способ перезаписи всех данных в источнике данных. Если вы вызываете метод clear, а затем add, чтобы заменить все данные в источнике данных, к карте осуществляются два вызова отрисовки. Метод setShape очищает и добавляет данные в источник данных с помощью одного вызова отрисовки к карте.

Кроме того, свойства можно загрузить в JsonObject, а затем передать в функцию при его создании, как показано в следующем примере кода.

//Create a JsonObject to store properties for the feature.
JsonObject properties = new JsonObject();
properties.addProperty("custom-property", "value");

Feature feature = Feature.fromGeometry(Point.fromLngLat(-100, 45), properties);
//Create a JsonObject to store properties for the feature.
val properties = JsonObject()
properties.addProperty("custom-property", "value")

val feature = Feature.fromGeometry(Point.fromLngLat(-100, 45), properties)

После создания объекта GeoJSON источник данных можно добавить на карту с помощью свойства sources карты. В следующем коде показано, как создать источник данных (DataSource), добавить его на карту, а затем добавить в него объект.

//Create a data source and add it to the map.
DataSource source = new DataSource();
map.sources.add(source);

//Add GeoJSON feature to the data source.
source.add(feature);

В следующем коде показано несколько способов создания объекта (Feature), коллекции объектов (FeatureCollection) и геометрий GeoJSON.

//GeoJSON Point Geometry
Point point = Point.fromLngLat(LONGITUDE, LATITUDE);

//GeoJSON Point Geometry
LineString linestring = LineString.fromLngLats(PointList);

//GeoJSON Polygon Geometry
Polygon polygon = Polygon.fromLngLats(listOfPointList);

Polygon polygonFromOuterInner = Polygon.fromOuterInner(outerLineStringObject,innerLineStringObject);

//GeoJSON MultiPoint Geometry
MultiPoint multiPoint = MultiPoint.fromLngLats(PointList);

//GeoJSON MultiLineString Geometry
MultiLineString multiLineStringFromLngLat = MultiLineString.fromLngLats(listOfPointList);

MultiLineString multiLineString = MultiLineString.fromLineString(singleLineString);

//GeoJSON MultiPolygon Geometry
MultiPolygon multiPolygon = MultiPolygon.fromLngLats(listOflistOfPointList);

MultiPolygon multiPolygonFromPolygon = MultiPolygon.fromPolygon(polygon);

MultiPolygon multiPolygonFromPolygons = MultiPolygon.fromPolygons(PolygonList);

//GeoJSON Feature
Feature pointFeature = Feature.fromGeometry(Point.fromLngLat(LONGITUDE, LATITUDE));

//GeoJSON FeatureCollection 
FeatureCollection featureCollectionFromSingleFeature = FeatureCollection.fromFeature(pointFeature);

FeatureCollection featureCollection = FeatureCollection.fromFeatures(listOfFeatures);
//GeoJSON Point Geometry
val point = Point.fromLngLat(LONGITUDE, LATITUDE)

//GeoJSON Point Geometry
val linestring = LineString.fromLngLats(PointList)

//GeoJSON Polygon Geometry
val polygon = Polygon.fromLngLats(listOfPointList)

val polygonFromOuterInner = Polygon.fromOuterInner(outerLineStringObject, innerLineStringObject)

//GeoJSON MultiPoint Geometry
val multiPoint = MultiPoint.fromLngLats(PointList)

//GeoJSON MultiLineString Geometry
val multiLineStringFromLngLat = MultiLineString.fromLngLats(listOfPointList)

val multiLineString = MultiLineString.fromLineString(singleLineString)

//GeoJSON MultiPolygon Geometry
val multiPolygon = MultiPolygon.fromLngLats(listOflistOfPointList)

val multiPolygonFromPolygon = MultiPolygon.fromPolygon(polygon)

val multiPolygonFromPolygons = MultiPolygon.fromPolygons(PolygonList)

//GeoJSON Feature
val pointFeature = Feature.fromGeometry(Point.fromLngLat(LONGITUDE, LATITUDE))

//GeoJSON FeatureCollection 
val featureCollectionFromSingleFeature = FeatureCollection.fromFeature(pointFeature)

val featureCollection = FeatureCollection.fromFeatures(listOfFeatures)

Сериализация и десериализация GeoJSON

Классы коллекций объектов, объектов и геометрий имеют статические методы fromJson() и toJson(), которые помогают выполнить сериализацию. Отформатированная допустимая строка JSON, передаваемая методом fromJson() , создает геометрический объект. Этот метод fromJson() также означает, что вы можете использовать Gson или другие стратегии сериализации и десериализации. В следующем коде показано, как десериализировать переведенный в строку объект GeoJSON в класс Feature, а затем сериализовать обратно в строку GeoJSON.

//Take a stringified GeoJSON object.
String GeoJSON_STRING = "{"
    + "      \"type\": \"Feature\","            
    + "      \"geometry\": {"
    + "            \"type\": \"Point\""
    + "            \"coordinates\": [-100, 45]"
    + "      },"
    + "      \"properties\": {"
    + "            \"custom-property\": \"value\""
    + "      },"
    + "}";

//Deserialize the JSON string into a feature.
Feature feature = Feature.fromJson(GeoJSON_STRING);

//Serialize a feature collection to a string.
String featureString = feature.toJson();
//Take a stringified GeoJSON object.
val GeoJSON_STRING = ("{"
        + "      \"type\": \"Feature\","
        + "      \"geometry\": {"
        + "            \"type\": \"Point\""
        + "            \"coordinates\": [-100, 45]"
        + "      },"
        + "      \"properties\": {"
        + "            \"custom-property\": \"value\""
        + "      },"
        + "}")

//Deserialize the JSON string into a feature.
val feature = Feature.fromJson(GeoJSON_STRING)

//Serialize a feature collection to a string.
val featureString = feature.toJson()

Импорт данных GeoJSON из Интернета или из папки assets

Большинство файлов GeoJSON содержит коллекцию объектов (FeatureCollection). Считывайте файлы GeoJSON в виде строк и используйте метод FeatureCollection.fromJson для десериализации.

Класс DataSource имеет встроенный метод importDataFromUrl , который может загружаться в файлы GeoJSON с помощью URL-адреса файла в Интернете или в папке ресурса. Этот метод нужно обязательно вызвать, прежде чем добавлять источник данных на карту.

zone_pivot_groups: azure-maps-android

//Create a data source and add it to the map.
DataSource source = new DataSource();

//Import the geojson data and add it to the data source.
source.importDataFromUrl("URL_or_FilePath_to_GeoJSON_data");

//Examples:
//source.importDataFromUrl("asset://sample_file.json");
//source.importDataFromUrl("https://example.com/sample_file.json");

//Add data source to the map.
map.sources.add(source);
//Create a data source and add it to the map.
var source = new DataSource()

//Import the geojson data and add it to the data source.
source.importDataFromUrl("URL_or_FilePath_to_GeoJSON_data")

//Examples:
//source.importDataFromUrl("asset://sample_file.json")
//source.importDataFromUrl("https://example.com/sample_file.json")

//Add data source to the map.
map.sources.add(source)

Этот importDataFromUrl метод предоставляет простой способ загрузки веб-канала GeoJSON в источник данных, но предоставляет ограниченный контроль над загрузкой данных и тем, что происходит после загрузки. Следующий код является повторно используемым классом для импорта данных из Интернета или папки ресурсов и их возврата в поток пользовательского интерфейса с помощью функции обратного вызова. Затем добавьте дополнительную логику после загрузки в обратный вызов для обработки данных, добавьте его в карту, вычислите ограничивающий прямоугольник и обновите камеру карт.

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.webkit.URLUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.HttpsURLConnection;

public class Utils {

    interface SimpleCallback {
        void notify(String result);
    }

    /**
     * Imports data from a web url or asset file name and returns it to a callback.
     * @param urlOrFileName A web url or asset file name that points to data to load.
     * @param context The context of the app.
     * @param callback The callback function to return the data to.
     */
    public static void importData(String urlOrFileName, Context context, SimpleCallback callback){
        importData(urlOrFileName, context, callback, null);
    }

    /**
     * Imports data from a web url or asset file name and returns it to a callback.
     * @param urlOrFileName A web url or asset file name that points to data to load.
     * @param context The context of the app.
     * @param callback The callback function to return the data to.
     * @param error A callback function to return errors to.
     */
    public static void importData(String urlOrFileName, Context context, SimpleCallback callback, SimpleCallback error){
        if(urlOrFileName != null && callback != null) {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Handler handler = new Handler(Looper.getMainLooper());

            executor.execute(() -> {
                String data = null;

                try {

                    if(URLUtil.isNetworkUrl(urlOrFileName)){
                        data = importFromWeb(urlOrFileName);
                    } else {
                        //Assume file is in assets folder.
                        data = importFromAssets(context, urlOrFileName);
                    }

                    final String result = data;

                    handler.post(() -> {
                        //Ensure the resulting data string is not null or empty.
                        if (result != null && !result.isEmpty()) {
                            callback.notify(result);
                        } else {
                            error.notify("No data imported.");
                        }
                    });
                } catch(Exception e) {
                    if(error != null){
                        error.notify(e.getMessage());
                    }
                }
            });
        }
    }

    /**
     * Imports data from an assets file as a string.
     * @param context The context of the app.
     * @param fileName The asset file name.
     * @return
     * @throws IOException
     */
    private static String importFromAssets(Context context, String fileName) throws IOException {
        InputStream stream = null;

        try {
            stream = context.getAssets().open(fileName);

            if(stream != null) {
                return readStreamAsString(stream);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Close Stream and disconnect HTTPS connection.
            if (stream != null) {
                stream.close();
            }
        }

        return null;
    }

    /**
     * Imports data from the web as a string.
     * @param url URL to the data.
     * @return
     * @throws IOException
     */
    private static String importFromWeb(String url) throws IOException {
        InputStream stream = null;
        HttpsURLConnection connection = null;
        String result = null;

        try {
            connection = (HttpsURLConnection) new URL(url).openConnection();

            //For this use case, set HTTP method to GET.
            connection.setRequestMethod("GET");

            //Open communications link (network traffic occurs here).
            connection.connect();

            int responseCode = connection.getResponseCode();
            if (responseCode != HttpsURLConnection.HTTP_OK) {
                throw new IOException("HTTP error code: " + responseCode);
            }

            //Retrieve the response body as an InputStream.
            stream = connection.getInputStream();

            if (stream != null) {
                return readStreamAsString(stream);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Close Stream and disconnect HTTPS connection.
            if (stream != null) {
                stream.close();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }

        return result;
    }

    /**
     * Reads an input stream as a string.
     * @param stream Stream to convert.
     * @return
     * @throws IOException
     */
    private static String readStreamAsString(InputStream stream) throws IOException {
        //Convert the contents of an InputStream to a String.
        BufferedReader in = new BufferedReader(new InputStreamReader(stream, "UTF-8"));

        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }

        in.close();

        return response.toString();
    }
}
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.webkit.URLUtil
import java.net.URL
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class Utils {
    companion object {

        /**
            * Imports data from a web url or asset file name and returns it to a callback.
            * @param urlOrFileName A web url or asset file name that points to data to load.
            * @param context The context of the app.
            * @param callback The callback function to return the data to.
            */
        fun importData(urlOrFileName: String?, context: Context, callback: (String?) -> Unit) {
            importData(urlOrFileName, context, callback, null)
        }

        /**
            * Imports data from a web url or asset file name and returns it to a callback.
            * @param urlOrFileName A web url or asset file name that points to data to load.
            * @param context The context of the app.
            * @param callback The callback function to return the data to.
            * @param error A callback function to return errors to.
            */
        public fun importData(urlOrFileName: String?, context: Context, callback: (String?) -> Unit, error: ((String?) -> Unit)?) {
            if (urlOrFileName != null && callback != null) {
                val executor: ExecutorService = Executors.newSingleThreadExecutor()
                val handler = Handler(Looper.getMainLooper())
                executor.execute {
                    var data: String? = null

                    try {
                        data = if (URLUtil.isNetworkUrl(urlOrFileName)) {
                            URL(urlOrFileName).readText()
                        } else { //Assume file is in assets folder.
                            context.assets.open(urlOrFileName).bufferedReader().use{
                                it.readText()
                            }
                        }

                        handler.post {
                            //Ensure the resulting data string is not null or empty.
                            if (data != null && !data.isEmpty()) {
                                callback(data)
                            } else {
                                error!!("No data imported.")
                            }
                        }
                    } catch (e: Exception) {
                        error!!(e.message)
                    }
                }
            }
        }
    }
}

В следующем коде показано, как использовать эту программу для импорта данных GeoJSON в виде строки и возврата его в поток пользовательского интерфейса с помощью обратного вызова. В обратном вызове строковые данные можно сериализовать в коллекцию объектов GeoJSON и добавить в источник данных. При необходимости обновите камеру карт, чтобы сосредоточиться на данных.

//Create a data source and add it to the map.
DataSource source = new DataSource();
map.sources.add(source);

//Import the geojson data and add it to the data source.
Utils.importData("URL_or_FilePath_to_GeoJSON_data",
    this,
    (String result) -> {
        //Parse the data as a GeoJSON Feature Collection.
        FeatureCollection fc = FeatureCollection.fromJson(result);

        //Add the feature collection to the data source.
        source.add(fc);

        //Optionally, update the maps camera to focus in on the data.

        //Calculate the bounding box of all the data in the Feature Collection.
        BoundingBox bbox = MapMath.fromData(fc);

        //Update the maps camera so it is focused on the data.
        map.setCamera(
            bounds(bbox),
            padding(20));
    });
//Create a data source and add it to the map.
DataSource source = new DataSource();
map.sources.add(source);

//Import the GeoJSON data and add it to the data source.
Utils.importData("SamplePoiDataSet.json", this) { 
    result: String? ->
        //Parse the data as a GeoJSON Feature Collection.
            val fc = FeatureCollection.fromJson(result!!)

        //Add the feature collection to the data source.
        source.add(fc)

        //Optionally, update the maps camera to focus in on the data.

        //Calculate the bounding box of all the data in the Feature Collection.
        val bbox = MapMath.fromData(fc);

        //Update the maps camera so it is focused on the data.
        map.setCamera(
            bounds(bbox),

            //Padding added to account for pixel size of rendered points.
            padding(20)
        )
    }

Обновление объекта

Класс DataSource упрощает добавление и удаление функций. Обновление геометрии или свойств объекта требует замены объекта в источнике данных. Для обновления объектов можно использовать два метода:

  1. Создайте новые объекты с нужными изменениями и замените все объекты в источнике данных с помощью метода setShapes. Этот метод удобно использовать, если нужно обновить все объекты в источнике данных.
DataSource source;

private void onReady(AzureMap map) {
    //Create a data source and add it to the map.
    source = new DataSource();
    map.sources.add(source);

    //Create a feature and add it to the data source.
    Feature myFeature = Feature.fromGeometry(Point.fromLngLat(0,0));
    myFeature.addStringProperty("Name", "Original value");

    source.add(myFeature);
}

private void updateFeature(){
    //Create a new replacement feature with an updated geometry and property value.
    Feature myNewFeature = Feature.fromGeometry(Point.fromLngLat(-10, 10));
    myNewFeature.addStringProperty("Name", "New value");

    //Replace all features to the data source with the new one.
    source.setShapes(myNewFeature);
}
var source: DataSource? = null

private fun onReady(map: AzureMap) {
    //Create a data source and add it to the map.
    source = DataSource()
    map.sources.add(source)

    //Create a feature and add it to the data source.
    val myFeature = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0))
    myFeature.addStringProperty("Name", "Original value")
    source!!.add(myFeature)
}

private fun updateFeature() {
    //Create a new replacement feature with an updated geometry and property value.
    val myNewFeature = Feature.fromGeometry(Point.fromLngLat(-10.0, 10.0))
    myNewFeature.addStringProperty("Name", "New value")

    //Replace all features to the data source with the new one.
    source!!.setShapes(myNewFeature)
}
  1. Следите за экземпляром объекта в переменной и передайте его в метод remove источников данных, чтобы удалить. Создайте новые объекты с нужными изменениями, обновите ссылку на переменную и добавьте ее в источник данных с помощью метода add.
DataSource source;
Feature myFeature;

private void onReady(AzureMap map) {
    //Create a data source and add it to the map.
    source = new DataSource();
    map.sources.add(source);

    //Create a feature and add it to the data source.
    myFeature = Feature.fromGeometry(Point.fromLngLat(0,0));
    myFeature.addStringProperty("Name", "Original value");

    source.add(myFeature);
}

private void updateFeature(){
    //Remove the feature instance from the data source.
    source.remove(myFeature);

    //Get properties from original feature.
    JsonObject props = myFeature.properties();

    //Update a property.
    props.addProperty("Name", "New value");

    //Create a new replacement feature with an updated geometry.
    myFeature = Feature.fromGeometry(Point.fromLngLat(-10, 10), props);

    //Re-add the feature to the data source.
    source.add(myFeature);
}
var source: DataSource? = null
var myFeature: Feature? = null

private fun onReady(map: AzureMap) {
    //Create a data source and add it to the map.
    source = DataSource()
    map.sources.add(source)

    //Create a feature and add it to the data source.
    myFeature = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0))
    myFeature.addStringProperty("Name", "Original value")
    source!!.add(myFeature)
}

private fun updateFeature() {
    //Remove the feature instance from the data source.
    source!!.remove(myFeature)

    //Get properties from original feature.
    val props = myFeature!!.properties()

    //Update a property.
    props!!.addProperty("Name", "New value")

    //Create a new replacement feature with an updated geometry.
    myFeature = Feature.fromGeometry(Point.fromLngLat(-10.0, 10.0), props)

    //Re-add the feature to the data source.
    source!!.add(myFeature)
}

Совет

Если есть данные, которые будут регулярно обновляться, и другие данные, которые будут изменяться редко, лучше включить их в отдельные экземпляры источника данных. Если в источнике данных выполняется обновление, карту перерисует все объекты в источнике данных. Если разделить данные, при обновлении одного источника данных будут перерисовываться только те объекты, которые регулярно обновляются, а объекты другого источника данных перерисовывать не нужно. Это помогает повысить производительность.

Источник векторных фрагментов

Источник векторных фрагментов описывает порядок доступа к слою векторных фрагментов. Используйте класс VectorTileSource для создания экземпляра источника векторных фрагментов. Слои векторных фрагментов похожи на обычные слои фрагментов, но несколько отличаются. Слой фрагментов — это растровое изображение. Слои векторных фрагментов — это сжатые файлы в формате PBF. Такой сжатый файл содержит данные векторной карты и один или несколько слоев. Этот файл может быть отрисован и настроен на стороне клиента с учетом стиля каждого слоя. Данные в векторном фрагменте содержат географические фигуры в виде точек, линий и многоугольников. Слои векторных фрагментов обладают рядом преимуществ по сравнению с растровыми векторными слоями.

  • Размер файла для векторного фрагмента обычно намного меньше, чем для эквивалентного растрового фрагмента. В связи с этим достаточно меньшей пропускной способности. Это означает меньшую задержку, ускоренную отрисовку карты и большее удобство для пользователей.
  • Поскольку векторные фрагменты отрисовываются на клиенте, они адаптируются к разрешению устройства, на котором отображаются. В результате отображаемые карты выглядят четче, а метки отлично читаются.
  • Изменение стиля данных в векторных картах не требует повторной загрузки данных, так как применить новый стиль можно на клиенте. И напротив, изменение стиля слоя растровых фрагментов, как правило, требует загрузки этих фрагментов с сервера, а затем применения нового стиля.
  • Поскольку данные передаются в векторной форме, для их подготовки на стороне сервера требуется меньше обработки. В результате новые данные становятся доступны быстрее.

Azure Maps соответствует открытому стандарту — спецификации векторных фрагментов Mapbox Vector. В составе платформы Azure Maps доступны следующие службы для работы с векторными фрагментами:

Совет

При использовании векторных или растровых фрагментов из службы отрисовки Azure Maps с пакетом SDK веб-версии atlas.microsoft.com можно заменять заполнителем azmapsdomain.invalid. Вместо этого заполнителя будет подставляться домен, который используется картой, и будут автоматически добавляться соответствующие данные для проверки подлинности. Это значительно упрощает проверку подлинности с помощью службы отрисовки при использовании проверки подлинности Microsoft Entra.

Чтобы отобразить данные из источника векторных фрагментов на карте, подключите источник к одному из слоев отрисовки данных. Для всех слоев, использующих векторный источник, в параметрах должно быть указано значение sourceLayer. В примере кода ниже служба векторных фрагментов дорожного движения Azure Maps загружается в качестве источника векторных фрагментов, а затем ее данные отображаются на карте в слое линий. Этот источник векторных фрагментов содержит один набор данных на исходном слое "Traffic flow". Данные линий в этом наборе содержат свойство traffic_level, которое используется в коде для выбора цвета и масштабирования линий.

//Formatted URL to the traffic flow vector tiles, with the maps subscription key appended to it.
String trafficFlowUrl = "https://azmapsdomain.invalid/traffic/flow/tile/pbf?api-version=1.0&style=relative&zoom={z}&x={x}&y={y}";

//Create a vector tile source and add it to the map.
VectorTileSource source = new VectorTileSource(
    tiles(new String[] { trafficFlowUrl }),
    maxSourceZoom(22)
);
map.sources.add(source);

//Create a layer for traffic flow lines.
LineLayer layer = new LineLayer(source,
    //The name of the data layer within the data source to pass into this rendering layer.
    sourceLayer("Traffic flow"),

    //Color the roads based on the traffic_level property.
    strokeColor(
        interpolate(
            linear(),
            get("traffic_level"),
            stop(0, color(Color.RED)),
            stop(0.33, color(Color.YELLOW)),
            stop(0.66, color(Color.GREEN))
        )
    ),

    //Scale the width of roads based on the traffic_level property.
    strokeWidth(
        interpolate(
            linear(),
            get("traffic_level"),
            stop(0, 6),
            stop(1,1)
        )
    )
);

//Add the traffic flow layer below the labels to make the map clearer.
map.layers.add(layer, "labels");
//Formatted URL to the traffic flow vector tiles, with the maps subscription key appended to it.
val trafficFlowUrl = "https://azmapsdomain.invalid/traffic/flow/tile/pbf?api-version=1.0&style=relative&zoom={z}&x={x}&y={y}"

//Create a vector tile source and add it to the map.
val source = VectorTileSource(
    tiles(arrayOf(trafficFlowUrl)),
    maxSourceZoom(22)
)
map.sources.add(source)

//Create a layer for traffic flow lines.
val layer = LineLayer(
    source,  //The name of the data layer within the data source to pass into this rendering layer.
    sourceLayer("Traffic flow"),  //Color the roads based on the traffic_level property.
    strokeColor(
        interpolate(
            linear(),
            get("traffic_level"),
            stop(0, color(Color.RED)),
            stop(0.33, color(Color.YELLOW)),
            stop(0.66, color(Color.GREEN))
        )
    ),  //Scale the width of roads based on the traffic_level property.
    strokeWidth(
        interpolate(
            linear(),
            get("traffic_level"),
            stop(0, 6),
            stop(1, 1)
        )
    )
)

//Add the traffic flow layer below the labels to make the map clearer.
map.layers.add(layer, "labels")

Карта с цветными полосами на дорогах, отображающими разные уровни интенсивности дорожного движения

Подключение источника данных к слою

Данные отрисовываются на карте с помощью слоев отрисовки. Один или несколько слоев отрисовки могут ссылаться на один источник данных. Источник данных требуется для следующих слоев отрисовки:

  • Слой выносок — служит для отрисовки на карте точек в виде масштабируемых кругов.
  • Слой символов отображает данные точки в виде значков или текста.
  • Слой тепловой карты — служит для отрисовки на карте точек в виде карты тепловой плотности.
  • Слой линий — служит для отрисовки линий и (или) контуров многоугольников.
  • Слой многоугольников — служит для заливки области многоугольника сплошным цветом или узором.

В примере кода ниже показано, как создать источник данных, добавить его на карту и подключить его к слою выносок. Затем в источник данных нужно импортировать данные точек GeoJSON из удаленного расположения.

//Create a data source and add it to the map.
DataSource source = new DataSource();

//Import the geojson data and add it to the data source.
source.importDataFromUrl("URL_or_FilePath_to_GeoJSON_data");

//Add data source to the map.
map.sources.add(source);

//Create a layer that defines how to render points in the data source and add it to the map.
BubbleLayer layer = new BubbleLayer(source);
map.layers.add(layer);
//Create a data source and add it to the map.
val source = DataSource()

//Import the geojson data and add it to the data source.
source.importDataFromUrl("URL_or_FilePath_to_GeoJSON_data")

//Add data source to the map.
map.sources.add(source)

Существует больше слоев отрисовки, которые не подключаются к этим источникам данных, но они напрямую загружают данные для отрисовки.

Один источник данных с несколькими слоями

К одному источнику данных можно подключить несколько слоев. Существует множество различных сценариев, в которых может быть полезен этот вариант. Например, рассмотрим ситуацию, в которой пользователь рисует многоугольник. Мы должны отрисовывать и заполнять область многоугольника при добавлении пользователем точек на карту. Добавив линию с настроенным стилем в качестве контура многоугольника, мы улучшим отображение его краев по мере того, как пользователь рисует. Чтобы пользователь мог легко изменить на многоугольнике ту или иную позицию, мы можем добавить над каждой позицией маркер (например, в виде булавки или другого значка).

Карта с несколькими слоями отрисовки данных из одного источника данных

На большинстве картографических платформ для каждой позиции многоугольника требуются объект многоугольника, объект лини и булавка. По мере изменения многоугольника необходимо вручную обновлять линию и булавки, что приводит к быстрому усложнению алгоритма.

При использовании Azure Карты все, что вам нужно, — это один многоугольник в источнике данных, как показано в следующем коде.

//Create a data source and add it to the map.
DataSource source = new DataSource();
map.sources.add(source);

//Create a polygon and add it to the data source.
source.add(Polygon.fromLngLats(/* List of points */));

//Create a polygon layer to render the filled in area of the polygon.
PolygonLayer polygonLayer = new PolygonLayer(source,
    fillColor("rgba(255,165,0,0.2)")
);

//Create a line layer for greater control of rendering the outline of the polygon.
LineLayer lineLayer = new LineLayer(source,
    strokeColor("orange"),
    strokeWidth(2f)
);

//Create a bubble layer to render the vertices of the polygon as scaled circles.
BubbleLayer bubbleLayer = new BubbleLayer(source,
    bubbleColor("orange"),
    bubbleRadius(5f),
    bubbleStrokeColor("white"),
    bubbleStrokeWidth(2f)
);

//Add all layers to the map.
map.layers.add(new Layer[] { polygonLayer, lineLayer, bubbleLayer });
//Create a data source and add it to the map.
val source = DataSource()
map.sources.add(source)

//Create a polygon and add it to the data source.
source.add(Polygon.fromLngLats())

//Create a polygon layer to render the filled in area of the polygon.
val polygonLayer = PolygonLayer(
    source,
    fillColor("rgba(255,165,0,0.2)")
)

//Create a line layer for greater control of rendering the outline of the polygon.
val lineLayer = LineLayer(
    source,
    strokeColor("orange"),
    strokeWidth(2f)
)

//Create a bubble layer to render the vertices of the polygon as scaled circles.
val bubbleLayer = BubbleLayer(
    source,
    bubbleColor("orange"),
    bubbleRadius(5f),
    bubbleStrokeColor("white"),
    bubbleStrokeWidth(2f)
)

//Add all layers to the map.
map.layers.add(arrayOf<Layer>(polygonLayer, lineLayer, bubbleLayer))

Совет

При добавлении слоев на карту с помощью метода map.layers.add в качестве второго параметра можно передать идентификатор или экземпляр существующего слоя. В результате карта должна будет вставить новый слой под существующим. Помимо передачи идентификатора слоя, этот метод также поддерживает перечисленные ниже параметры.

  • "labels" — вставляет новый слой под слоями меток карты.
  • "transit" — вставляет новый слой под слоями дорог и общественного транспорта на карте.

Следующие шаги

Дополнительные примеры кода для добавления в карты см. в следующих статьях: