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