プラグインの例

カスタムレンダラ プラグインをローカルで開発およびテストする場合は、まず Custom data using custom JavaScript translators to GeoJSON オプションを使用して Web アプリジェネレータをインストールする必要があります。 次に renderer.plugin.template.js 、生成されたサンプルアプリケーションで使用できるようになるファイルを拡張します。

以下の例に従って、レンダラ プラグインの能力開発スキルを練習してください。

トポロジジオメトリのプラグインの例

この例では、 HERE Map Content ( HMC )トポロジパーティションのカスタムレンダリングを使用しています。 この例では、道路セグメントとノードを視覚化して、さまざまなプラグイン機能を示します。

トポロジジオメトリのプラグインの例
図 1. トポロジジオメトリのプラグインの例

トポロジーパーティションの生データには必要なすべてのジオメトリ情報が含まれているため、同期 getGeoJSON 関数が使用されます。 この場合、プラグインはソースデータを HERE Map Content トポロジジオメトリから GeoJSON 形式にのみ変換します。 使用可能な関数の詳細については、「レンダリング関数」を参照してください。

コード例

以下のコードを renderer.plugin.template.js ファイルに貼り付ける前に ( プロジェクトのルートディレクトリにあります ) index.ts 、ファイルを開き DataInspector 、クラスの構成が次のように設定されていることを確認してください。

  • externalRenderingPluginName パラメータの値は .js 、拡張子のないレンダラ プラグインファイル名と一致します。 この場合は、です renderer.plugin.template

  • この例で使用されているカタログの HERE リソースネーム ( HERE リソースネーム )がに設定 hrn:here:data::olp-here:rib-2されています。

  • この例で使用されているレイヤー ID は、に設定 topology-geometryされています。

    ...
    interactiveDataSource: {
        hrn: "hrn:here:data::olp-here:rib-2",
        layer: "topology-geometry",
        levelWithData: 12,
        externalRenderingPluginName: "renderer.plugin.template"
    },
    ...
    

renderer.plugin.template.js

/**
 * Renderer plugin to convert JSON data to GeoJSON.
 *
 * Generated GeoJSON must be created according to GeoJSON Specification:
 * https://tools.ietf.org/html/rfc7946
 *
 * This template is an example of plugin to generate the GeoJSON object based on decoded partition
 * content (the same data structure as in Decoded Data panel).
 *
 * In order to use renderer plugin this file must be placed into project root directory and
 * parameter `externalRendererPluginName` must be passed into constructor:
 *
 * ```
 * const geoJsonDataSource = new GeoJsonDataSource({
 *    dataStore: {
 *        hrn: HRN.fromString("Your HRN string"),
 *        getBearerToken: tokenResolver.getBearerToken.bind(tokenResolver),
 *        layer: "Layer name"
 *    },
 *    externalRendererPluginName: "renderer.plugin.template"
 * });
 * ```
 *
 * Renderer plugin must return an object with one of these functions in it:
 *
 * ```
 * getGeoJSON: function (params) { ... }
 * getGeoJSONAsync: async function (params) { ... }
 * ```
 *
 * Both synchronous \`getGeoJSON\` and async \`getGeoJSONAsync\` functions return an object with
 * GeoJSON data.
 *
 * Only one of these functions must be declared, if both are present plugin will not be used.
 */
(() => {
    "use strict";

    /** @type {RendererPlugin} */
    const plugin = {
        /**
         * Generates GeoJSON from RIB topology layer data.
         *
         * @param {object} params.logger Logger to log information to console.
         * @param {object} params.layer All properties as well as dataUrl, name, partitioning and
         * originalConfiguration.
         * @param {object} params.decodeResult Input RIB Navigation Attributes data.
         * @param {object} params.mapUtils Various converters like `mercatorConverter`,
         * `heretileToQuad`, `hdmapCoordinateToLonLat`, etc.
         * @param {object} params.geometryDataProviders Various geometry data providers to map data.
         * @returns {object} An object with generated GeoJSON data.
         */
        getGeoJSON(params) {
            // Destructuring of `params` object.
            const { logger, decodeResult } = params;

            const data = decodeResult.data;

            if (!data.node && !data.segment) {
                throw new Error("Source data does not have node and segment lists. Make sure you are passing correct data to the renderer.");
            }

            var result = {
                "type": "FeatureCollection",
                "features": []
            };

            // Process segments in the tile if any exist.
            result.features = result.features.concat(processSegments(data.segment));
            logger.info("Segments successfully processed: " + data.segment.length);

            // Process nodes in the tile.  They will be rendered on top of the segments since they were added to the
            // FeatureCollection afterwards.
            result.features = result.features.concat(processNodes(data.node));
            logger.info("Nodes successfully processed: " + data.node.length);

            // return object with GeoJSON
            return {
                contentType: "application/vnd.geo+json; charset=utf-8",
                body: result
            };
        }
    };

    return plugin;

    function processNodes(nodes) {
        var list = [];

        for (var i = 0; i < nodes.length; i++) {
            list.push(createNodeFeature(nodes[i], i));
        }

        return list;
    }

    function createNodeFeature(node, index) {
        return {
            type: "Feature",
            properties: {
                protobufRef: `node[${index}]`,
                tooltip: `<b>Node Identifier:</b><br>${node.identifier}<br><br><b>Geometry:</b><br>Latitude: ${node.geometry.latitude}<br>Longitude: ${node.geometry.longitude}`,
                style: { color: "#FAD87A" }
            },
            geometry: {
                type: "Polygon",
                coordinates: convertCoordinateToShape(node.geometry.longitude, node.geometry.latitude)
            }
        };
    }

    function convertCoordinateToShape(lon, lat) {
        var coordinateList = [];
        var distance = 0.000025;

        // Creates rectangle from point
        coordinateList.push([lon - distance, lat + distance]);  // Upper-left corner.
        coordinateList.push([lon + distance, lat + distance]);  // Upper-right corner.
        coordinateList.push([lon + distance, lat - distance]);  // Lower-right corner.
        coordinateList.push([lon - distance, lat - distance]);  // Lower-left corner.
        coordinateList.push([lon - distance, lat + distance]);  // End ring with upper-left corner.

        return [coordinateList];
    }

    function processSegments(segments) {
        var list = [];

        for (var i = 0; i < segments.length; i++) {
            list.push(processLineStringArray(segments[i], i));
        }

        return list;
    }

    function processLineStringArray(segment, index) {
        var feature = {
            type: "Feature",
            properties: {
                protobufRef: `segment[${index}]`,
                identifier: segment.identifier,
                length: segment.length,
                style: {
                    color: "#1EE0FF"
                },
                width: 1
            },
            geometry: {
                type: "LineString",
                coordinates: []
            }
        };

        for (var i = 0; i < segment.geometry.point.length; i++) {
            var point = segment.geometry.point[i];
            feature.geometry.coordinates.push([point.longitude, point.latitude]);
        }

        return feature;
    }
})();

制限速度属性プラグインの例

このレンダラ プラグインは、制限速度の行を含む GeoJSON 出力を生成します。 制限速度情報は HERE Map Content Roads-Navigation Attributes レイヤーから取得され、道路ジオメトリは HERE Map Content トポロジジオメトリレイヤーから取得されます。

制限速度属性プラグインの例
図 2. 制限速度属性プラグインの例

これは、前の同期の例よりも複雑な例です。 ナビゲーション属性データには道路形状情報がないため SegmentAnchor 、ではなくを使用して道路セグメントを参照する必要があります。

そのため、 getGeoJSONAsync 道路のジオメトリを取得するために非同期機能が実装 SegmentAnchorGeometryDataProviderされています。 道路のジオメトリは getSegmentAnchorCoordinates 、データプロバイダのメソッドを呼び出して取得されます。 データプロバイダは、 GeoJSON 座標の配列として使用できる形式でジオメトリ情報を返します。 これにより、 GeoJSON Feature オブジェクトの生成に必要な労力が削減されます。

ジオメトリデータプロバイダ getSegmentAnchorCoordinates の関数への呼び出しは非同期で、データプロバイダインスタンスを取得します。

コード例

以下のコードを renderer.plugin.template.js ファイルに貼り付ける前に ( プロジェクトのルートディレクトリにあります ) index.ts 、ファイルを開き DataInspector 、クラスの構成が次のように設定されていることを確認してください。

  • externalRenderingPluginName パラメータの値は .js 、拡張子のないレンダラ プラグインファイル名と一致します。 この場合は、です renderer.plugin.template

  • この例で使用されているカタログの HERE リソースネーム ( HERE リソースネーム )がに設定 hrn:here:data::olp-here:rib-2されています。

  • この例で使用されているレイヤー ID は、に設定 navigation-attributesされています。

    ...
    interactiveDataSource: {
        hrn: "hrn:here:data::olp-here:rib-2",
        layer: "navigation-attributes",
        levelWithData: 12,
        externalRenderingPluginName: "renderer.plugin.template"
    },
    ...
    

renderer.plugin.template.js

/*
 * Copyright (C) 2017-2020 HERE Global B.V. and its affiliate(s). All rights reserved.
 *
 * This software and other materials contain proprietary information controlled by HERE and are
 * protected by applicable copyright legislation. Any use and utilization of this software and other
 * materials and disclosure to any third parties is conditional upon having a separate agreement
 * with HERE for the access, use, utilization or disclosure of this software. In the absence of such
 * agreement, the use of the software is not allowed.
 */

/**
 * Renderer plugin to convert JSON data to GeoJSON.
 *
 * Generated GeoJSON must be created according to GeoJSON Specification:
 * https://tools.ietf.org/html/rfc7946
 *
 * This template is an example of plugin to generate the GeoJSON object based on decoded partition
 * content (the same data structure as in Decoded Data panel).
 *
 * In order to use renderer plugin this file must be placed into project root directory and
 * parameter `externalRendererPluginName` must be passed into constructor:
 *
 * ```
 * const geoJsonDataSource = new GeoJsonDataSource({
 *    dataStore: {
 *        hrn: HRN.fromString("Your HRN string"),
 *        getBearerToken: tokenResolver.getBearerToken.bind(tokenResolver),
 *        layer: "Layer name"
 *    },
 *    externalRendererPluginName: "renderer.plugin.template"
 * });
 * ```
 *
 * Renderer plugin must return an object with one of these functions in it:
 *
 * ```
 * getGeoJSON: function (params) { ... }
 * getGeoJSONAsync: async function (params) { ... }
 * ```
 *
 * Both synchronous \`getGeoJSON\` and async \`getGeoJSONAsync\` functions return an object with
 * GeoJSON data.
 *
 * Only one of these functions must be declared, if both are present plugin will not be used.
 */
(() => {
    "use strict";

    let geometryDataProvider; // An instance of geometry data provider.
    let loggerInstance; // An instance of logger.

    // Thresholds and colors for different speed limit values.
    const MEDIUM_LIMIT_LOW_VALUE = 50;
    const MEDIUM_LIMIT_HIGH_VALUE = 80;
    const COLOR_SPEEDLIMIT_LOW = "#bf212f";
    const COLOR_SPEEDLIMIT_MEDIUM = "#e18c1e";
    const COLOR_SPEEDLIMIT_HIGH = "#27b376";
    const HOVER_SPEEDLIMIT_LOW = "#e52838";
    const HOVER_SPEEDLIMIT_MEDIUM = "#ffa423";
    const HOVER_SPEEDLIMIT_HIGH = "#2fd98f";

    /** @type {RendererPlugin} */
    const plugin = {
        /**
         * Asynchronously generates GeoJSON from RIB Navigation Attributes data.
         *
         * @param {object} params.logger Logger to log information to console.
         * @param {object} params.layer All properties as well as dataUrl, name, partitioning and
         * originalConfiguration.
         * @param {object} params.decodeResult Input RIB Navigation Attributes data.
         * @param {object} params.mapUtils Various converters like `mercatorConverter`,
         * `heretileToQuad`, `hdmapCoordinateToLonLat`, etc.
         * @param {object} params.geometryDataProviders Various geometry data providers to map data.
         * @returns {Promise} A promise which resolves with generated GeoJSON data.
         */
        async getGeoJSONAsync(params) {
            // Destructuring of `params` object.
            const { logger, decodeResult, geometryDataProviders } = params;

            // This is an async function so it must return a Promise.
            // Save an instance of logger so it can be used by other functions within the scope of
            // this plugin.
            loggerInstance = logger;

            // Get an instance of geometry data provider from `geometryDataProviders`.
            // Please note that this is an asynchronous call.
            geometryDataProvider = await geometryDataProviders.getSegmentAnchorDataProvider();

            // Validate input data.
            if (!decodeResult.data.speed_limit && !decodeResult.data.segment_anchor) {
                loggerInstance.error("Source data does not have speed limits data. Make sure you are passing correct data to the renderer.");
                throw new Error();
            }

            // Create result object.
            const result = {
                "type": "FeatureCollection",
                "features": await processSpeedLimits(decodeResult.data)
            };

            // Resolve promise with generated GeoJSON.
            return {
                contentType: "application/geo+json; charset=utf-8",
                body: result
            };
        }
    };

    return plugin;

    /**
     * Creates speed limits geometry.
     * Please note that this function is async.
     */
    async function processSpeedLimits(data) {
        const result = [];

        // Preliminary sort source data by speed limit value.
        data.speed_limit.sort((item1, item2) => item1.value < item2.value);

        // Iterate over speed limit items.
        let speedLimitItemIndex = 0;
        for (const speedLimitItem of data.speed_limit) {
            const style = getLineStyle(speedLimitItem);

            // Iterate over `SegmentAnchor`s of speed limit item.
            let segmentAnchorIndex = 0;
            for (const anchorIndex of speedLimitItem.segment_anchor_index) {
                // First, get actual `SegmentAnchor` from pool.
                const anchor = data.segment_anchor[anchorIndex];
                // Then, retrieve line coordinates from geometry data provider.
                const coordinates = await geometryDataProvider.getSegmentAnchorCoordinates(anchor);

                if (coordinates.length === 0) {
                    loggerInstance.warn(`Can't retrieve geometry for SegmentAnchor #${anchorIndex}`);
                }

                // Create a `LineString` feature for speed limit segment
                result.push({
                    type: "Feature",
                    properties: {
                        // `protobufRef` is a path pointing to original data property
                        protobufRef: `speed_limit[${speedLimitItemIndex}].segment_anchor_index[${segmentAnchorIndex}]`,
                        speedLimit: speedLimitItem.value,
                        style, // apply visual styles to feature
                        width: 4,
                        // `title` will be shown in tooltip when mouse is moved over the feature
                        title: speedLimitItem.value + " km/h"
                    },
                    geometry: {
                        type: "LineString",
                        coordinates
                    }
                });

                segmentAnchorIndex++;
            }
            speedLimitItemIndex++;
        };

        return result;
    }

    /**
     * Returns line style based on speed limit value.
     *
     * @param {object} speedLimitItem Speed limit item.
     * @returns {string} String with line color.
     */
    function getLineStyle(speedLimitItem) {
        if (speedLimitItem.value < MEDIUM_LIMIT_LOW_VALUE) {
            return { color: COLOR_SPEEDLIMIT_LOW, hoverColor: HOVER_SPEEDLIMIT_LOW };
        } else if (speedLimitItem.value < MEDIUM_LIMIT_HIGH_VALUE) {
            return { color: COLOR_SPEEDLIMIT_MEDIUM, hoverColor: HOVER_SPEEDLIMIT_MEDIUM };
        } else {
            return { color: COLOR_SPEEDLIMIT_HIGH, hoverColor: HOVER_SPEEDLIMIT_HIGH };
        }
    }
})();

レンダラ プラグインが特定の道路トポロジパーティションのすべての道路セグメントを必要とする場合、以下を実装することで、道路セグメントデータプロバイダのインスタンスからセグメントデータを取得できます。

getGeoJSONAsync: function (params) {
    ...
    // Get an instance of geometry data provider.
    const roadSegmentsProvider = await params.geometryDataProviders.getRoadSegmentsDataProvider();
    // Get an array of all road segments from data provider for current partition.
    const roadSegments = await roadSegmentsProvider.getRoadSegmentsForTile(params.decodeResult.partitionId);
    ...
}

この例では、プラグインがソース params.decodeResult.partitionId データを使用して、 HERE Map Content パーティションと同じパーティションを参照します。

道路セグメントのデータ形式の詳細について BaseRoadSegmentsDataProvider は、参照ドキュメントを参照してください。

」に一致する結果は 件です

    」に一致する結果はありません