I'm trying to view a group of unclustered points on the map after zooming in to level 16 but, for understandable reasons all the circles are being rendered on top of each other.
What i'm trying to achieve is, after zooming to level 16, i want to show set markers that contain a correct total number of points being rendered at that point (similar to cluster) but i want to show the GeoJson properties that is attached to it.
i've tried mini clustering it with turf that didn't work. i would like to create a marker that shows min and max of the value of all the points that are near to each other.
here's my codesandbox
https://codesandbox.io/s/late-smoke-tuc6b
Could make something that looks like it work by reading this pull requesst and relative issue
Here is the codesandbox modified :
https://codesandbox.io/s/cocky-greider-dserm
the main idea is to use these options
map.on("load", () => {
map.addSource("earthquakes", {
type: "geojson",
// Point to GeoJSON data. This example visualizes all M1.0+ earthquakes
// from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program.
data,
cluster: true,
clusterMaxZoom: 15, // Max zoom to cluster points on
clusterRadius: 60, // Radius of each cluster when clustering points (defaults to 50)
// THIS OPTION TO ADD --------------------------------------
clusterProperties: {
max: ["max", ["get", "value"]],
min: ["min", ["get", "value"]]
}
});
// ...
map.addLayer({
id: "cluster-count",
type: "symbol",
source: "earthquakes",
filter: ["has", "max"],
layout: { // the text field ------------------------
"text-field": "min:{min}, max:{max}",
"text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"],
"text-size": 12
}
});
now you have more flexibility on what you can show, but remains the part of making a marker that fits the text
I'm not a mapbox user, hope this is what you were looking for
PS: you should change your API KEY now that anybody has been able to see it ...
Related
Using this tutorial (https://docs.mapbox.com/mapbox-gl-js/example/add-terrain/) I have given my map a 3D terrain. However, when I add try to add a 3D object (GLTF format) as in this example (https://docs.mapbox.com/mapbox-gl-js/example/add-3d-model/) then the 3D object does not appear at all.
Either the object is inserted but is covered by the 3D terrain or it is not inserted at all. If it is the former, then my question would be if it is possible to adjust the height of a 3D object in a 3D terrain.
With a 2D terrain in the map and the same implementation of the 3D object, the object is visible.
Would be really grateful for any help and I'll try to answer any questions that may arise.
Yes, it is, manually you only have to modify the variable const modelAltitude = 0; which is by default at sea level. But if what you want is to calculate automatically it's a bit harder, you have to query the terrain layer as it's shown here and then use the result to set the modelAltitude variable, so you have to defer its creation and consecuently the rest of the 3D layer. Other option is to manipulate the transformation matrix array once the object is already positioned, but that's a bit of a nightmare.
As an alternative option, I was able to make it using a Mapbox plugin that I'm maintaining on GitHub, and helps to deal with the complexity of 3D objects on Mapbox.
You have an example on how to do it using the model of a glacier mixed with the Alps terrain layer
Indeed, as terrain layer is automatically detected, the object will be positioned at the altitude defined by its 3rd coordinate.
window.tb = new Threebox(
map,
map.getCanvas().getContext('webgl'),
{
realSunlight: true,
sky: true,
terrain: true,
enableSelectingObjects: true,
enableTooltips: true,
}
);
...
map.on('style.load', function () {
// stats
stats = new Stats();
map.getContainer().appendChild(stats.dom);
animate();
map.addLayer({
id: mapConfig.names.customLayer,
type: 'custom',
renderingMode: '3d',
onAdd: function (map, mbxContext) {
var options = {
obj: mapConfig.PAR.obj,
type: 'gltf',
scale: 1,
units: 'meters',
rotation: mapConfig.PAR.rotation,
anchor: 'auto'
}
tb.loadObj(options, function (model) {
model.setCoords(mapConfig.PAR.origin);
tb.add(model);
})
},
render: function (gl, matrix) {
tb.update();
}
});
});
my objective is to have a mapbox map with a custom layer overlaid on it. that custom layer should be masked by an other mapbox map.
usecase:
i have some default map. on top of that i want to render some special heatmap. that heatmap has to only be shown above roads.
current approach:
add layers to mapbox that produce a map where all roads are white and background is black
add a custom layer that copies the content of the framebuffer to a texture
add layers to render some default map
add custom layer that draws the special heatmap using the above texture as a mask.
problem:
the order in which the layers are rendered seems to not match the order in which they are added to mapbox. this example has a custom layer that outputs the color of the center pixel in the framebuffer at the moment it is called:
var map = window.map = new mapboxgl.Map({
container: 'map',
zoom: 16,
center: [8.30468, 47.05232],
style: {
"version":8,
"name":"test",
"sources": {
"openmaptiles": {
"type": "vector",
"url": "https://api.maptiler.com/tiles/v3/tiles.json?key=X0HhMcrAjHfR6MvFLSSn"
}
},
"layers":[]}
});
var getPixelLayer = {
id: 'highlight',
type: 'custom',
onAdd: function (map, gl) { },
render: function (gl, matrix) {
var pixels = new Uint8Array(4);
gl.readPixels(Math.round(gl.drawingBufferWidth/2), Math.round(gl.drawingBufferHeight/2), 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
document.getElementById("color").innerHTML = pixels[0]+","+pixels[1]+","+pixels[2];
}
};
map.on('load', function() {
//expected output: "255,255,255" everywhere
//actual output: "0,0,0" everywhere
/*
map.addLayer({"id":"bkg1","paint":{"background-color":"rgb(255, 255, 255)"},"type":"background"});
map.addLayer(getPixelLayer);
map.addLayer({"id":"bkg2","paint":{"background-color":"rgb(0, 0, 0)"},"type":"background"});
*/
//expected output: "0,0,0" everywhere
//actual output: "177,177,177" above buildings
// "0,0,0" above streets
// "0,0,0" above background
map.addLayer({"id":"bkg1","paint":{"background-color":"rgb(0, 0, 0)"},"type":"background"});
map.addLayer(getPixelLayer);
map.addLayer({"id":"roads","layout":{"line-cap":"round","line-join":"round","visibility":"visible"},"paint":{"line-color":"#fff","line-offset":0,"line-width":{"base":1.4,"stops":[[8,1.2],[16,12]]}},"source":"openmaptiles","source-layer":"transportation","type":"line"});
map.addLayer({"id":"building","paint":{"fill-color":"rgb(177, 177, 177)"},"source":"openmaptiles","source-layer":"building","type":"fill"});
});
https://jsfiddle.net/t31cj5v0/
as can be seen the rendering order seems to be background->buildings->custom_layer->streets instead of background->custom_layer->streets->buildings. The layer orderings appears correct on screen at the end, but doing what i want is not possible like this.
does anyone know what causes this behaviour and how it could be avoided?
other implementations i tried:
i also tried to render the black-white street map to a different mapbox object with synced movement. this works, but since each mapbox has its own webgl context i cant use the resulting black-white texture in the other context.
featurerequest (?)
it would be really nice if some "mask" layer could be implemented to accomplish this objective natively. but right now i'm just searching for some hack to do this. thanks in advance.
mapbox layers are drawn in 3 rendering passes. "offscreen", "opaque" and "translucent". "offscreen" is not important as the subsequent passes always draw over it. the problem appears since first all layers classifiead as "opaque" are drawn, then all layers classified as "translucent" (inside the render passes layer order is respected). because of this the rendering order does not match the declared order.
custom layers are drawn in the "translucent" pass, but because of the above it can happen that later layers are already rendered (if "opaque"). usually this is not a problem as mapbox uses a stencil buffer to track which layer goes under/over which one even if they are not drawn in order.
to make the render order match add this line after creating the mapbox instance. it will disable the "opaque" rendering pass and force every layer that was "opaque" to be "translucent":
map.painter.opaquePassEnabledForLayer = function() { return false; }
I'm a beginner of Cesium and Javascript code. I need to create the orbit of a satellite (ESEO) around the globe. The satellite have to move according to SGP4 model. How can I create it in Cesium sandcastle?
I'm in trouble because I'm not finding any tutorials or examples online that explain how to do it.
Right now I have only positioned the ground station where I want on the globe.
var viewer = new Cesium.Viewer ('cesiumContainer', {
scene3DOnly: false,
selectionIndication: false,
baseLayerPicker: true
});
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI4ODY0ZjkwMy03YmZlLTRlNGEtYmNhOS0xMDBlZGVmNDRkZGMiLCJpZCI6OTE4MSwic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTU1MzYxMjM5Mn0.aYYX1f1MQfg9zLFu0vnP3A56Neo4Y_N3G2O5tuTS0XM';
// Enable lighting based on sun/moon positions
viewer.scene.globe.enableLighting = true;
//Stazione di terra
var entity = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(12.04, 44.23),
ellipse : {
semiMinorAxis : 150000.0,
semiMajorAxis : 150000.0,
material : Cesium.Color.YELLOW.withAlpha(0.5)
}
});
var ellipse = entity.ellipse;
ellipse.material = new Cesium.GridMaterialProperty({
color : Cesium.Color.YELLOW,
cellAlpha : 0.2,
lineCount : new Cesium.Cartesian2(8, 8),
lineThickness : new Cesium.Cartesian2(2.0, 2.0)
});
var GroundStation = viewer.entities.add({
name : 'Ground Station',
position : Cesium.Cartesian3.fromDegrees(12.07, 44.23),
point : {
pixelSize : 5,
color : Cesium.Color.RED,
outlineColor : Cesium.Color.WHITE,
outlineWidth : 2
},
label : {
text : 'Ground Station',
font : '14pt monospace',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth : 2,
verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
pixelOffset : new Cesium.Cartesian2(0, -9)
}
});
When people draw orbits in Cesium, they usually use polylines instead of the ellipse entity that you use.
Link to polyline/polyline collection: https://cesiumjs.org/Cesium/Build/Documentation/Polyline.html
Now with regards to SGP4, this propagator takes in a NORAD two-line element. So if you have your orbital parameters you can create your own TLE. This should be pretty easy to do in an external python program and writing it to a JSON file which can be read into javascript and then passed to the propagator. For more info about TLEs, wikipedia has a good description of how it is formatted.
TLE info: https://en.wikipedia.org/wiki/Two-line_element_set
For the actual SGP4 implementation, there is a Satellite.js package whose github is linked below. They are able to properly implement a propagator that takes in NORAD TLE's and spits out trajectory information.
Satellite.js github: https://github.com/shashwatak/satellite-js
I am pretty sure you have to convert the TLE into different parameters you pass into the satellite-js module which propagates it.
When you are doing this, I highly recommend creating a worker thread for the code to execute because of the heavy overhead. You do not want to have all your mathematical heavylifting being done along with the graphical rendering of Cesium.
Another note of importance: You absolutely should not need to use SGP4 if you can avoid it. You should create your own propagator that fits the needs of your project. Orbit determination can be tricky but is much better practice than using a propagator meant for NORAD satellite tracking.
You can look at this project for a demonstration of some of the items listed in aaastro's answer.
jspredict
You can also checkout this great paper and supporting source for further explanations on TLE, SGP4, Cesium.
Spacemission
Visualization of Orbital Debris with Cesium and Satellite-js
I am trying to draw something similar to a radar search fence. I am finding this difficult and have tried many different types of entities to draw them. The blue fence in this picture is an example of something I am trying to draw. Here is some code that got me the furthest along but still is not correct
var orangePolygon = viewer.entities.add({
name: 'Orange polygon with per-position heights and outline',
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(
[155.5, 54.37139, 50000,
154.5, 54.37139, 50000,
135.87694, 72.37139, 500000,
175.87694, 72.37139, 500000
]),
extrudedHeight: 600000,
perPositionHeight: true,
material: Cesium.Color.ORANGE.withAlpha(0.5),
outline: true,
outlineColor: Cesium.Color.BLACK
}
});
What I'm trying to replicate or at least something similar
search fence picture
I'm trying to create a data vs time graph which allows the user to zoom in/out to large/smaller time periods. You can see an example of one such graph here:
http://people.iola.dk/olau/flot/examples/visitors.html
While the API has been a breeze for setting up that level of functionality, I've gotten stock when trying to figure out how to create a default "zoom" value - to, for instance, set the default zoom to the past 30 days (while retaining the ability for the user to zoom out and view an entire year of data).
Does anyone have any experience or know a way of doing this? Or is it a matter of digging into the source code and customizing it?
Thanks!
Walker
If you look at the code in, for example, flot zooming example, and you modify the definitions of the variable "options" thusly:
var options = {
legend: {
show: false
},
series: {
lines: {
show: true
},
points: {
show: true
}
},
yaxis: {
ticks: 10,
min: -0.8,
max: 0.4
},
selection: {
mode: "xy"
}
};
... you'll find that the graph starts out with this level of y-axis zoom, but the smaller graph still retains the full range that you can zoom out to if you want. The lines added are in the "yaxis" definition, "min" and "max". You would do something similar for "xaxis" if you want to pre-zoom the x-axis.