how to put threejs building on mapbox to its real place - three.js

currently i've load eiffel tower obj file, and render it using threejs, but how can i put the building on map to its place in real world. i use mapgox-gl-js to handle map issues, for its convenience on 3d map.
style: {
"version": 8,
"sources": {
"satellite": {
"type": "raster",
"url": "mapbox://mapbox.satellite",
"tileSize": 256
},
"canvas": {
type: 'canvas',
canvas: 'idOfMyHTMLCanvas',
// animate: true,
coordinates: [
[-74.02204952394804, 40.706782422418456],
[-73.99115047610259, 40.706782422418456],
[-73.99115047610259, 40.72021689994298],
[-74.02204952394804, 40.72021689994298]
],
contextType: 'webgl'
}
},
"layers": [{
"id": "satellite",
"type": "raster",
"source": "satellite"
}, {
"id": "video",
"type": "raster",
"source": "canvas"
}]
}
thank you for any help.

You may want to check out Threebox, which is designed to sync a Three.js scene graph with a Mapbox GL JS map.

This question is quite old, but indeed as suggested by #lauren-budorick, it took me 5 minutes to do this sample using the latest version of threebox and the result is like this
<script>
mapboxgl.accessToken = 'PASTE HERE YOUR TOKEN';
var origin = [2.294514, 48.857475];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/satellite-v9',
center: origin,
zoom: 18,
pitch: 60,
bearing: 0
});
map.on('style.load', function () {
map
.addLayer({
id: 'custom_layer',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, mbxContext) {
window.tb = new Threebox(
map,
mbxContext,
{
defaultLights: true,
}
);
// import tower from an external glb file, downscaling it to real size
// IMPORTANT: .glb is not a standard MIME TYPE, you'll have to add it to your web server config,
// otherwise you'll receive a 404 error
var options = {
obj: '/3D/eiffel/eiffel.glb',
type: 'gltf',
scale: 0.01029,
units: 'meters',
rotation: { x: 0, y: 0, z: 0 }, //default rotation
adjustment: { x: -0.5, y: -0.5, z: 0 } // place the center in one corner for perfect positioning and rotation
}
tb.loadObj(options, function (model) {
model.setCoords(origin); //position
model.setRotation({ x: 0, y: 0, z: 45.7 }); //rotate it
tb.add(model);
})
},
render: function (gl, matrix) {
tb.update();
}
});
})
</script>

I just stumbled across this question and wanted to provide an updated answer for anyone else who ends up here. At the time the question was asked, this was not possible in Mapbox GL JS without a plugin but it can be achieved now with the CustomLayerInterface.
Here's an example of adding a Three.js model to a Mapbox GL JS map.

Related

Phaser 3.17 doesn't setect collision between tilemap layer and player sprite

No collisions between map layer and player sprite. But collisions between world bounds work. What is wrong?
I tried different workarounds that could find online and none of them worked.
Game config
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: "#f",
physics: {
default: 'arcade',
arcade: {
gravity: { y: gameState.gravity },
debug: true
}
},
scene: {
preload,
create,
update
}
};
Code in create() regarding the tilemap and its layers and the character
gameState.map = this.add.tilemap('map');
gameState.dungeonTileset = gameState.map.addTilesetImage('dungeon', 'dungeonTiles');
gameState.backgroundLayer = gameState.map.createStaticLayer('Background', gameState.dungeonTileset);
gameState.mapLayer = gameState.map.createStaticLayer('Map', gameState.dungeonTileset);
gameState.miscLayer = gameState.map.createStaticLayer('Misc', gameState.dungeonTileset);
gameState.mapLayer.setCollisionByExclusion([-1]);
this.physics.world.bounds.width = gameState.mapLayer.width;
this.physics.world.bounds.height = gameState.mapLayer.height;
gameState.player = this.physics.add.sprite(73, 398, 'player', 0);
gameState.player.setCollideWorldBounds(true);
this.physics.add.collider(gameState.player, gameState.mapLayer);
No warning and no errors are coming up in the console. I don't know what to do anymore.
Thanks in advance!
I have addited a little bit the original example so you can see how it looks, I think the problem in your code is the line concerning gameState.mapLayer.setCollisionByExclusion([-1]); but am not sure because I can't see how the tilemap is created
var config = {
type: Phaser.WEBGL,
width: 800,
height: 576,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
loader: {
baseURL: 'https://labs.phaser.io',
crossOrigin: 'anonymous'
},
pixelArt: true,
physics: {
default: 'arcade',
arcade: { gravity: { y: 300 } }
},
scene: {
preload: preload,
create: create,
update: update
}
};
var game = new Phaser.Game(config);
var map;
var cursors;
var player;
var groundLayer;
function preload ()
{
this.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/tile-collision-test.json');
this.load.image('player', 'assets/sprites/phaser-dude.png');
}
function create ()
{
map = this.make.tilemap({ key: 'map' });
var groundTiles = map.addTilesetImage('ground_1x1');
map.createDynamicLayer('Background Layer', groundTiles, 0, 0);
groundLayer = map.createDynamicLayer('Ground Layer', groundTiles, 0, 0);
groundLayer.setCollisionBetween(1, 25);//here
player = this.physics.add.sprite(80, 70, 'player')
.setBounce(0.1);
this.physics.add.collider(player, groundLayer);
cursors = this.input.keyboard.createCursorKeys();
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.startFollow(player);
}
function update ()
{
player.body.setVelocityX(0);
if (cursors.left.isDown)
{
player.body.setVelocityX(-200);
}
else if (cursors.right.isDown)
{
player.body.setVelocityX(200);
}
if (cursors.up.isDown && player.body.onFloor())
{
player.body.setVelocityY(-300);
}
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.17.0/dist/phaser.min.js"></script>

Programatically placing marker in drilldown map amchart

I have a map which is a drilldown map. It goes from continents view to a country view.
My goal is to place markers dynamically based on the selected country (after the drilldown).
Here in this example I want to place a marker in Berlin (Germany) however this marker doesn't get created.
Example: https://codepen.io/ms92o/pen/gjMPEJ?editors=1111
var map = AmCharts.makeChart("chartdiv", {
"type": "map",
"theme": "light",
"areasSettings": {
"autoZoom": true,
"rollOverOutlineColor": "#9a7bca",
"selectedColor": "#9a7bca",
"color": "#a791b4",
"rollOverColor": "#9a7bca"
},
"zoomControl": {
"buttonFillColor": "#a6bd7f",
"buttonRollOverColor": "#9a7bca"
},
"dataProvider": continentsDataProvider,
"objectList": {
"container": "listdiv"
},
"listeners": [{
"event": "clickMapObject",
"method": function (event) {
console.log(event);
// TODO: how to create some markers here based on the selected country?
let rep = { title: 'Berin', latitude: '52.520', longitude: '13.409779' };
rep.svgPath = targetSVG;
rep.zoomLevel = 3;
rep.scale = 1.2;
rep.label = rep.title;
map.dataProvider.images.push(rep);
}
}]
});
You need to call the map's validateNow()/validateData() methods whenever you update the map with new areas/markers or changes to its properties. The caveat of these calls is that they reset the map's zoom position unless you modify the dataProvider's zoom properties (zoomLevel, zoomLatitude and zoomLongitude), which also affects the home button unless you reset them after the fact.
Here's a solution that adds the marker while making sure the zoom level sticks and fixes the home button afterward:
"listeners": [{
"event": "clickMapObject",
"method": function (event) {
let rep = { title: 'Berin', latitude: '52.520', longitude: '13.409779' };
rep.svgPath = targetSVG;
rep.zoomLevel = 3;
rep.scale = 1.2;
rep.label = rep.title;
map.dataProvider.images.push(rep);
//delay the update so that the click+zoom action still occurs before
//adding the marker
setTimeout(function() {
//preserve current zoom level on update
map.dataProvider.zoomLevel = map.zoomLevel();
map.dataProvider.zoomLatitude = map.zoomLatitude();
map.dataProvider.zoomLongitude = map.zoomLongitude();
map.validateNow(); //add marker
//reset the zoom values so that the home button zooms
//completely out when clicked
map.dataProvider.zoomLevel = 0;
map.dataProvider.zoomLatitude = undefined;
map.dataProvider.zoomLongitude = undefined;
}, (map.zoomDuration + .5) * 1000);
}
}]
Updated codepen

How to pass an array of objects as uniforms to custom shader in Three.js

I would like to pass an array of Javascript objects to my custom shader written with THREE.RawShaderMaterial but still haven't been able to figure out how to achieve this.
Specifically, I would like to manually perform the same thing as THREE.PointLightSource. I am currently achieving this by using Three.js's functionality:
var matrixUniforms = {
projectionMat: { value: new THREE.Matrix4() },
modelViewMat: { value: new THREE.Matrix4() },
normalMat: { value: new THREE.Matrix3() },
};
var material = new THREE.RawShaderMaterial( {
uniforms: THREE.UniformsUtils.merge( [
matrixUniforms,
THREE.UniformsLib[ "lights" ],
] ),
vertexShader: $( "#" + vShader ).text(),
fragmentShader: $( "#" + fShader ).text(),
lights: true,
} );
Let's say I have
var pointLights = [
{ position: new THREE.Vector3(0,0,0),
color: new THREE.Vector3(1,1,1) },
{ position: new THREE.Vector3(10,10,10),
color: new THREE.Vector3(2,2,2) }
];
How can I add it to uniforms of THREE.RawShaderMaterial? Is there any easy way to pass an array of objects that has the same properties? I may have found its solution for the older version of Three.js but couldn't find a way to do it with the latest version (r84).
EDIT:
I found this thread and this thread. However, it seems that it is not documented anywhere...

Transform too dense data

Trying to display line chart using plotly.js, my data are collected per second. I fed my graph but the result looks strange even if I zoom in, to very low detail where it should be displayed per seconds.
Are there any methods I could use to preprocess the data so it would display well in different scales (as I zoom in and out)?
var gd = document.getElementById('tester');
var layout = {
xaxis: {
showgrid: true,
tickformat: "%H:%M:%S",
},
margin: {
l: 40,
b: 40,
r: 30,
t: 20
},
hovermode: 'x',
};
var draw = function(data, layout) {
Plotly.newPlot(gd, data, layout, {
showLink: false,
displaylogo: false
});
};
var dataurl = 'https://gist.githubusercontent.com/fhurta/6c53839fbc91a363d62966a972a5e4a2/raw/2cd735f0b024e496164dacec92fa4a7abcd5da2e/series.csv';
Plotly.d3.csv(dataurl, function(rows) {
var data = [{
type: 'scatter',
x: rows.map(function(row) {
return new Date(row['Time']);
}),
y: rows.map(function(row) {
return row['Value1'];
}),
line: {
width: 1
}
}];
draw(data, layout);
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="tester" style="width:600px;height:300px;"></div>

Highmaps limit zoom range breaks zooming out

I am using Highmaps and I limit the zoom range by setting the minRange property in the xAxis property.
This limits the zoom in factor as expected, but when zooming in too much, it does not let you zoom out again until you drag the map. To be more precise, it lets you zoom out until the x-axis is completely visible, but if your viewport is too wide to display the whole y-range of the map at this particular zoom step, you only get a horizontal stripe.
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=world-population-density.json&callback=?', function (data) {
// Initiate the chart
$('#container').highcharts('Map', {
title : {
text : 'Zoom in on country by double click'
},
mapNavigation: {
enabled: true,
enableDoubleClickZoomTo: true
},
colorAxis: {
min: 1,
max: 1000,
type: 'logarithmic'
},
xAxis: {
minRange: 5000 // <- prevent zooming in too much
},
series : [{
data : data,
mapData: Highcharts.maps['custom/world'],
joinBy: ['iso-a2', 'code'],
name: 'Population density',
states: {
hover: {
color: '#BADA55'
}
},
tooltip: {
valueSuffix: '/kmĀ²'
}
}]
});
});
});
You can see a working demo here: http://jsfiddle.net/b9d0ry3t/
This is a standard Highmaps demo, where I simply added the minRange on the xAxis, nothing else.
This and many other problems have been fixed with the most recent Highmaps update (Feb. 2015).

Resources