Openlayers 3 coordinate conversions - coordinates

Newb alert: I'm completely new to OpenLayers 3 and mapping in general. My background is in SQL Server, and back end systems design. I have no experience in HTML, JavaScript, web development etc. I'm sure this is a very simple issue but I can't seem to figure out the details.
I've modified one of the samples for OpenLayers.org and it doesn't behave as expected. It uses a GeoJSON object and draws points on the map, but they don't end up where expected. Apparently there is some conversion or something that happens.
The sample I used is here: GeoJSON example
My test map is here: Test Map
The GeoJSON object is defined as
var geojsonObject = {
'type': 'FeatureCollection',
'crs': {
'type': 'name',
'properties': {
'name': 'EPSG:3857'
}
},
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [0, 0]
}
},
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-8.575653e6, 4.70681e6]
//White House, Washington DC Lon -77.03648269999997 Lat 38.89767579999999
}
}
]
};
Through a little trial and error, I was able to get my point to display on the White House Lawn (NSA has no doubt flagged this conversation) and there is no resemblance to the latitude and longitude I pulled from a different source. I understand that the order of coordinates is [lon, lat] but I can see no rhyme or reason that -77.036 = -8.575e6. The other point [0,0] displays right where I would expect it.
I have tried to figure this out myself and searched quite a few places, but I can't seem to find the answer.
Also, if you could direct me to any good tutorials, it would be most appreciated. Thanks!

Your GeoJSON data is in EPSG:3857, a different coordinate system than latitude/longitude. The coordinates are not equal, but represent the same geographical location.
See http://spatialreference.org/ref/sr-org/6864/ for information about EPSG:3857. The map there clearly shows the difference between a given points coordinates in the coordinate reference systems.

Openlayers actually allows you to convert these using the ol.proj functions.
For instance, to convert from lat/lon (EPSG:4326) to Spherical mercator ( EPSG:3857 - the one most online web maps use, in metres) you could do:-
var newCoordinates = ol.proj.transform(latLonCoordinate, "EPSG:4326", "EPSG:3857");
newCoordinates would then contain the converted values in a coordinates array. Same can be done for 'extents' (the box coordinates for top left/bottom right of a map view) using ol.proj.transformExtent(extent, fromProj, toProj).

Related

How I can I get geojson boundary array from existing layer using mapbox gl?

I am using one of the custom tileset of tilling service in mapbox. I loaded that custom tile layer in map using below code.
map.addSource('california', {
type: 'vector',
url: 'mapbox://xyz.california'
});
map.addLayer({
'id': 'terrain-data',
'type': 'fill',
'source': 'california',
'source-layer': 'california',
'layout': {},
'paint': {
'fill-color': 'black',
'fill-opacity': 0.5
}
});
Above code is filling out the inner area with black color. But I want to fill out the outer area of that polygon. Only one way I know to do that is getting difference of whole map with that polygon by using turf.js. After that I will be able to fill the outside area.
Now the question is how can I get the geojson ploygon array of above added layer? So I can calculate the difference.
You can't easily get the complete geometry of a polygon from a vector tile set, because it has already been cut up into tiles. You would have to either find a way to merge them together, or get your geometry into the front end as a complete geojson first.

Find objects of near locations using mongodb | all nearby location A to all nearby location B

Hey MongoDB experts
I am trying to achieve some query results using MongoDB various location features ($near, $geoNear and more).
I have this mongoose model with geoJSON type.
const geoSchema = new Schema({
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
},
});
const pickupSchema = new Schema({
geo_location_from: geoSchema,
geo_location_to: geoSchema,
});
pickupSchema.index({ geo_location_from: '2dsphere' });
pickupSchema.index({ geo_location_to: '2dsphere' });
What I am trying to achieve is near by location of the event.
I have main pickup event from A to B, and as displayed in the image I have Latitude and Longitude of the all location. Now I am trying to query all of those events object from the db where event geo_location_from is near by Location A (example: A1, A2, A3 ) and geo_location_to is near by Location B ( B1, B2 ).
Here is something I did, which is not right. I am not 100% sure.
Pickup.find(
{
$and: [{
geo_location_from: {
$near: {
$maxDistance: 1000,
$geometry: {
type: 'Point',
coordinates: [args.longitude_from, args.latitude_from],
},
},
},
}, {
geo_location_to: {
$near: {
$maxDistance: 1000,
$geometry: {
type: 'Point',
coordinates: [args.longitude_to, args.latitude_to],
},
},
},
}],
},
)
Some of my try ended up giving various kind of errors.
like Too many geoNear expressions and many more.
Anybody has any good solution to this kind of problem ?
So I've been thinking of a clever way of doing all you are asking, but I think that it's very hard (if not impossible) to do that only with MongoDb.
A perfect solution would be IMHO to use turf.js alongside MongoDb.
I've come up with an approach I think might be what you are looking for.
To get all the places that are at the same time near to both two points in space, we need to define a polygon (an area) where to look for this places. Given that most of the time you have only 2 points, the polygon must be similar to a circle.
There's no way to do this with GeoJson, so turf.js comes in. You can do something like:
// create the points
let point1 = turf.point([-90.548630, 14.616599]);
let point2 = turf.point([-88.548630, 14.616599])
// we get the midpoint that we'll use as the center of our circle
const midPoint = turf.midpoint(point1, point2)
// we create a polygon that we'll use as our area
const options = {steps: 100, units: 'kilometers'}; // options for the circle
const circle = turf.circle(midPoint, options)
// a possible place near both locations
let point3 = turf.point([-91.43897, 14.56784])
// then you can get if a point is inside the circle or not
const inside = turf.booleanWithin(point3, circle)
This is just an idea. You can customize the radius of your circle to get further or nearer places. Then you can of course order the places you've found inside the circle by proximity to the center (this can be done easily in MongoDb).
I'll suggest you to look closely at the turf.js and MongoDb docs to have a clearer idea on how to make them work together seamlessly (and maybe to find a better, easier solution than mine).

Mapbox-gl fill layer distorts rendering on higher zoom

I'm using mapbox-gl version 0.46.0-beta.1 (With ReactJS).
I've drawn simple fill layers using GeoJSON source.
The coordinates used to draw the fill are
Coordinates Data:
At a specific zoom level, the polygon renders as expected.
Expected Layer:
But on zooming in, the polygon distorts.
Distorted Fill Layer:
The code I'm using is:
`let paint = {
'fill-color': 'Some Color',
'fill-opacity': 0.4,
'fill-outline-color': 'Some Color'
}
let uniqueID = 'someuniqueID';
map.addLayer({
'id': uniqueID,
'type': 'fill',
'source': {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [polyCoords]
}
}
},
'paint': paint
})`
We were able to solve the same problem by making sure the first point in the polygon is also the last. For this polygon, you should add the point:
[28.6045067810482..., 77.3860210554725...]
I also had this issue. For a bounding box of coordinates for mapbox, having just the 4 coordinate pairs to create the polygon does create rendering artifacts, directly like above. When making your coordinate bounding box as a polygon, be sure to enter a 5th record, which would be the same as your coordinate at coor[0], where coor is your array of coordinate pairs. You will only notice the weird rendering and re-drawing when zooming, this in fact made it go away for me.
For example:
var coors = [[+sw_lng, +sw_lat], [+sw_lng, +ne_lat], [+ne_lng, +ne_lat], [+ne_lng, +sw_lat]]; //makes a 4 pointed polygon
coors.push(coors[0]); //then continue

Mapbox GL JS: zoom to filtered polygon?

I am using Mapbox GL JS to display a polygon layer. I would to allow the user to choose a name from a dropdown, and then highlight and zoom to the matching polygon.
I already know how to highlight the matching polygon using map.setFilter, but I don't know how to zoom to the bounds of the matching polygon. This is my current code:
map.addLayer({
'id': 'polygon_hover',
'source': 'mysource',
'source-layer': 'mylayer',
'type': 'fill',
'paint': {
'fill-color': 'red',
"fill-opacity": 0.6
},
"filter": ["==", 'CUSTNAME', ""]
});
// Get details from dropdown
custname.on("change", function(e) {
// get details of name from select event
map.setFilter('polygon_hover', ["==", 'CUSTNAME', name]);
// Get bounds of filtered polygon somehow?
// var bounds = ??;
// map.fitBounds(bounds);
});
I have examined the Mapbox example of zooming to bounds, but it assumes that you already know what the bounds are.
Is there any way it's possible to get the bounds of the polygon matching a map filter in Mapbox?
I've the following code to fitBounds to Polygon center coords:
var coordinates = f.geometry.coordinates[0];
var bounds = coordinates.reduce(function (bounds, coord) {
return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(coordinates[0], coordinates[0]));
map.fitBounds(bounds, {
padding: 20
});
Where f is one Feature.
I found a solution to your problem. Leaflet has a polygon Class which takes an Array of polygon coordinates and has a function called getBounds() that returns south west and north east bounds. However, Leaflet doesn't follow the convention of LngLat, its format is LatLng. Therefore, you have to switch it. I took an example out from Mapbox Show drawn polygon area, and added exactly what you're looking for.
var polygon = data.features[0].geometry.coordinates;
var fit = new L.Polygon(polygon).getBounds();
var southWest = new mapboxgl.LngLat(fit['_southWest']['lat'], fit['_southWest']['lng']);
var northEast = new mapboxgl.LngLat(fit['_northEast']['lat'], fit['_northEast']['lng']);
var center = new mapboxgl.LngLatBounds(southWest, northEast).getCenter();
// map.flyTo({center: center, zoom: 10});
map.fitBounds(new mapboxgl.LngLatBounds(southWest, northEast));
I see that the question is still relevant - I solved it making a separate request to the database containing all points of a given polygon and building bounds [[minLng, minLat], [maxLng, maxLat]].
All attempts to address geometry of already rendered or source features didn't work for me - most probably because Mapbox doesn't keep initial geoJSON in the tiles.

Mapbox GL JS Bearing

Is it possible in Mapbox GL JS to get the users bearing?
I would like to show the direction in which the user is facing, to assist them in navigating to nearby POI.
I understand that it is possible to set the bearing of the map and also get the current bearing of it, but i need the actual real life bearing of the user.
Kind of the same thing as on Google Maps:
The service is intended to run as an Ionic app on iOS and Android, and the assistance in bearing is a key feature in helping them locate nearby POI on a well populated map.
You can get the user's bearing (if their device has such a sensor) by obtaining a Coordinates object from Gelocation#getCurrentPosition() and reading Coordinates#heading.
Mapbox GL JS has no built-in user interface for displaying a user's heading. Building your own user interface is easy. See this example which uses the symbol-rotation property.
So, after some time spend on this, i thought I'd show how i ended up doing this, in case someone else needs it or have a better solution.
It seems cordova has a built in "heading" property in the position object.
https://github.com/apache/cordova-plugin-geolocation
var heading = $rootScope.position.heading;
First, i make sure that the marker is always pointing in the heading direction, even when the user turns the map, by subtracting the mapBearing(degrees the map has turned from North), from the user heading.
map.on('rotate', function(){
map.setLayoutProperty('drone', 'icon-rotate', heading - map.getBearing())
});
I create an icon, at the users position, add the source and add the layer with the source.
map.on('load', function () {
var point = {"type": "Point", "coordinates": [$rootScope.position.long, $rootScope.position.lat]};
map.addSource('drone', {type: 'geojson', data: point });
map.addLayer({
"id": "drone",
"type": "symbol",
"source": "drone"
}
});
Next i check that heading is actually available, since it only appears to return a value, when the user is moving(only tested on Android so far), and if it is, update the heading of the point.
if($rootScope.position.heading){
var heading = $rootScope.position.heading;
map.setLayoutProperty('drone', 'icon-rotate', $rootScope.position.heading);
};
Finally i update the position of the point, in a "$watch" position.
map.getSource('drone').setData(point);
This way, i can watch the users heading, and the point keeps on track, even when the user rotates the map.
For the users coming here after 2020 (what an year lol), mapbox gl js now supports geolocation which not only provides user's heading but also a bunch of other helpful data:
const geolocate = map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
})
)
then listen for geolocate event:
geolocate.on('geolocate', (e) => {
console.log(e);
});
this will give you following object:
{
coords: {
accuracy: number;
altitude: number;
altitudeAccuracy: number;
heading: number;
latitude: number;
longitude: number;
speed: number;
};
timestamp: number;
heading will give you direction of the user. As the geolocate control keeps triggering automatically so can get the user's direction as well as speed and altitude etc in real time and use that to display data driven symbols on map.