349 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			349 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { GeoGraphics, GeoShape, GeoMultiShape, GeoLine, GeoPoint } from './geographics.js'
 | 
						|
 | 
						|
/**
 | 
						|
 * Helper class for handling GeoJson data.
 | 
						|
 * As specified by [RFC7946](https://tools.ietf.org/html/rfc7946).
 | 
						|
 *
 | 
						|
 * @static
 | 
						|
 * @export
 | 
						|
 * @class
 | 
						|
 */
 | 
						|
export default class GeoJson {
 | 
						|
    static isLineType(type) {
 | 
						|
        return type == 'LineString' || type == 'MultiLineString'
 | 
						|
    }
 | 
						|
 | 
						|
    static _getFormatStringOfType(type) {
 | 
						|
        let description = ', where p represents a coordinate point'
 | 
						|
        let format = ''
 | 
						|
        switch (type) {
 | 
						|
            case 'Point':
 | 
						|
                format = 'p'
 | 
						|
                break
 | 
						|
            case 'LineString':
 | 
						|
                format = '[p1,p2,p3,...,pn]'
 | 
						|
                break
 | 
						|
            case 'Polygon':
 | 
						|
                format = '[ [p1,p2,...,pn], [h1,h2,...,hn] ]'
 | 
						|
                description += ' and h also represents a coordinate point, but it creates a hole.'
 | 
						|
                break
 | 
						|
            default:
 | 
						|
                format = type
 | 
						|
                description = " is either not valid or not yet implemented in method '_getFormatStringOfType(type)'."
 | 
						|
        }
 | 
						|
 | 
						|
        return format + ' - ' + description
 | 
						|
    }
 | 
						|
 | 
						|
    static get types() {
 | 
						|
        return ['Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon']
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Takes a JSON object that contains a FeatureCollection and returns an array
 | 
						|
     * of GeoJson objects.
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {array} featureCollection - Array of GeoJSON objects that were contained in the feature collection.
 | 
						|
     * @returns {array} Returns an array of geo json objects.
 | 
						|
     * @memberof GeoJson
 | 
						|
     */
 | 
						|
    static unwrapFeatureCollection(featureCollection) {
 | 
						|
        if (featureCollection.features == null) {
 | 
						|
            console.error(
 | 
						|
                'Error at GeoJson.unrwapFeatureCollection(collection): Provided object was no valid FeatureCollection.',
 | 
						|
                featureCollection
 | 
						|
            )
 | 
						|
            return
 | 
						|
        }
 | 
						|
 | 
						|
        let list = []
 | 
						|
 | 
						|
        featureCollection.features.forEach(feature => {
 | 
						|
            let { type, coordinates } = feature
 | 
						|
 | 
						|
            if (feature.type.toLowerCase() == 'feature') {
 | 
						|
                ;({ type, coordinates } = feature.geometry)
 | 
						|
            }
 | 
						|
 | 
						|
            list.push({ type, coordinates })
 | 
						|
        })
 | 
						|
 | 
						|
        return list
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Validates and converts one set of coordinates of a specific type.
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {string} type - Type of the GeoJson.
 | 
						|
     * @param {array} coordinates - array of points.
 | 
						|
     * @returns {array}
 | 
						|
     * @memberof GeoJson
 | 
						|
     */
 | 
						|
    static validateAndConvert(type, coordinates) {
 | 
						|
        let converted = null
 | 
						|
 | 
						|
        if (!GeoJson.validateType(type)) throw new GeoJson.InvalidTypeError(type)
 | 
						|
        else {
 | 
						|
            if (GeoJson.validateCoordinates(type, coordinates)) {
 | 
						|
                converted = GeoJson.convert(type, coordinates)
 | 
						|
            } else {
 | 
						|
                console.error(
 | 
						|
                    `Coordinates are invalid. They must be in format of type '${type} - ${GeoJson._getFormatStringOfType(
 | 
						|
                        type
 | 
						|
                    )}'`
 | 
						|
                )
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return converted
 | 
						|
    }
 | 
						|
 | 
						|
    static validateType(type) {
 | 
						|
        return GeoJson.types.indexOf(type) != -1
 | 
						|
    }
 | 
						|
 | 
						|
    static validateCoordinates(type, coordinates) {
 | 
						|
        let valid = false
 | 
						|
 | 
						|
        switch (type) {
 | 
						|
            case 'Point':
 | 
						|
                valid = !!GeoJson.validateAndConvertPoint(coordinates)
 | 
						|
                break
 | 
						|
            case 'LineString':
 | 
						|
                valid = GeoJson.validateLineString(coordinates)
 | 
						|
                break
 | 
						|
            case 'Polygon':
 | 
						|
                valid = GeoJson.validatePolygon(coordinates)
 | 
						|
                break
 | 
						|
            case 'MultiPolygon':
 | 
						|
                valid = true
 | 
						|
                for (let i = 0; i < coordinates.length; i++) {
 | 
						|
                    if (!GeoJson.validatePolygon(coordinates[i])) {
 | 
						|
                        valid = false
 | 
						|
                        break
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                break
 | 
						|
            case 'MultiPoint':
 | 
						|
            case 'MultiLineString':
 | 
						|
            default:
 | 
						|
                console.error('Type was not yet implemented: ', type)
 | 
						|
        }
 | 
						|
        return valid
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Validates a point if it's an valid coordinate.
 | 
						|
     *
 | 
						|
     * NOTE: Here we are not following the GeoJSON standard.
 | 
						|
     * For convenience multiple forms of representing a coordinate are
 | 
						|
     * considered valid. A complete list is provided in the GeoUtils.
 | 
						|
     *
 | 
						|
     * @param {object} point - The point that is tested for validity.
 | 
						|
     * @returns {boolean}
 | 
						|
     * @memberof GeoJson
 | 
						|
     */
 | 
						|
    static validateAndConvertPoint(point) {
 | 
						|
        return GeoUtils.validateCoordinate(point)
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     *Validates if the given points represent a 'LineString'.
 | 
						|
     *
 | 
						|
     * @param {array} points - A list of coordinates that represent a line.
 | 
						|
     * @returns {boolean} - Returns true, if the array is in formes as: [x1,x2,x3,...,xn]. Where x# represent a valid coordinate.
 | 
						|
     * @memberof GeoJson
 | 
						|
     */
 | 
						|
    static validateLineString(points) {
 | 
						|
        let valid = false
 | 
						|
        if (Array.isArray(points)) valid = points.every(GeoJson.validateAndConvertPoint)
 | 
						|
        return valid
 | 
						|
    }
 | 
						|
 | 
						|
    static validatePolygon(points) {
 | 
						|
        let valid = false
 | 
						|
        if ((Array.isArray(points) && points.length >= 1) || points.length <= 2)
 | 
						|
            valid = points.every(this.validateLineString)
 | 
						|
        return valid
 | 
						|
    }
 | 
						|
 | 
						|
    static convert(type, coordinates) {
 | 
						|
        let converted = null
 | 
						|
        switch (type) {
 | 
						|
            case 'Point':
 | 
						|
                converted = GeoJson.validateAndConvertPoint(coordinates)
 | 
						|
                break
 | 
						|
            case 'LineString':
 | 
						|
                converted = GeoJson._convertLineString(coordinates)
 | 
						|
                break
 | 
						|
            case 'Polygon':
 | 
						|
                converted = GeoJson._convertPolygon(coordinates)
 | 
						|
                break
 | 
						|
            case 'MultiPolygon':
 | 
						|
                converted = GeoJson._convertMultiPolygon(coordinates)
 | 
						|
                break
 | 
						|
            default:
 | 
						|
                throw new GeoJson.InvalidTypeError(type)
 | 
						|
        }
 | 
						|
 | 
						|
        return converted
 | 
						|
    }
 | 
						|
 | 
						|
    static _convertLineString(coordinates) {
 | 
						|
        return coordinates.map(point => {
 | 
						|
            return GeoJson.validateAndConvertPoint(point)
 | 
						|
        })
 | 
						|
    }
 | 
						|
 | 
						|
    static _convertPolygon(coordinates) {
 | 
						|
        return coordinates.map(shape => {
 | 
						|
            return GeoJson._convertLineString(shape)
 | 
						|
        })
 | 
						|
    }
 | 
						|
    static _convertMultiPolygon(coordinates) {
 | 
						|
        return coordinates.map(polygon => {
 | 
						|
            return GeoJson._convertPolygon(polygon)
 | 
						|
        })
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
GeoJson.InvalidTypeError = class extends Error {
 | 
						|
    constructor(type) {
 | 
						|
        super(`The requested Type was not implemented: ${type}.`)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * GeoUtils contains a collection of useful functions when working with maps.
 | 
						|
 *
 | 
						|
 * @static
 | 
						|
 */
 | 
						|
export class GeoUtils {
 | 
						|
    static transformToGeoGraphics(list) {
 | 
						|
        let geographicsList = []
 | 
						|
        list.forEach(item => {
 | 
						|
            if (item.type == 'FeatureCollection') {
 | 
						|
                item.features.forEach(feature => {
 | 
						|
                    let { type, coordinates } = feature
 | 
						|
 | 
						|
                    if (type.toLowerCase() == 'feature') ({ type, coordinates } = feature.geometry)
 | 
						|
 | 
						|
                    coordinates = GeoJson.validateAndConvert(type, coordinates)
 | 
						|
 | 
						|
                    let geographics = this.fromGeoJsonToGeoGraphics(type, coordinates)
 | 
						|
                    if (geographics) geographicsList.push(geographics)
 | 
						|
                })
 | 
						|
            } else {
 | 
						|
                let geo = fromGeoJsonToGeoGraphics(item.type, item.geometry)
 | 
						|
                if (geo) geographicsList.push(geo)
 | 
						|
            }
 | 
						|
        })
 | 
						|
        return geographicsList
 | 
						|
    }
 | 
						|
 | 
						|
    static resolveFeatureCollection(collection) {
 | 
						|
        if (!collection.features) {
 | 
						|
            console.error(
 | 
						|
                'Error in GeoUtils.resolveFeatureCollection(colelction): Passed parameter was no feature collection.',
 | 
						|
                collection
 | 
						|
            )
 | 
						|
            return
 | 
						|
        }
 | 
						|
        let geojson = []
 | 
						|
        collection.features.forEach(feature => {
 | 
						|
            let { type, coordinates } = feature
 | 
						|
 | 
						|
            if (feature.type == feature) {
 | 
						|
                coordinates = feature.geometry.coordinates
 | 
						|
                type = feature.geometry.type
 | 
						|
            }
 | 
						|
 | 
						|
            geojson.push({ type, coordinates })
 | 
						|
        })
 | 
						|
 | 
						|
        return geojson
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates the appropriate GeoGraphics object from a GeoJson type.
 | 
						|
     * The coordinates need to be alread in the appropriate PIXI format.
 | 
						|
     * If not already - this can be achieved by calling 'GeoJson.validateAndConvert(type, points)'
 | 
						|
     * beforehand.
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {string} type - Any of the GeoJson types ('Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon').
 | 
						|
     * @param {array} coordinates - Array of coordinates that fit the type. The positions within these must be in PIXI format: {x:a, y:b}.
 | 
						|
     * @returns {GeoGraphics} - Returns a GeoGraphics object. If the conversion fails, it returns null.
 | 
						|
     * @memberof GeoGraphics
 | 
						|
     */
 | 
						|
    static fromGeoJsonToGeoGraphics(type, coordinates, opts = {}) {
 | 
						|
        let geographics = null
 | 
						|
 | 
						|
        /**
 | 
						|
         * TODO: REMOVE
 | 
						|
         * Just for initial debugging purposes
 | 
						|
         */
 | 
						|
        Object.assign(opts, {
 | 
						|
            debug: true
 | 
						|
        })
 | 
						|
 | 
						|
        switch (type) {
 | 
						|
            case 'Polygon':
 | 
						|
                geographics = new GeoShape(coordinates, opts)
 | 
						|
                break
 | 
						|
 | 
						|
            case 'MultiPolygon':
 | 
						|
                geographics = new GeoMultiShape(coordinates, opts)
 | 
						|
                break
 | 
						|
            case 'LineString':
 | 
						|
                geographics = new GeoLine(coordinates, opts)
 | 
						|
                break
 | 
						|
            case 'Point':
 | 
						|
                geographics = new GeoPoint(coordinates, opts)
 | 
						|
                break
 | 
						|
            default:
 | 
						|
                console.log('Could not create Geographics for type: ' + type + '. This was not implemented yet.')
 | 
						|
            //Nothing
 | 
						|
        }
 | 
						|
 | 
						|
        return geographics
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Validates an object, if it's an coordinate object.
 | 
						|
     * Coordinate objects can be in the following forms:
 | 
						|
     *
 | 
						|
     * [lng, lat] - GeoJSON array format. !Attention lng and lat are swapped compared to the x,y format.
 | 
						|
     * {x: lat, y: lng} - 'correct' PIXI.format
 | 
						|
     * {lat, lng}
 | 
						|
     * {latitude: lat, longitude: lng}
 | 
						|
     *
 | 
						|
     * @static
 | 
						|
     * @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
 | 
						|
     * @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
 | 
						|
     * @memberof GeoGraphics
 | 
						|
     */
 | 
						|
    static validateCoordinate(coordinate) {
 | 
						|
        if (Array.isArray(coordinate)) {
 | 
						|
            if (coordinate.length == 2 && typeof coordinate[0] == 'number' && typeof coordinate[1] == 'number')
 | 
						|
                return new PIXI.Point(coordinate[1], coordinate[0])
 | 
						|
            else return false
 | 
						|
        } else {
 | 
						|
            const latvalues = ['x', 'lat', 'latitude']
 | 
						|
            const lngvalues = ['y', 'lng', 'longitude']
 | 
						|
 | 
						|
            let result = {}
 | 
						|
            for (let key of Object.keys(coordinate)) {
 | 
						|
                let target = key.toLowerCase()
 | 
						|
                if (latvalues.indexOf(target) !== -1) result.x = coordinate[key]
 | 
						|
                else if (lngvalues.indexOf(target) !== -1) result.y = coordinate[key]
 | 
						|
            }
 | 
						|
 | 
						|
            if (result.hasOwnProperty('x') && result.hasOwnProperty('y')) return new PIXI.Point(result.x, result.y)
 | 
						|
            else return false
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |