إنشاء مصدر بيانات في iOS SDK (معاينة)

إشعار

خرائط Azure إيقاف iOS SDK

تم الآن إهمال خرائط Azure Native SDK لنظام التشغيل iOS وسيتم إيقافه في 3/31/25. لتجنب انقطاع الخدمة، قم بالترحيل إلى خرائط Azure Web SDK بحلول 3/31/25. لمزيد من المعلومات، راجع دليل ترحيل خرائط Azure iOS SDK.

تخزن Azure Maps iOS SDK البيانات في مصادر البيانات. يؤدي استخدام مصادر البيانات إلى تحسين عمليات البيانات للاستعلام والعرض. يوجد حاليًا نوعان من مصادر البيانات:

  • مصدر GeoJSON: يدير بيانات الموقع الأولية بتنسيق GeoJSON محليًا. جيد لمجموعات البيانات الصغيرة والمتوسطة (حتى مئات الآلاف من الأشكال).
  • مصدر صور مصغرة من الخريطة لخطوط المتجه: تحميل البيانات المنسقة على هيئة صور مصغرة من الخريطة لخطوط المتجه للخريطة الحالية، بناءً على نظام تقسيم الخرائط. مثالي لمجموعات البيانات الكبيرة إلى الضخمة (ملايين أو مليارات الأشكال).

مصدر بيانات GeoJSON

تستخدم خرائط Azure GeoJSON كأحد نماذج البيانات الأساسية. GeoJSON هي طريقة قياسية جغرافية مكانية مفتوحة لتمثيل البيانات الجغرافية المكانية بتنسيق JSON. تتوفر فئات GeoJSON في خرائط Azure iOS SDK لتسهيل إنشاء بيانات GeoJSON وتسلسلها. قم بتحميل وتخزين بيانات GeoJSON في فئة DataSource وقم بعرضها باستخدام الطبقات. توضح التعليمة البرمجية التالية كيف يمكن إنشاء عناصر GeoJSON في خرائط Azure.

/*
    Raw GeoJSON feature

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

*/

//Create a point feature.
let feature = Feature(Point(CLLocationCoordinate2D(latitude: 45, longitude: -100)))

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

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

بدلا من ذلك، يمكن تحميل الخصائص في قاموس (JSON) أولا ثم تمريرها إلى الميزة عند إنشائها، كما توضح التعليمات البرمجية التالية:

//Create a dictionary to store properties for the feature.
var properties: [String: Any] = [:]
properties["custom-property"] = "value"

let feature = Feature(Point(CLLocationCoordinate2D(latitude: 45, longitude: -100)), properties: properties)

بمجرد إنشاء ميزة GeoJSON، يمكن إضافة مصدر بيانات إلى الخريطة من خلال خاصية sources على الخريطة. توضح التعليمة البرمجية التالية كيفية إنشاء DataSourceوإضافته إلى الخريطة وإضافة معلم إلى مصدر البيانات.

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

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

يوضح الكود التالي عدة طرق لإنشاء GeoJSON Feature وFeatureCollection والأشكال الهندسية.

// GeoJSON Point Geometry
let point = Point(location)

// GeoJSON LineString Geometry
let polyline = Polyline(locations)

// GeoJSON Polygon Geometry
let polygon = Polygon(locations)

let polygonWithInteriorPolygons = Polygon(locations, interiorPolygons: polygons)

// GeoJSON MultiPoint Geometry
let pointCollection = PointCollection(locations)

// GeoJSON MultiLineString Geometry
let multiPolyline = MultiPolyline(polylines)

let multiPolylineFromLocations = MultiPolyline(locations: arrayOfLocationArrays) // [[CLLocationCoordinate2D]]

// GeoJSON MultiPolygon Geometry
let multiPolygon = MultiPolygon(polygons)

let multiPolygonFromLocations = MultiPolygon(locations: arrayOfLocationArrays) // [[CLLocationCoordinate2D]]

// GeoJSON GeometryCollection Geometry

let geometryCollection = GeometryCollection(geometries)

// GeoJSON Feature
let pointFeature = Feature(Point(location))

// GeoJSON FeatureCollection
let featureCollection = FeatureCollection(features)

ترتيب وإلغاء تسلسل GeoJSON

تحتوي جميع فئات المعالم والميزات والهندسة على طرق fromJson(_:) وtoJson() ثابتة تساعد في إنشاء التسلسل. تقوم سلسلة JSON الصالحة المنسقة التي fromJson() تم تمريرها عبر الأسلوب بإنشاء العنصر الهندسي. تعني هذه الطريقة fromJson() أيضاً أنه يمكنك استخدام JSONSerialization أو إستراتيجيات التسلسل/إلغاء التسلسل الأخرى. توضح التعليمة البرمجية التالية كيفية أخذ ميزة GeoJSON ذات السلسلة النصية وإلغاء تسلسلها في فئة Feature، ثم إعادة ترتيبها في سلسلة GeoJSON.

// Take a stringified GeoJSON object.
let geoJSONString = """
    {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [-100, 45]
        },
        "properties": {
            "custom-property": "value"
        }
    }
"""

// Deserialize the JSON string into a feature.
guard let feature = Feature.fromJson(geoJSONString) else {
    throw GeoJSONSerializationError.couldNotSerialize
}

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

قم باستيراد بيانات GeoJSON من الويب أو مجلد الأصول

تحتوي معظم ملفات GeoJSON على FeatureCollection. اقرأ ملفات GeoJSON كسلاسل واستخدم طريقة FeatureCollection.fromJson(_:) لإلغاء تسلسلها.

DataSource تحتوي الفئة على أسلوب مضمن يسمى importData(fromURL:) يمكنه التحميل في ملفات GeoJSON باستخدام عنوان URL لملف على الويب أو الجهاز.

// Create a data source.
let source = DataSource()

// Import the geojson data and add it to the data source.
let url = URL(string: "URL_or_FilePath_to_GeoJSON_data")!
source.importData(fromURL: url)

// Examples:
// source.importData(fromURL: URL(string: "asset://sample_file.json")!)
// source.importData(fromURL: URL(string: "https://example.com/sample_file.json")!)

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

importData(fromURL:) يوفر الأسلوب طريقة لتحميل موجز GeoJSON في مصدر بيانات ولكنه يوفر تحكما محدودا في كيفية تحميل البيانات وما يحدث بعد تحميلها. التعليمة البرمجية التالية هي فئة قابلة لإعادة الاستخدام لاستيراد البيانات من الويب أو مجلد الأصول وإعادتها إلى مؤشر ترابط واجهة المستخدم عبر وظيفة رد الاتصال. في رد الاتصال، يمكنك بعد ذلك إضافة المزيد من منطق التحميل اللاحق لمعالجة البيانات وإضافتها إلى الخريطة وحساب مربع الإحاطة وتحديث كاميرا الخرائط.

import Foundation

@objc
public class Utils: NSObject {
    /// Imports data from a web url or local file url and returns it as a string to a callback on the main thread.
    /// - Parameters:
    ///     - url: A web url or local file url that points to data to load.
    ///     - completion: The callback function to return the data to.
    @objc
    public static func importData(fromURL url: URL, completion: @escaping (String?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            DispatchQueue.main.async {
                if let data = data {
                    completion(String(decoding: data, as: UTF8.self))
                } else {
                    completion(nil)
                }
            }
        }.resume()
    }
}

توضح التعليمات البرمجية التالية كيفية استخدام هذه الأداة المساعدة لاستيراد بيانات GeoJSON كسلسلة وإعادتها إلى مؤشر الترابط الرئيسي عبر رد اتصال. في رد الاتصال، يمكن إجراء تسلسل لبيانات السلسلة في مجموعة ميزات GeoJSON وإضافتها إلى مصدر البيانات. اختيارياً، قم بتحديث كاميرا الخرائط للتركيز على البيانات.

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

// Create a web url or a local file url
let url = URL(string: "URL_to_GeoJSON_data")!
// Examples:
// let url = Bundle.main.url(forResource: "FeatureCollectionSample", withExtension: "geojson")!
// let url = URL(string: "www.yourdomain.com/path_to_feature_collection_sample")!

// Import the geojson data and add it to the data source.
Utils.importData(fromURL: url) { result in
    guard let result = result else {
        // No data imported.
        return
    }

    // Parse the data as a GeoJSON Feature Collection.
    guard let fc = FeatureCollection.fromJson(result) else {
        // Invalid data for FeatureCollection type.
        return
    }

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

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

    // Calculate the bounding box of all the data in the Feature Collection.
    guard let bbox = BoundingBox.fromData(fc) else {
        // The feature collection is empty.
        return
    }

    // Update the maps camera so it is focused on the data.
    map.setCameraBoundsOptions([
        .bounds(bbox),
        .padding(20)
    ])
}

تحديث ميزة

DataSource تسهل الفئة إضافة الميزات وإزالتها. يتطلب تحديث الهندسة أو خصائص المعلم استبدال الميزة في مصدر البيانات. هناك طريقتان يمكن استخدامهما لتحديث ميزة (ميزات):

  1. قم بإنشاء الميزة (الميزات) الجديدة بالتحديثات المطلوبة واستبدل جميع الميزات في مصدر البيانات باستخدام طريقة set. تعمل هذه الطريقة بشكل جيد عندما تريد تحديث كافة الميزات الموجودة في مصدر البيانات.
var source: DataSource!

private func 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.
    let myFeature = Feature(Point(CLLocationCoordinate2D(latitude: 0, longitude: 0)))
    myFeature.addProperty("Name", value: "Original value")
    source.add(feature: myFeature)
}

private func updateFeature() {
    // Create a new replacement feature with an updated geometry and property value.
    let myNewFeature = Feature(Point(CLLocationCoordinate2D(latitude: -10, longitude: 10)))
    myNewFeature.addProperty("Name", value: "New value")

    // Replace all features to the data source with the new one.
    source.set(feature: myNewFeature)
}
  1. تتبع مثيل الميزة في متغير، وقم بتمريرها إلى طريقة مصادر البيانات remove لإزالتها. قم بإنشاء الميزة (الميزات) الجديدة بالتحديثات المطلوبة، وتحديث مرجع المتغير، وإضافته إلى مصدر البيانات باستخدام طريقة add.
var source: DataSource!
var myFeature: Feature!

private func 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(Point(CLLocationCoordinate2D(latitude: 0, longitude: 0)))
    myFeature.addProperty("Name", value: "Original value")
    source.add(feature: myFeature)
}

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

    // Get properties from original feature.
    var props = myFeature.properties

    // Update a property.
    props["Name"] = "New value"

    // Create a new replacement feature with an updated geometry.
    myFeature = Feature(
        Point(CLLocationCoordinate2D(latitude: -10, longitude: 10)),
        properties: props
    )

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

تلميح

إذا كانت لديك بعض البيانات التي سيتم تحديثها بانتظام، وبيانات أخرى نادراً ما يتم تغييرها، فمن الأفضل تقسيمها إلى مثيلات مصدر بيانات منفصلة. عند حدوث تحديث في مصدر بيانات، فإنه يفرض على الخريطة إعادة رسم جميع المعالم الموجودة في مصدر البيانات. من خلال تقسيم هذه البيانات، ستتم إعادة رسم الميزات التي يتم تحديثها بانتظام فقط عند حدوث تحديث في مصدر البيانات هذا بينما لن تحتاج الميزات الموجودة في مصدر البيانات الآخر إلى إعادة رسمها. هذا يساعد في الأداء.

مصدر صور مصغرة من الخريطة لخطوط المتجهات

يصف مصدر الصور المصغرة الاتجاهية من الخريطة كيفية الوصول إلى طبقة الصور المصغرة الاتجاهية من الخريطة. استخدم الفئة VectorTileSource لإنشاء مثيل لمصدر الصور المصغرة الاتجاهية من الخريطة. طبقات تجانب الاتجاه تشبه طبقات التجانب، لكنها ليست متماثلة. تمثل طبقة التجانب صورة نقطية. طبقات تجانب الاتجاه هي ملف مضغوط بتنسيق PBF. يحتوي هذا الملف المضغوط على بيانات خريطة خط متجه وطبقة واحدة أو أكثر. يمكن تقديم الملف وتصميمه على العميل، بناءً على نمط كل طبقة. تحتوي البيانات الموجودة في صور مصغرة من الخريطة لخطوط المتجه على معالم جغرافية في شكل نقاط وخطوط ومضلعات. يوجد العديد من المزايا لاستخدام طبقات الصور المصغرة من الخريطة بدلًا من طبقات الصور المصغرة النقطية:

  • عادةً ما يكون حجم ملف الصور المصغرة من الخريطة أصغر بكثير من صورة مصغرة من الخريطة النقطية المكافئة. على هذا النحو، يتم استخدام نطاق ترددي أقل. إنه يعني زمن انتقال أقل وخريطة أسرع وتجربة مستخدم أفضل.
  • نظرًا لأن الصور المصغرة من الخريطة يتم عرضها على العميل، فإنها تتكيف مع دقة الجهاز الذي يتم عرضها عليه. نتيجة لذلك، تظهر الخرائط المعروضة بشكل أكثر تحديدًا، مع تسميات واضحة تمامًا.
  • لا يتطلب تغيير نمط البيانات في خرائط المتجهات تنزيل البيانات مرة أخرى، حيث يمكن تطبيق النمط الجديد على العميل. في المقابل، يتطلب تغيير نمط طبقة تجانب البيانات النقطية عادةً تحميل صور مصغرة من الخريطة من الخادم ثم تطبيق النمط الجديد.
  • نظرًا لأن البيانات يتم تسليمها في شكل متجه، فهناك معالجة أقل من جانب الخادم مطلوبة لإعداد البيانات. نتيجة لذلك، يمكن توفير البيانات الأحدث أسرع.

تلتزم خرائط Azure بمواصفات Mapbox Vector Tile، وهو معيار مفتوح. توفر خرائط Azure خدمات الصور المصغرة الاتجاهية من الخريطة التالية كجزء من النظام الأساسي:

تلميح

عند استخدام مربعات الصور المتجهة أو النقطية من خدمة عرض خرائط Azure مع iOS SDK، يمكنك استبدال atlas.microsoft.com بخاصية AzureMap domainPlaceholder. سيتم استبدال هذا العنصر النائب بنفس المجال الذي تستخدمه الخريطة وسيقوم تلقائيًا بإلحاق نفس تفاصيل المصادقة أيضًا. يعمل هذا على تبسيط المصادقة بشكل كبير مع خدمة العرض عند استخدام مصادقة Microsoft Entra.

لعرض البيانات من مصدر الصور المصغرة من الخريطة على الخريطة، قم بتوصيل المصدر بإحدى طبقات عرض البيانات. يجب أن تحدد كل الطبقات التي تستخدم مصدر متجه قيمة sourceLayer في الخيارات. تقوم التعليمة البرمجية التالية بتحميل خدمة صور مصغرة من الخريطة لمتجهات تدفق نسبة استخدام شبكة خرائط Azure كمصدر صورة مصغرة من الخريطة للمتجهات، ثم تعرضها على خريطة باستخدام طبقة خط. يحتوي مصدر الصور المصغرة من الخريطة هذا على مجموعة واحدة من البيانات في الطبقة المصدر تسمى "تدفق نسبة استخدام الشبكة". تحتوي بيانات الخط في مجموعة البيانات هذه على خاصية تسمى traffic_level تُستخدم في هذه التعليمة البرمجية لتحديد اللون وقياس حجم الخطوط.

// Formatted URL to the traffic flow vector tiles.
let trafficFlowUrl = "\(map.domainPlaceholder)/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.
let source = VectorTileSource(options: [
    .tiles([trafficFlowUrl]),
    .maxSourceZoom(22)
])
map.sources.add(source)

// Create a layer for traffic flow lines.
let layer = LineLayer(
    source: source,
    options: [

        // 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(
            from: NSExpression(
                forAZMInterpolating: NSExpression(forKeyPath: "traffic_level"),
                curveType: .linear,
                parameters: nil,
                stops: NSExpression(forConstantValue: [
                    0: UIColor.red,
                    0.33: UIColor.yellow,
                    0.66: UIColor.green
                ])
            )
        ),

        // Scale the width of roads based on the traffic_level property.
        .strokeWidth(
            from: NSExpression(
                forAZMInterpolating: NSExpression(forKeyPath: "traffic_level"),
                curveType: .linear,
                parameters: nil,
                stops: NSExpression(forConstantValue: [
                    0: 6,
                    1: 1
                ])
            )
        )
    ]
)

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

لقطة شاشة لخريطة بخطوط طريق مرمزة بالألوان تعرض مستويات تدفق حركة المرور.

توصيل مصدر بيانات بطبقة

يتم عرض البيانات على الخريطة باستخدام طبقات التقديم. يمكن أن تشير طبقة عرض واحدة أو أكثر إلى مصدر بيانات واحد. تتطلب طبقات العرض التالية مصدر بيانات:

يوضح الكود التالي كيفية إنشاء مصدر بيانات، وإضافته إلى الخريطة، واستيراد بيانات نقطة GeoJSON من موقع بعيد إلى مصدر البيانات، ثم توصيله بطبقة فقاعية.

// Create a data source.
let source = DataSource()

// Create a web url or a local file url
let url = URL(string: "URL_or_FilePath_to_GeoJSON_data")!
// Examples:
// let url = Bundle.main.url(forResource: "FeatureCollectionSample", withExtension: "geojson")!
// let url = URL(string: "yourdomain.com/path_to_feature_collection_sample")!

// Import the geojson data and add it to the data source.
source.importData(fromURL: url)

// 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.
let layer = BubbleLayer(source: source)
map.layers.addLayer(layer)

هناك طبقات عرض أخرى لا تتصل بمصادر البيانات هذه، ولكنها تقوم بتحميل البيانات مباشرة للعرض.

مصدر بيانات واحد متعدد الطبقات

يمكن توصيل طبقات متعددة بمصدر بيانات واحد. يوجد العديد من السيناريوهات المختلفة التي يكون فيها هذا الخيار مفيدًا. على سبيل المثال، ضع في اعتبارك السيناريو الذي يرسم فيه المستخدم مضلعًا. يجب أن نعرض ونملأ منطقة المضلع بينما يضيف المستخدم نقاطًا إلى الخريطة. إضافة خط ذي نمط لتخطيط المضلع يجعل من السهل رؤية حواف المضلع، أثناء رسم المستخدم. لتعديل موضع فردي بشكل ملائم في المضلع، قد نضيف مؤشراً، مثل دبوس أو علامة، فوق كل موضع.

لقطة شاشة لخريطة تعرض طبقات متعددة تعرض البيانات من مصدر بيانات واحد.

في معظم الأنظمة الأساسية لرسم الخرائط، قد تحتاج إلى عنصر مضلع وعنصر خط ودبوس لكل موضع في المضلع. أثناء تعديل المضلع، ستحتاج إلى تحديث الخط والدبابيس يدويًا، والتي يمكن أن تصبح معقدة بسرعة.

مع خرائط Azure، كل ما تحتاجه هو مضلع واحد في مصدر بيانات كما هو موضح في التعليمات البرمجية التالية.

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

// Create a polygon and add it to the data source.
source.add(geometry: Polygon([
    CLLocationCoordinate2D(latitude: 33.15, longitude: -104.5),
    CLLocationCoordinate2D(latitude: 38.5, longitude: -113.5),
    CLLocationCoordinate2D(latitude: 43, longitude: -111.5),
    CLLocationCoordinate2D(latitude: 43.5, longitude: -107),
    CLLocationCoordinate2D(latitude: 43.6, longitude: -94)
]))

// Create a polygon layer to render the filled in area of the polygon.
let polygonLayer = PolygonLayer(
    source: source,
    options: [.fillColor(UIColor(red: 1, green: 165/255, blue: 0, alpha: 0.2))]
)

// Create a line layer for greater control of rendering the outline of the polygon.
let lineLayer = LineLayer(source: source, options: [
    .strokeColor(.orange),
    .strokeWidth(2)
])

// Create a bubble layer to render the vertices of the polygon as scaled circles.
let bubbleLayer = BubbleLayer(
    source: source,
    options: [
        .bubbleColor(.orange),
        .bubbleRadius(5),
        .bubbleStrokeColor(.white),
        .bubbleStrokeWidth(2)
    ]
)

// Add all layers to the map.
map.layers.addLayers([polygonLayer, lineLayer, bubbleLayer])

تلميح

يمكنك أيضًا استخدام طريقة map.layers.insertLayer(_:below:)، حيث يمكن تمرير المعرف أو مثيل الطبقة الحالية كمعامل ثاني. هذا من شأنه أن يخبر تلك الخريطة بإدراج الطبقة الجديدة التي تتم إضافتها أسفل الطبقة الحالية. بالإضافة إلى تمرير معرف الطبقة، تدعم هذه الطريقة أيضًا القيم التالية.

  • "labels" - إدراج الطبقة الجديدة أسفل طبقات تسمية الخريطة.
  • "transit" - يُدرج الطبقة الجديدة أسفل طريق الخريطة وطبقات النقل العام.

معلومات إضافية

راجع المقالات التالية للحصول على المزيد من نماذج التعليمات البرمجية لإضافتها إلى الخرائط الخاصة بك: