Ejemplo n.º 1
0
/**
 * Buffer single Feature/Geometry
 *
 * @private
 * @param {Feature<any>} geojson input to be buffered
 * @param {number} radius distance to draw the buffer
 * @param {string} [units='kilometers'] any of the options supported by turf units
 * @param {number} [steps=64] number of steps
 * @returns {Feature<Polygon|MultiPolygon>} buffered feature
 */
function bufferFeature(geojson, radius, units, steps) {
    var properties = geojson.properties || {};
    var geometry = (geojson.type === 'Feature') ? geojson.geometry : geojson;

    // Geometry Types faster than jsts
    if (geometry.type === 'GeometryCollection') {
        var results = [];
        geomEach(geojson, function (geometry) {
            var buffered = bufferFeature(geometry, radius, units, steps);
            if (buffered) results.push(buffered);
        });
        return featureCollection(results);
    }

    // Project GeoJSON to Transverse Mercator projection (convert to Meters)
    var projected;
    var bbox = turfBbox(geojson);
    var needsTransverseMercator = bbox[1] > 50 && bbox[3] > 50;

    if (needsTransverseMercator) {
        projected = {
            type: geometry.type,
            coordinates: projectCoords(geometry.coordinates, defineProjection(geometry))
        };
    } else {
        projected = toMercator(geometry);
    }

    // JSTS buffer operation
    var reader = new GeoJSONReader();
    var geom = reader.read(projected);
    var distance = radiansToLength(lengthToRadians(radius, units), 'meters');
    var buffered = BufferOp.bufferOp(geom, distance);
    var writer = new GeoJSONWriter();
    buffered = writer.write(buffered);

    // Detect if empty geometries
    if (coordsIsNaN(buffered.coordinates)) return undefined;

    // Unproject coordinates (convert to Degrees)
    var result;
    if (needsTransverseMercator) {
        result = {
            type: buffered.type,
            coordinates: unprojectCoords(buffered.coordinates, defineProjection(geometry))
        };
    } else {
        result = toWgs84(buffered);
    }

    return (result.geometry) ? result : feature(result, properties);
}
Ejemplo n.º 2
0
module.exports = function lineDistance(geojson, units) {
    // Input Validation
    if (!geojson) throw new Error('geojson is required');
    geomEach(geojson, function (geometry) {
        if (geometry.type === 'Point') throw new Error('geojson cannot be a Point');
        if (geometry.type === 'MultiPoint') throw new Error('geojson cannot be a MultiPoint');
    });

    // Calculate distance from 2-vertex line segements
    return segmentReduce(geojson, function (previousValue, segment) {
        var coords = segment.geometry.coordinates;
        var start = point(coords[0]);
        var end = point(coords[1]);
        return previousValue + distance(start, end, units);
    }, 0);
};
Ejemplo n.º 3
0
/**
 * Convert Collection to FeatureCollection
 *
 * @private
 * @param {FeatureCollection|GeometryCollection<Point>} points Points
 * @returns {FeatureCollection<Point>} points
 */
function normalize(points) {
    var features = [];
    var type = points.geometry ? points.geometry.type : points.type;
    switch (type) {
    case 'GeometryCollection':
        geomEach(points, function (geom) {
            if (geom.type === 'Point') features.push({type: 'Feature', properties: {}, geometry: geom});
        });
        return {type: 'FeatureCollection', features: features};
    case 'FeatureCollection':
        points.features = points.features.filter(function (feature) {
            return feature.geometry.type === 'Point';
        });
        return points;
    default:
        throw new Error('points must be a Point Collection');
    }
}
Ejemplo n.º 4
0
/**
 * Calculates a buffer for input features for a given radius. Units supported are miles, kilometers, and degrees.
 *
 * When using a negative radius, the resulting geometry may be invalid if
 * it's too small compared to the radius magnitude. If the input is a
 * FeatureCollection, only valid members will be returned in the output
 * FeatureCollection - i.e., the output collection may have fewer members than
 * the input, or even be empty.
 *
 * @name buffer
 * @param {FeatureCollection|Geometry|Feature<any>} geojson input to be buffered
 * @param {number} radius distance to draw the buffer (negative values are allowed)
 * @param {Object} [options={}] Optional parameters
 * @param {string} [options.units="kilometers"] any of the options supported by turf units
 * @param {number} [options.steps=64] number of steps
 * @returns {FeatureCollection|Feature<Polygon|MultiPolygon>|undefined} buffered features
 * @example
 * var point = turf.point([-90.548630, 14.616599]);
 * var buffered = turf.buffer(point, 500, {units: 'miles'});
 *
 * //addToMap
 * var addToMap = [point, buffered]
 */
function buffer(geojson, radius, options) {
    // Optional params
    options = options || {};
    var units = options.units;
    var steps = options.steps || 64;

    // validation
    if (!geojson) throw new Error('geojson is required');
    if (typeof options !== 'object') throw new Error('options must be an object');
    if (typeof steps !== 'number') throw new Error('steps must be an number');

    // Allow negative buffers ("erosion") or zero-sized buffers ("repair geometry")
    if (radius === undefined) throw new Error('radius is required');
    if (steps <= 0) throw new Error('steps must be greater than 0');

    // default params
    steps = steps || 64;
    units = units || 'kilometers';

    var results = [];
    switch (geojson.type) {
    case 'GeometryCollection':
        geomEach(geojson, function (geometry) {
            var buffered = bufferFeature(geometry, radius, units, steps);
            if (buffered) results.push(buffered);
        });
        return featureCollection(results);
    case 'FeatureCollection':
        featureEach(geojson, function (feature) {
            var multiBuffered = bufferFeature(feature, radius, units, steps);
            if (multiBuffered) {
                featureEach(multiBuffered, function (buffered) {
                    if (buffered) results.push(buffered);
                });
            }
        });
        return featureCollection(results);
    }
    return bufferFeature(geojson, radius, units, steps);
}
Ejemplo n.º 5
0
/**
 * Smooths a {@link Polygon} or {@link MultiPolygon}. Based on [Chaikin's algorithm](http://graphics.cs.ucdavis.edu/education/CAGDNotes/Chaikins-Algorithm/Chaikins-Algorithm.html).
 * Warning: may create degenerate polygons.
 *
 * @name polygonSmooth
 * @param {FeatureCollection|Feature<Polygon|MultiPolygon>} inputPolys (Multi)Polygon(s) to smooth
 * @param {Object} [options={}] Optional parameters
 * @param {string} [options.iterations=1] THe number of times to smooth the polygon. A higher value means a smoother polygon.
 * @returns {FeatureCollection<Polygon>} FeatureCollection containing the smoothed polygon/poylgons
 * @example
 * var polygon = turf.polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15], [11, 11], [11, 0]]]);
 *
 * var smoothed = turf.polygonSmooth(polygon, {iterations: 3})
 *
 * //addToMap
 * var addToMap = [smoothed, polygon];
 */
function polygonSmooth(inputPolys, options) {
    var outPolys = [];
    // Optional parameters
    var iterations = options.iterations || 1;
    if (!inputPolys) throw new Error('inputPolys is required');

    geomEach(inputPolys, function (geom, geomIndex, properties) {
        var outCoords;
        var poly;
        var tempOutput;

        switch (geom.type) {
        case 'Polygon':
            outCoords = [[]];
            for (var i = 0; i < iterations; i++) {
                tempOutput = [[]];
                poly = geom;
                if (i > 0) poly = polygon(outCoords).geometry;
                processPolygon(poly, tempOutput);
                outCoords = tempOutput.slice(0);
            }
            outPolys.push(polygon(outCoords, properties));
            break;
        case 'MultiPolygon':
            outCoords = [[[]]];
            for (var y = 0; y < iterations; y++) {
                tempOutput = [[[]]];
                poly = geom;
                if (y > 0) poly = multiPolygon(outCoords).geometry;
                processMultiPolygon(poly, tempOutput);
                outCoords = tempOutput.slice(0);
            }
            outPolys.push(multiPolygon(outCoords, properties));
            break;
        default:
            throw new Error('geometry is invalid, must be Polygon or MultiPolygon');
        }
    });
    return featureCollection(outPolys);
}