Draw area on Map based on square foot.

Balasaheb Molawade 116 Reputation points
2023-09-04T09:36:58.3933333+00:00

Hi Team.

We are using the Bing Maps v8 control to develop applications for our customers.

We have a requirement to draw an area based on square footage. We will have values in the fields as shown below. Using these values, they want to draw the area. Allow me to explain our use case. Our customers are agencies that manage restaurants in the USA. They designate specific locations or areas for restaurants. They need to draw boundaries to ensure that restaurants are not built outside of these designated areas and do not encroach upon sidewalk spaces. By plotting the restaurants within these predefined boundaries, they can easily monitor compliance. If a restaurant exceeds the allocated area, they will notify the restaurant owner to rectify the situation. 

Below is a screenshot of how the customer displays the values.

Is it possible to draw an area based on square foot using Bing Maps. Is there a sample code available.

Thanks!

Azure Maps
Azure Maps
An Azure service that provides geospatial APIs to add maps, spatial analytics, and mobility solutions to apps.
587 questions
{count} votes

1 answer

Sort by: Most helpful
  1. rbrundritt 15,231 Reputation points Microsoft Employee
    2023-09-12T01:26:16.2233333+00:00

    Based on the long conversation in the comments, I've put together a code sample that uses Open Steet Map data to get the nearest road or sidewalk to the center point using the Overpass Turbo API. I then used that to determine the heading of the road/sidewalk and aligned the length edge of the rectangle with that when calculating the points of a rotated rectangle. Here is the full source code (add your own Bing Maps Key):

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
        <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
        <style type='text/css'>
            body {
                margin: 0;
                padding: 0;
                overflow: hidden;
                width: 100%;
                height: 100%;
            }
    
            #myMap {
                position: relative;
                width: 100%;
                height: 100%;
            }
    
            .inputPanel {
                position: absolute;
                top: 10px;
                left: 10px;
                background-color: white;
                padding:10px;
                border-radius: 10px;
            }
        </style>
    </head>
    <body>
        <div id="myMap"></div>
    
        <div class="inputPanel">
    		<table>
    			<tr>
    				<td>Center:</td>
    				<td><input type="text" id="coordTbx" value="47.674516, -122.125789"/></td>
    			</tr>
    			<tr>
    				<td>Length:</td>
    				<td><input type="number" id="lengthTbx" value="80" min="0"/> ft</td>
    			</tr>
    			<tr>
    				<td>Width:</td>
    				<td><input type="number" id="widthTbx" value="10" min="0"/> ft</td>
    			</tr>
    		</table>
    
            <input type="button" value="Calculate" onclick="calculate();"/>
    	</div>
    
        <script type='text/javascript'>
            var map;
    
            var overpassTurboRoadQuery = `
                /*
                This has been generated by the overpass-turbo wizard.
                The original search was:
                “highway=* and type:way”
                */
                [out:json][timeout:25];
                // gather results
                (
                  // query part for: “highway=*”
                  way["highway"]({{bbox}});
                );
                // print results
                out body;
                >;
                out skel qt;
            `;
    
            function loadMapScenario() {
                map = new Microsoft.Maps.Map(document.getElementById('myMap'), {
                    zoom: 4
                });
    
                //Load the GeoJSON and Spatial math modules.
                Microsoft.Maps.loadModule(['Microsoft.Maps.GeoJson', 'Microsoft.Maps.SpatialMath']);
            }
    
            function calculate() {
                //Clear all data on the map.
                map.entities.clear();
    
                //Capture the input data.
                var center = Microsoft.Maps.Location.parseLatLong(document.getElementById('coordTbx').value);
                var lengthFt = parseFloat(document.getElementById('lengthTbx').value);
                var widthFt = parseFloat(document.getElementById('widthTbx').value);    
                
                //Add the center point to the map.
                var centerPin = new Microsoft.Maps.Pushpin(center);
                map.entities.push(centerPin);
    
                //Center the map.
                map.setView({ center: center, zoom: 18 });
    
                //Validate input
                if (isNaN(lengthFt) || isNaN(widthFt)) {
                    alert('Please enter a valid number for the length and width.');
                    return;
                }
    
                if(!center) {
                    alert ('Invalid latitude, longitude value provided');
                }
    
                //Convert feet to meters.
                var lengthMeters = lengthFt * 0.3048;
                var widthMeters = widthFt * 0.3048;   
                
                //Get data from OSM.
                getOsmData(center, widthMeters, lengthMeters);
            }
    
            function getOsmData(center, widthMeters, lengthMeters) {
    
                //Overpass turbo requires a bounding box to be specified in the form of south,west,north,east. 0.0001 degrees is roughly 10 meters.
                var bbox = (center.latitude - 0.0001) + ',' + (center.longitude - 0.0001) + ',' + (center.latitude + 0.0001) + ',' + (center.longitude + 0.0001);
    
                //Create request for Overpass Turbo that retrieves road and sidewalk data.
                var query = cleanQuery(overpassTurboRoadQuery.replace(/\{\{bbox\}\}/gi, bbox));
    
                //Make a request to Overpass Turbo.
                fetch('https://overpass-api.de/api/interpreter', {
                    method: 'POST', // *GET, POST, PUT, DELETE, etc.
                    mode: 'cors', // no-cors, *cors, same-origin
                    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
                    },
                    body: 'data=' + query
                }).then(x => x.json()).then(osm_data => {
                    //Convert the OSM data to GeoJSON
                    var data = osmtogeojson(osm_data);
    
                    //Convert the GeoJSON data to Bing Maps objects.
                    var shapes = Microsoft.Maps.GeoJson.read(data);
    
                    //Loop through the shapes and find the closest one to the center point using the spatial math module.
                    var closestShape, closestDistance = Infinity;
    
                    for (var i = 0; i < shapes.length; i++) {
                        var distance = Microsoft.Maps.SpatialMath.Geometry.distance(center, shapes[i]);
                        if (distance < closestDistance) {
                            closestShape = shapes[i];
                            closestDistance = distance;
                        }
                    }
    
                    //Loop through the line segments in the closet shape and find the closest segment.
                    var closestSegment, closestSegmentDistance = Infinity;
                    
                    var path = closestShape.getLocations();
    
                    for (var i = 0; i < path.length - 1; i++) {
                        var distance = Microsoft.Maps.SpatialMath.Geometry.distance(center, path[i], path[i + 1]);
                        if (distance < closestSegmentDistance) {
                            closestSegment = [path[i], path[i + 1]];
                            closestSegmentDistance = distance;
                        }
                    }     
    
                    //Calculate the heading of the line segment.
                    var heading = Microsoft.Maps.SpatialMath.getHeading(closestSegment[0], closestSegment[1]);
    
                    //Calculate the distance from the center of the rectangle to each point.
                    var radiusMeters = Math.sqrt(Math.pow(lengthMeters * 0.5, 2) + Math.pow(2, widthMeters * 0.5));
    
                    //Calculate the angle to the first point in the rectangle.
                    var angleRadians = Math.atan2(widthMeters * 0.5, lengthMeters * 0.5);
    
                    //Calculate the angle in degrees.
                    var angle = normalizeHeading(angleRadians * 180 / Math.PI);
    
                    //Calculate the heading of the first point in the rectangle.
                    var heading1 = normalizeHeading(heading - angle);
    
                    //Calculate the heading of the second point in the rectangle.
                    var heading2 = normalizeHeading(heading1 + angle);
    
                    //Calculate heading of the third point in the rectangle.
                    var heading3 = normalizeHeading(heading1 + 180);
    
                    //Calculate heading of the third point in the rectangle.
                    var heading4 = normalizeHeading(heading2 + 180);
    
                    //Calculate the points of a rectangle when unrotated.
                    var points = [
                        Microsoft.Maps.SpatialMath.getDestination(center, heading1, radiusMeters),
                        Microsoft.Maps.SpatialMath.getDestination(center, heading2, radiusMeters),
                        Microsoft.Maps.SpatialMath.getDestination(center, heading3, radiusMeters),
                        Microsoft.Maps.SpatialMath.getDestination(center, heading4, radiusMeters)
                    ];
    
                    //CLose the rectangle polygon (first and last points should be the same).
                    points.push(points[0]);
    
                    //Rotate the points to match the heading of the road.
                    //points = Microsoft.Maps.SpatialMath.Geometry.rotate(points, heading, center);
    
                    //Create a polygon from the points.
                    var rect = new Microsoft.Maps.Polygon(points, {
                        fillColor: 'rgba(0,0,255,0.5)',
                        strokeColor: 'blue'
                    });
    
                    //Add the polygon to the map.
                    map.entities.push(rect);
    
                }, e => {
                    aler('Unable to retrieve data from overpass.');
                });
            }
    
            function cleanQuery(query) {
                //Remove comments /* */ and //
                query = query.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '');
    
                //Remove new lines characters and surrounding whitespace.
                query = query.replace(/[\s\t]*\n[\s\t]*/gi, '');
    
                //Encode the query.
                return encodeURIComponent(query);
            }
    
            function normalizeHeading(heading) {
                //Make the heading between 0 and 360.
                heading = heading % 360;
    
                if(heading < 0){
                    heading += 360;
                }
    
                return heading;
            }
        </script>
    
        <!-- Load a library that converts Overpass Turbo responses to GeoJSON. -->
        <script type="text/javascript" src="https://tyrasd.github.io/osmtogeojson/osmtogeojson.js"></script>
    
        <!-- Load the Bing Maps SDK -->
        <script type="text/javascript" src="https://www.bing.com/api/maps/mapcontrol?key=[YOUR_BING_MAPS_KEY]&callback=loadMapScenario"' async defer></script>
    </body>
    </html>
    

    Here is a live version of this code running: https://rbrundritt.azurewebsites.net/Demos/BingMaps/RoadAlignedRectangles.html