I need to build a simple online map editor and I'm intended to use leaflet (but can eventually use openlayers). How can I draw graph elements (nodes and edges) in leaflet (on top of a map). Specifically, I can draw linestrings (edges) and circles (nodes), but they are not correlated in any way, that is, the linestring is not an edge of the point since they are two different geometries. Hence, how can I draw graph elements, nodes and edges, where edges and nodes are associeted.
Example:
Here, the circles should represent a node, while the polylines are the edges. However, the two geometries are not associated in any way, that is, I can't associate the edges with the nodes. What I want to know is how to draw a graph on top of such mach where I can add, retrieve, delete edges and nodes.
I'm not sure why you don't want to directly create a polyline, but you can implement your request as follows:
// the given points array
let points = [
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
];
// iterate through the points to dynamically create the elements
for(let i = 0; i < points.length; i++)
{
// every point creates a circle (node)
L.circle(points[i], {radius: 20}).addTo(mymap)
// every pair of adjacent points creates an edge,
// other logic can be implemented
if(i + 1 < points.length){
L.polyline([points[i], points[i+1]]).addTo(mymap);
}
};
The resulted looks like that:
If you still want a whole polyline, without nodes and edges separation, please use L.polyline like so:
// create a polyline from an array of points
let polyline = L.polyline(points).addTo(map);
You can find some further reading on the different options of polylines and circles here and here.
A possible API for a graph format like you asked, for add, retrieve and delete nodes and edges elements, can be as follows:
// points represented by circles
function addNode(lat, lng){
L.circle([lat, lng], {radius: 20}).addTo(mymap);
}
function deleteNode(node){
node.remove();
}
function retrieveNode(node){
let latlng = node.getLatLng();
return [latlng.lat, latlng.lng];
}
// edges represented by polylines
function addEdge(nodeA, nodeB){
L.polyline([retrieveNode(nodeA), retrieveNode(nodeB)]).addTo(mymap);
}
function deleteEdge(edge){
edge.remove();
}
function retrieveEdge(edge)
{
let line = edge.getLatLngs();
return [[line[0].lat, line[0].lng], [line[1].lat, line[1].lng]];
}
By adding a Graph class, you can continue this direction, and further abstract your map building.
Related
I am working on a bike-sharing application where I have the following requirements.
Show all bikes near the user locations in a certain radius.
For this I came with an approach where get all indexes of bike
locations on a specific resolution and get all the indexes from the user location
with a radius using kRing function and then find all the bike locations which are
inside kRing indexes.
const res = 8;
//get hexagon index of all locations
const lookupMap = bike_locations.reduce((map, bike) => {
const {latitude, longitude} = bike;
const h3Index = h3.geoToH3(latitude, longitude, res);
if (!map[h3Index]) map[h3Index] = [];
map[h3Index].push(bike.id);
return map;
}, {});
//hexagon index of user location
const origin = h3.geoToH3(user_lat, user_logn, res);
const radius = Math.floor(distance_in_km / (h3.edgeLength(res, h3.UNITS.km) * 2));
const lookupIndexes = h3.kRing(origin, radius);
// Find all points of bikes in those indexes
const results = lookupIndexes.reduce(
(output, h3Index) => [...output, ...(lookupMap[h3Index] || [])],
[]);
I have a big circle with centre lat/long, the radius of the circle in km and these big circle divided into multiple polygons with data points.
How to get all the bikes that are outside the city boundary (i.e big circle)?
How to get in which polygon how many bikes are existing and how to get in which polygon a given bike location present using polyfill?
How to display all the bike locations, polygons, big circle and filled with hexagons on a map? here I am using MapmyIndia.
How can I implement the above requirements using Uber H3-js and my solution for the point is correct or any better solution is there for the same?
There are a lot of questions here, and I'm not sure I understand them all, but here's my attempt:
K-ring lookup
The k-ring based radius lookup seems like a reasonable solution (looks like it's based on this answer), particularly if the bike lookup map can be calculated once rather than every time you need to perform the lookup.
Bikes not in circle
Getting all the bikes not in the circle works in a similar way. If you need the circle to be more circular than a k-ring, you'd need to calculate the set of hexes within the circle - there isn't an H3 function for this, but a reasonable approach might look like:
Calculate a k-ring of cells that covers the circle
Check the haversine distance from each cell center to the circle center
If distance <= radius add the cell to the circle set
Now you have a set of cells representing the circle, filter out all bikes whose cells are in that set. The remaining bikes are outside the circle.
Bikes in polygons
To take a set of polygons and use it for lookup, you'll need a reverse index (cell => polygon). You can create it like this (untested):
const lookup = {};
for (const poly of polygons) {
for (const cell of h3.polyfill(poly, res)) {
lookup[cell] = polygon; // or polygon id, or whatever
}
}
Now you can use the index to easily lookup the polygon for a particular bike. To count the bikes in a polygon, you can either loop through all the bikes and count which ones are inside, or loop through all the cells in the polygon and determine which ones have a bike - these are both O(n), so the better option just depends on which n is smaller, bikes or cells.
I have made my own vector tile .pbf data, consisting of polygons, lines and points.
The source of .pbf data are from a grouplayer of shapefiles in Geoserver, where each shapefile has been ordered so that points are at the top:
Using OpenLayers, I've styled the vector tiles and followed the example for "Vector Tile Info". The difference is mine is toggled by single click.
The problem is that I can't click on any point, even as a sprite image, if it is completely within a polygon.
It's possible to click on lines that are completely within a polygon.
Underlying polygons are selected as feature[0] when attempting to select a point.
What might be the cause of this render order? That a point is only a single pixel?
What might be the remedy?
I'm very new to JavaScript and OpenLayers so I'm hoping it's something naive.
I need to be able to prioritize the points when they're surrounded by a polygon.
The order of feature returned by map.getFeaturesAtPixel() seems to be random and not related to render order. Either check all the features returned, not just entry [0], or sort the array so Points/MultiPoints are lower in the sort order than LineStrings/MultiLineStrings with Polygons last
function rank(feature) {
var type = feature.getType();
if (type.indexOf('Point') > -1) {
return 1;
} else if (type.indexOf('LineString') > -1) {
return 2;
} else {
return 3;
}
}
map.getFeaturesAtPixel(event.pixel).sort(function(a, b) {
return rank(a) - rank(b);
});
I want to display a track in real-time with OpenLayers 3 which disolves at the end, just like a snails trail.
Only appending new coordinates to a LineString is easy. See this example. But removing coordinates from the end of the line does not seem to be supported by the API.
How should I go about that? Is extending the LineString class the only option? Or should I use a separate feature for each line segment?
Update:
I used this code with ol-debug.js. But get/setFlatCoordinates is not exported in the compiled version.
var flatCoordinates = geometry.getFlatCoordinates(); // not exported
if (flatCoordinates && flatCoordinates.length > 100) {
// remove first coordinate elements from array
flatCoordinates.splice(0, geometry.getStride());
// call push with coordinate elements as arguments
Array.prototype.push.apply(flatCoordinates, coordinate);
// update coordinates calling change()
geometry.setFlatCoordinates(geometry.getLayout(), flatCoordinates);
}
else {
geometry.appendCoordinate(coordinate);
}
The appendCoordinate method is a shortcut for the fairly common use case of adding a coordinate to the end of a LineString. To modify geometries with more control, set your desired coordinates with setCoordinates.
var maxCoords = 100;
var coords = lineString.getCoordinates(); // get coordinate array
coords.unshift(newCoord); // add to beginning of array
if (coords.length > maxCoords) {
coords.length = maxCoords;
}
lineString.setCoordinates(coords);
Does mxGraph have a specific polyline object? That is, an edge that goes through a number of points. At the moment I'm faking it using multiple straight edges linked by invisible vertices, but this messes up the graph structure.
Waypoints can be added to edges in mxGeometry.points. To change them you need to clone any existing geometry object (in-place changes cause undo problems):
var geometry = model.getGeometry(edge);
geometry = geometry.clone();
geometry.points = points;
Assuming edge is the edge object to alter and points is an array of mxPoint.
The terminal points for dangling edges can be changed via mxGeometry.setTerminalPoint(mxPoint, boolean).
I am drawing a map with D3.js importing it from a topojson file.
In order to draw it I user the following code:
d3.json(input_file, function(error, data) {
var units = topojson.feature(data, data.objects.countries),
view = d3.select("#map_view");
// ... SETUP OF THE PROJECTION AND PATH
view.selectAll("path")
.data(units.features)
.enter()
.append("path")
.attr("d", path);
}
This code will plots all the shapes defined in the input topojson file.
Now I want to remove few outlier small islands which I don't need which are located in a specified region in spherical coordinates.
Since now units object defines everything that is drawn, I tried to remove the shapes in the following way:
I identify the index of the country in the features list and index of the arc that lies in the desired region and remove the arc from the array:
var coordinates = units.features[5].geometry.coordinates,
outlierId = 23;
coordinates.splice(outlierId, 1);
Now when I check the coordinates array of this country I see no arc with coordinates in the excluded region.
But it is still drawn on the map although the modified units object enters the .data() method. It looks like information about the shapes is coming from somewhere else. How can it be?