Skip to main content

Feature Intersections

This example displays a map with 25 bounded random points, and one large circle polygon. Any points that intersect with the circle are colored blue, otherwise active features default to pink and inactive features default to light blue. You can also use the draw tool to add additional points and polygons to the map, which will also appear blue if they intersect with the circle. Requires Trimble Maps v3.0.0 or later.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.6.0.css" />
        <script src="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.6.0.js"></script>
        <script src="https://unpkg.com/@turf/turf/turf.min.js"></script>
        <link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/addon/trimblemaps-draw-1.0.0.css" />
        <script src="https://maps-sdk.trimblemaps.com/addon/trimblemaps-draw-1.0.0.js"></script>
        <style>
            body { margin: 0; padding: 0; }
            #map {
                position: absolute;
                top: 0;
                bottom: 0;
                width: 100%;
          }
        </style>
    </head>
      <body>
      <div id="map"></div>

      <script>
        TrimbleMaps.APIKey = 'YOUR_API_KEY_HERE';
        const map = window.map = new TrimbleMaps.Map({
            container: 'map', // container id
            style: TrimbleMaps.Common.Style.TRANSPORTATION, // hosted style id
            center: [-109, 37.75], // starting position
            zoom: 6 // starting zoom
        });

        // https://stackoverflow.com/questions/37599561/drawing-a-circle-with-the-radius-in-miles-meters-with-mapbox-gl-js
        const createGeoJSONCircle = function(center, radiusInKm, points) {
            if(!points) points = 64;
            const coords = {
                latitude: center[1],
                longitude: center[0]
            };
            const km = radiusInKm;
            const ret = [];
            const distanceX = km / (111.320 * Math.cos(coords.latitude * Math.PI/180));
            const distanceY = km / 110.574;
            let theta, x, y;
            for(let i = 0; i < points; i++) {
                theta = (i / points) * (2 * Math.PI);
                x = distanceX * Math.cos(theta);
                y = distanceY * Math.sin(theta);
                ret.push([coords.longitude + x, coords.latitude + y]);
            }
            ret.push(ret[0]);
            return {
                type: 'geojson',
                data: {
                    type: 'FeatureCollection',
                    features: [{
                        type: 'Feature',
                        geometry: {
                            type: 'Polygon',
                            coordinates: [ret]
                        }
                    }]
                }
            };
        };

        const randomNumber = function(min, max) {
            const nbr = Math.random() * (max - min) + min;
            return nbr.toFixed(5);
        }

        const createFeature = function(id, coords) {
            return {
                type: 'Feature',
                properties: { id: id, intersect: 0 },
                geometry: {
                    type: 'Point',
                    coordinates: coords
                }
          };
        }

        const createFeatureCollection = function(features) {
            return {
                type: 'geojson',
                data: {
                    type: 'FeatureCollection',
                    features: features,
                }
            };
        }

        const smallCircleStyle = {
            id: '',
            type: 'circle',
            source: '',
            paint: {
                'circle-radius': 5,
                'circle-color': [
                      'match',
                      ['get', 'intersect'],
                      1,
                      '#0000FF',
                      '#33C2FF'
                ],
                'circle-stroke-color': [
                      'match',
                      ['get', 'intersect'],
                      1,
                      '#0000FF',
                      '#FFF'
                ],
                'circle-stroke-width': 2,
                'circle-opacity': 0.9,
                'circle-stroke-opacity': 1
            }
        };

        const drawStyles = [
            {
                id: 'gl-draw-polygon-fill-inactive',
                type: 'fill',
                filter: ['all',
                    ['==', 'active', 'false'],
                    ['==', '$type', 'Polygon'],
                    ['!=', 'mode', 'static']
                ],
                paint: {
                    'fill-color': ['case',
                        ['==', ['get', 'user_intersect'], 1], '#0000FF',
                        '#33C2FF'
                    ],
                    'fill-outline-color': '#33C2FF',
                    'fill-opacity':  ['case',
                        ['==', ['get', 'user_intersect'], 1], 0.9,
                        0.25
                    ],
                }
            },
            {
                id: 'gl-draw-polygon-fill-active',
                type: 'fill',
                filter: ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
                paint: {
                    'fill-color': ['case',
                        ['==', ['get', 'user_intersect'], 1], '#0000FF',
                        '#E555C9'
                    ],
                    'fill-outline-color': '#E555C9',
                    'fill-opacity':  ['case',
                        ['==', ['get', 'user_intersect'], 1], 0.9,
                        0.25
                    ],
                }
            },
            {
                id: 'gl-draw-polygon-midpoint',
                type: 'circle',
                filter: ['all',
                    ['==', '$type', 'Point'],
                    ['==', 'meta', 'midpoint']],
                paint: {
                    'circle-radius': 5,
                    'circle-color': '#E555C9'
                }
            },
            {
                id: 'gl-draw-polygon-stroke-inactive',
                type: 'line',
                filter: ['all',
                    ['==', 'active', 'false'],
                    ['==', '$type', 'Polygon'],
                    ['!=', 'mode', 'static']
                ],
                layout: {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#33C2FF',
                    'line-width': 2
                }
            },
            {
                id: 'gl-draw-polygon-stroke-active',
                type: 'line',
                filter: ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
                layout: {
                    'line-cap': 'butt',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#E555C9',
                    'line-dasharray': [3, 2],
                    'line-width': 2.5
                }
            },
            {
                id: 'gl-draw-line-inactive',
                type: 'line',
                filter: ['all',
                    ['==', 'active', 'false'],
                    ['==', '$type', 'LineString'],
                    ['!=', 'mode', 'static']
                ],
                layout: {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#33C2FF',
                    'line-width': 2.5
                }
            },
            {
                id: 'gl-draw-line-active',
                type: 'line',
                filter: ['all',
                    ['==', '$type', 'LineString'],
                    ['==', 'active', 'true']
                ],
                layout: {
                    'line-cap': 'butt',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#E555C9',
                    'line-dasharray': [3, 2],
                    'line-width': 2.5
                }
            },
            {
                id: 'gl-draw-polygon-and-line-vertex-stroke-inactive',
                type: 'circle',
                filter: ['all',
                    ['==', 'meta', 'vertex'],
                    ['==', '$type', 'Point'],
                    ['!=', 'mode', 'static']
                ],
                paint: {
                    'circle-radius': 7,
                    'circle-color': '#fff'
                }
            },
            {
                id: 'gl-draw-polygon-and-line-vertex-inactive',
                type: 'circle',
                filter: ['all',
                    ['==', 'meta', 'vertex'],
                    ['==', '$type', 'Point'],
                    ['!=', 'mode', 'static']
                ],
                paint: {
                    'circle-radius': 5,
                    'circle-color': '#E555C9'
                }
            },
            {
                id: 'gl-draw-point-point-stroke-inactive',
                type: 'circle',
                filter: ['all',
                    ['==', 'active', 'false'],
                    ['==', '$type', 'Point'],
                    ['==', 'meta', 'feature'],
                    ['!=', 'mode', 'static']
                ],
                paint: {
                    'circle-radius': 7,
                    'circle-opacity': 1,
                    'circle-color': ['case',
                        ['==', ['get', 'user_intersect'], 1], '#0000FF',
                        '#FFF'
                    ],
                }
            },
            {
                id: 'gl-draw-point-inactive',
                type: 'circle',
                filter: ['all',
                    ['==', 'active', 'false'],
                    ['==', '$type', 'Point'],
                    ['==', 'meta', 'feature'],
                    ['!=', 'mode', 'static']
                ],
                paint: {
                    'circle-radius': 5,
                    'circle-color': ['case',
                        ['==', ['get', 'user_intersect'], 1], '#0000FF',
                        '#33C2FF'
                    ],
                }
            },
            {
                id: 'gl-draw-point-stroke-active',
                type: 'circle',
                filter: ['all',
                    ['==', '$type', 'Point'],
                    ['==', 'active', 'true'],
                    ['!=', 'meta', 'midpoint']
                ],
                paint: {
                    'circle-radius': 7,
                    'circle-color': ['case',
                        ['==', ['get', 'user_intersect'], 1], '#0000FF',
                        '#FFF'
                    ],
                }
            },
            {
                id: 'gl-draw-point-active',
                type: 'circle',
                filter: ['all',
                    ['==', '$type', 'Point'],
                    ['!=', 'meta', 'midpoint'],
                    ['==', 'active', 'true']],
                paint: {
                    'circle-radius': 5,
                    'circle-color': ['case',
                        ['==', ['get', 'user_intersect'], 1], '#0000FF',
                        '#E555C9'
                    ],
                }
            },
            {
                id: 'gl-draw-polygon-fill-static',
                type: 'fill',
                filter: ['all', ['==', 'mode', 'static'], ['==', '$type', 'Polygon']],
                paint: {
                    'fill-color': '#404040',
                    'fill-outline-color': '#404040',
                    'fill-opacity': 0.1
                }
            },
            {
                id: 'gl-draw-polygon-stroke-static',
                type: 'line',
                filter: ['all', ['==', 'mode', 'static'], ['==', '$type', 'Polygon']],
                layout: {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#404040',
                    'line-width': 2.5
                }
            },
            {
                id: 'gl-draw-line-static',
                type: 'line',
                filter: ['all', ['==', 'mode', 'static'], ['==', '$type', 'LineString']],
                layout: {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#404040',
                    'line-width': 2.5
                }
            },
            {
                id: 'gl-draw-point-static',
                type: 'circle',
                filter: ['all', ['==', 'mode', 'static'], ['==', '$type', 'Point']],
                paint: {
                    'circle-radius': 7,
                    'circle-color': '#404040'
                }
            }
        ];

        const bigCircleStyle = {
            id: 'bigCircle',
            type: 'fill',
            source: 'bigCircleSource',
            paint: {
                'fill-color': '#FFFF00',
                'fill-opacity': 0.4,
                'fill-outline-color': '#FFFF00'
            }
        };

        map.on('load', function() {
            // Add a big circle
            const bigCircle = createGeoJSONCircle([-109, 37.75], 160, 128);
            map.addSource('bigCircleSource', bigCircle);
            map.addLayer(bigCircleStyle);

            // Add 25 random small circles
            const bounds = map.getBounds();
            const sw = bounds.getSouthWest();
            const ne = bounds.getNorthEast();
            const features = []
            for (let i = 0; i < 25; i++) {
                const rndLng = randomNumber(sw.lng, ne.lng);
                const rndLat = randomNumber(ne.lat, sw.lat);
                const circle = createFeature('circle_' + i, [rndLng, rndLat])
                const isInside = turf.inside(circle, bigCircle.data.features[0]);
                circle.properties.intersect = isInside ? 1 : 0;
                features.push(circle);
            }
            const pointsData = createFeatureCollection(features);
            map.addSource('smallCircleSource', pointsData);
            smallCircleStyle.id = 'smallCircle';
            smallCircleStyle.source = 'smallCircleSource',
            map.addLayer(smallCircleStyle);

            // Add Draw control to draw points and polygons.
            const draw = new TrimbleMapsControl.Draw({
                styles: drawStyles,
                displayControlsDefault: false,
                controls: {
                    polygon: true,
                    point: true
                },
                defaultMode: 'draw_point',
                userProperties: true
            });

            // Map#addControl takes an optional second argument to set the position of the control.
            // If no position is specified the control defaults to `top-right`.
            map.addControl(draw, 'top-left');

            const eventHandler = function(e) {
                const geometryType = e.features[0].geometry.type;
                const features = e.features;
                let intersect = false;
                if(geometryType !== 'Polygon') {
                    intersect = turf.inside(e.features[0], bigCircle.data.features[0]);
                } else {
                    const intersection = turf.intersect(e.features[0], bigCircle.data.features[0]);
                    intersect = intersection !== null ? 1 : 0;
                }
                features[0].properties.intersect = intersect ? 1 : 0;
                const polygon = createFeatureCollection(features);
                draw.add(polygon.data);
            };

            map.on('draw.create', (e) => {
                eventHandler(e);
            })

            map.on('draw.update', (e) => {
                eventHandler(e);
            })
        })
      </script>
    </body>
</html>
Last updated August 31, 2022.