By default, Cesium globe focuses on the United States, as follows
But I want to show particular country as default (ex:India). I tried many ways to do this, like setting camera positions etc.
This question has already been asked here and here. You can adapt these answers for the country of India like so:
var west = 68.0;
var south = 7.0;
var east = 89.0;
var north = 35.0;
var rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);
Cesium.Camera.DEFAULT_VIEW_FACTOR = 0.5;
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = rectangle;
// NOTE: Viewer constructed after default view is set.
var viewer = new Cesium.Viewer('cesiumContainer', {
navigationHelpButton: false, animation: false, timeline: false
});
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif;
}
<link href="http://cesiumjs.org/releases/1.16/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.16/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>
Related
I'm an absolute beginner - that's why I need to ask my question here although I think it's absolutely easily to solve for some of you but: I've not found an answer anywhere online. So heres my "project":
I created a fantasy map using photoshop and now want to use this map to create multiple two-point lines in between different cities. Think of it like a airline destination map, where you can see the departure and arrival airport on a map, and both points connected with a line. Right now, I thought that I can do this by embedding my map .jpg on a html site and then I create a database (with excel) where I specify the name of a city and its coordinates on the image. Afterwards, I thought I can use these data to create those lines between two cities using maybe another database with different origins and destinations on the map. But I now notice, that I need to understand if this all is practicable or a completely wrong approach and how this could be done.
For the two-points on a map connection thingy, I read that there are many solutions for G'maps, but I haven't found a solution for a fantasy map project, yet and am unable to convert those G'map codes to a simple image file.
If you think this can be done and does not need too advanced skills, please let me know.
Anyways, thanks in advance for your help and time,
J.
As this is a 'beginner' project I would suggest using Javascript alongside HTML/CSS. If required, later on setting everything up through a database or other outside source.
The idea in this snippet is to have the map, measure up its width and height (a plastic ruler will do!) and the x, y coordinates of the various places and put the map in the site as an img with a canvas overlaid.
The positioning of the places is made relative to the actual dimensions of the img on the screen, and the places joined by using canvas stroke to draw the lines.
The whole is put inside a function which is called on each resize.
function init() {
const mapW = 300; // width of the map when you measured it (the units you used don't matter as long a you use the same ones for all the measurements)
const mapH = 192; // the height of the map when you measured it (the units you used don't matter)
const places = [{
"name": 'Never Winter Wood',
"x": 125,
"y": 40
}, {
"name": 'Star Mounts',
"x": 165,
"y": 50
}, {
"name": 'Cormanthor',
"x": 275,
"y": 105
}];
const routes = [{
"start": 'Never Winter Wood',
"end": 'Star Mounts'
}, {
"start": 'Cormanthor',
"end": 'Never Winter Wood'
}]
function getPlace(name) {
let result = '';
places.forEach(place => {
if (place.name == name) {
result = place;
}
});
return result;
}
const container = document.querySelector('.container');
const map = document.querySelector('.map');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const h = map.offsetHeight;
const w = h * mapW / mapH;
canvas.width = w;
canvas.height = h;
container.style.width = w + 'px';
container.style.height = h + 'px';
const scale = w / mapW;
function myMoveTo(place) {
ctx.moveTo(scale * place.x, scale * place.y);
}
function myLineTo(place) {
ctx.lineTo(scale * place.x, scale * place.y);
}
routes.forEach(route => {
const place1 = getPlace(route.start);
const place2 = getPlace(route.end);
ctx.lineWidth = 1;
ctx.beginPath();
myMoveTo(place1);
myLineTo(place2);
ctx.stroke();
});
}
window.onresize = init;
init();
.container {
height: 100vh;
position: relative;
}
.map {
height: 100vh;
width: auto;
position: relative;
display: inline-block;
}
canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
<div class="container">
<img class="map" src="https://i.stack.imgur.com/PGdKQ.jpg">
<canvas></canvas>
</div>
After having followed a mapbox tutorial, I managed to display a drone on a map.
My only question is :
How can I add a rotation parameter in my code (to display the drone-marker on different angles) ?
I have spent hours looking for examples but none corresponds to what I already wrote...
Thank you !
Here's the script :
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.css' rel='stylesheet' />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.marker {
background-image: url('MQ-1_Predator_silhouette.svg.png');
background-size: cover;
width: 61px;
height: 35px;
border-radius: 50%;
cursor: pointer;
}
</style>
</head>
<body>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibWF0dGhpZXU2MyIsImEiOiJjamNob3I3cmgxam1kMzFzNzdja2ZvNmhuIn0.AyFos9o0afaaBU21CgrxXg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v9',
center: [-96, 37.8],
zoom: 3
});
// code from the next step will go here!
var geojson = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [-90,40]
},
properties: {
title: 'Mapbox'
}
}]
};
// add markers to map
geojson.features.forEach(function(marker) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
// make a marker for each feature and add to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);
});
</script>
</body>
</html>
Mapbox offers inbuilt icon rotation feature under the heading of get bearing and icon rotation is handled internally once you find out geographical bearing between two pairs of LatLng and feed that value to get Bearing.
If you are using mapbox marker and are keen on rotating it, you can use css transform property (rotate()) and dynamically calculate the angle between two pairs of latLng and use that value in rotate property.
var dLon = destination[0]-origin[0];
var dLat = destination[1]-origin[1];
var angle = 180+(Math.atan2(dLon, dLat) * 180 / Math.PI);
var rotateString = "rotate(" + angle + "deg)";
var el = document.createElement('div');
el.className = 'marker';
var truckMarker = new mapboxgl.Marker(el)
el.style.transform = el.style.transform + rotateString;
In the last line it is important to append the rotate property because the transform property is already being updated as translate is called because of animation.
Works perfectly fine for me!!
If you're using Markers then you'll need to do the rotation yourself as part of the marker element style. This would probably only work if you disable map rotation, or unless you do something like https://github.com/mapbox/mapbox-gl-js/issues/3937#issuecomment-304916394 to account for the map rotation yourself.
If you're using Symbols then it's much easier as you can use https://www.mapbox.com/mapbox-gl-js/style-spec#layout-symbol-icon-rotate to set your rotation.
I need to create a setZoom() function in Cesium. For that, I believe I need to evaluate the current zoom so I can decide if I have to use the zoomIn or zoomOut to show what the user asks.
Does anyone know if it's possible to get the zoom level from the map when using Cesium? Or any other solution... Any tips are very welcome.
Does the function getMagnitude() does the trick?
Thanks!
Solution:
I put together all the tips emackey gave to me and got the following code:
var iniPos = new Cesium.Cartesian3();
iniPos = this.viewer.camera.position;
var cartographic = new Cesium.Cartographic();
cartographic.height = zoom * 1000;
cartographic.longitude = iniPos.x;
cartographic.latitude = iniPos.y;
var newPos = new Cesium.Cartesian3();
Cesium.Ellipsoid.WGS84.cartographicToCartesian(cartographic, newPos);
this.viewer.camera.setView({
position: newPos
});
With that I'm able to define the height of the camera to a zoom parameter defined by the user.
Cesium's default view is a 3D globe with a perspective view. A typical 2D zoom level number doesn't fully describe the different resolutions that Cesium's camera can see. Take a minute to read my full answer to Determining the Map Scale of the Viewed Globe for a better explanation.
EDIT 1: The camera.getMagnitude function gets the "magnitude of the camera's position" which really means the distance to the center of the Earth. This is probably not what you want, instead you want the altitude of the cartographic position.
EDIT 2: I've added a code snippet here that has buttons on it to set the camera's height to various altitudes. Click "Run code snippet" at the bottom to see this in action, or copy just the JavaScript portion of this into Sandcastle to run it there. Note that this works best when the camera is looking straight down, as it moves the camera to a specific height without altering the lat/lon. If the camera is off-axis, the mouse can "zoom" the camera along the look vector, which alters all three cartographic coordinates (lat, lon, and alt) at the same time. It's a trickier calculation to move the camera to a specific height along that line, I don't have code for that handy and it might not really be what you want anyway. Give this a try, see if it works for you.
var viewer = new Cesium.Viewer('cesiumContainer', {
navigationHelpButton: false,
animation: false,
timeline: false
});
var cartographic = new Cesium.Cartographic();
var cartesian = new Cesium.Cartesian3();
var camera = viewer.scene.camera;
var ellipsoid = viewer.scene.mapProjection.ellipsoid;
var toolbar = document.getElementById('toolbar');
toolbar.innerHTML = '<div id="hud"></div>' +
'<button type="button" class="cesium-button" id="h1km">1km height</button>' +
'<button type="button" class="cesium-button" id="h10km">10km height</button>' +
'<button type="button" class="cesium-button" id="h500km">500km height</button>';
toolbar.setAttribute('style', 'background: rgba(42,42,42,0.9); border-radius: 5px;');
var hud = document.getElementById('hud');
viewer.clock.onTick.addEventListener(function(clock) {
ellipsoid.cartesianToCartographic(camera.positionWC, cartographic);
hud.innerHTML =
'Lon: ' + Cesium.Math.toDegrees(cartographic.longitude).toFixed(3) + ' deg<br/>' +
'Lat: ' + Cesium.Math.toDegrees(cartographic.latitude).toFixed(3) + ' deg<br/>' +
'Alt: ' + (cartographic.height * 0.001).toFixed(1) + ' km';
});
function setHeightKm(heightInKilometers) {
ellipsoid.cartesianToCartographic(camera.position, cartographic);
cartographic.height = heightInKilometers * 1000; // convert to meters
ellipsoid.cartographicToCartesian(cartographic, cartesian);
camera.position = cartesian;
}
document.getElementById('h1km').addEventListener('click', function() {
setHeightKm(1);
}, false);
document.getElementById('h10km').addEventListener('click', function() {
setHeightKm(10);
}, false);
document.getElementById('h500km').addEventListener('click', function() {
setHeightKm(500);
}, false);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif; color: #edffff;
}
#toolbar {
padding: 2px 5px;
position: absolute;
top: 5px;
left: 5px;
}
<link href="http://cesiumjs.org/Cesium/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/Cesium/Build/Cesium/Cesium.js"></script>
<div id="cesiumContainer"></div>
<div id="toolbar"></div>
I'm having problems with resizing cells and built-in mxGraph layouts.
If I put a cell on canvas, and I try to resize it, even for a pixel, it grows huge, something like 50000px x 30000px, so it streches my whole canvas, and of course it is unusable.
If I load a graph from an xml file from the database, I can resize cells without any problems.
Similar thing happens with the built in layouts. I'd like to use compact tree layout (the reason I like it beacuse it aligns my whole horizontal).
When I draw a graph and try to use that layout, my graph goes wild, also streching to 50000px x 30000 px (example dimensions, but the scroll is so tiny I can barely aim it with the mouse).
If I load a graph from xml from a database, compact tree layout works perfect. But as soon as I add another cell in it, and try to use compact tree layout again, it goes wild, again.
I use absolute positioning for div which holds the canvas, as same as on the example here (http://jgraph.github.io/mxgraph/javascript/examples/editors/workfloweditor.html)
This is my css and html :
<head>
<style type="text/css">
#graphContainer {
background: url('../../resources/jgraph/src/images/grid.gif');
left: 20px;
right: 20px;
top: 65px;
bottom: 20px;
position: absolute;
border: 1px solid #F2F2F2;
white-space: nowrap;
font-family: Arial;
font-size: 8pt;
display: block;
}
</style>
</head>
<body>
<div id="graphContainer"></div>
<script>
$(document).ready(function(){
mc.init(document.getElementById('graphContainer'));
});
</script>
</body>
</html>
And this is my javascript for graph initialization (along with the couple of events, beacuse I'm not sure if they are the problem):
mxConnectionHandler.prototype.connectImage = new mxImage('../../resources/jgraph/src/images/connector.gif', 14, 14);
if (!mxClient.isBrowserSupported()) {
mxUtils.error('Browser is not supported!', 200, false);
} else {
var root = new mxCell();
root.insert(new mxCell());
var model = new mxGraphModel(root);
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(graphContainer);
}
var editor = new mxEditor();
editor.setGraphContainer(graphContainer);
editor.readGraphModel(model);
var graph = editor.graph;
graph.setConnectable(true);
new mxRubberband(graph);
/* CODE FOR ADDING THE TOOLBAR, excluded from example */
//code for writing out the node name
graph.convertValueToString = function(cell)
{
if (mxUtils.isNode(cell.value))
{
var outValue = cell.value.getAttribute('nodeName');
if (outValue != null && outValue.length > 0)
{
return outValue;
}
return '';
}
return '';
};
//defining on select event
graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt)
{
events.cellSelectionChanged(graph, graph.getSelectionCell());
});
//triggering the on select event
events.cellSelectionChanged(graph);
//cells added event
graph.addListener(mxEvent.CELLS_ADDED, function(sender, evt) {
var vertex = evt.getProperties().cells[0];
if(vertex.isVertex()){
var decoder = new mxCodec();
var nodeModel = decoder.decode(vertex.value);
if(nodeModel.type=='node' || nodeModel.type=='branch'){
utils.changeCellAttribute(vertex, 'nodeName', 'Node_' + vertex.id);
}else if(nodeModel.type=='start'){
utils.changeCellAttribute(vertex, 'nodeName', 'START');
}else if(nodeModel.type=='end'){
utils.changeCellAttribute(vertex, 'nodeName', 'END');
}else if(nodeModel.type=='form'){
utils.changeCellAttribute(vertex, 'nodeName', 'Form');
}
}
});
//on connect event
graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt){
var model = graph.getModel();
var edge = evt.getProperty('cell');
var source = model.getTerminal(edge, true);
var target = model.getTerminal(edge, false);
});
}
Any thoughts what the problem might be?
Solution:
Complete graph and cell configuration is loaded from the database (in this example), including the width and height for the cells.
The problem was adding toolbar items for certain cell types, more precise, dropped cell default width and height. As I said we are loading the configuration from the database, it is completely string-ified, so were the width and height.
They both had to be cast to JavaScript Number object for resize and layout to behave properly.
I tried Google place API, it's returning phone for a place "formatted_phone_number" or "international_phone_number".
But it is not returning for all places (just for 1 in 100 places). But, Currently I want to get phone number for each place (For example :- Hotel ).
I will send the Latitude and Longitude information to API it need to return nearest Hotel address and Phone number as well.
Please suggestion me better API for this.
Quality of the data (such as phone numbers) that you can receive from any Places API depends purely upon the quality of the data backing up that API. This will vary from vendor to vendor and coverage will differ from country to country, city to city and so on.
Your best bet here would be to run some sort of "beauty contest" to get a feel for which vendor holds the best data set that is relevant to you.
To get the phone number of a place using the Nokia RESTful Places API for example, is a two stage process:
make a general category query e.g "Hotels" near "Mumbai"
http://places.nlp.nokia.com/places/v1/discover/search?at=18.975%2C72.825833&q=hotel&tf=plain&pretty=y&size=10&app_id=_peU-uCkp-j8ovkzFGNU&app_code=gBoUkAMoxoqIWfxWA5DuMQ
Take the placeId, and query for more information such as contacts.phone
http://places.nlp.nokia.com/places/v1/places/356te7gc-e5c2e62e24254695b4e41c7762ded586;context=Zmxvdy1pZD03NTAwNzM3ZS1mZmEwLTU0ZDgtYjBkZC1lYjgwMDZmMGE2NDBfMTM1OTcwODg5MDQzNl8wXzU2ODAmcmFuaz0x?app_id=_peU-uCkp-j8ovkzFGNU&app_code=gBoUkAMoxoqIWfxWA5DuMQ
Again not all places will contain phone information. Now you could chain the response from the first request to loop through the placeIds received and place the phone numbers on screen.
An alternative would be to use the existing JavaScript API wrapper to display information as shown in the code below, you'll need your own app id and token to get it to work
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
Example from Nokia Maps API Playground, for more information visit http://api.maps.nokia.com
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Nokia Maps API Example: Search by category</title>
<meta name="description" content="Search by category"/>
<meta name="keywords" content="search, services, places, category"/>
<!-- For scaling content for mobile devices, setting the viewport to the width of the device-->
<meta name=viewport content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<!-- By default we add ?with=all to load every package available, it's better to change this parameter to your use case. Options ?with=maps|positioning|places|placesdata|directions|datarendering|all -->
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.3/jsl.js?with=all"></script>
<!-- JavaScript for example container (NoteContainer & Logger) -->
<style type="text/css">
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 40%;
height: 80%;
left: 0;
top: 0;
position: absolute;
}
#progress {
width: 80%;
height: 10%;
left: 0;
top: 80%;
position: absolute;
}
#buttons {
width: 80%;
height: 10%;
left: 0;
top: 90%;
position: absolute;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="progress"></div>
<div id="buttons">
<a onClick="searchByCategory( map.center, 'bookshop' );return false;" href="#">Find Hotels</a>
<div style="display:block">
<div id="csPlaceWidget" style="display:none"></div>
</div>
</div>
<script type="text/javascript" id="exampleJsSource">
/* Set authentication token and appid
* WARNING: this is a demo-only key
* please register on http://api.developer.nokia.com/
* and obtain your own developer's API key
*/
nokia.Settings.set("appId", "My App Id");
nokia.Settings.set("authenticationToken", "My Token");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
var infoBubbles = new nokia.maps.map.component.InfoBubbles();
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// Initial center and zoom level of the map
center: [18.975, 72.825833],
zoomLevel: 10,
components: [ infoBubbles,
new nokia.maps.map.component.Behavior()
]
});
var searchManager = nokia.places.search.manager,
resultSet;
var defaultHandler = function (evt) {
console.log(evt.target);
infoBubbles.openBubble('<h3>' +evt.target.title + '</h3>' + evt.target.vicinity + '<br/>'
+ '<a onclick="getPhoneNumber(\'' + evt.target.placeId +'\')" >Get Phone</a>' ,evt.target.position)
};
var myData;
var searchCat;
var maxDistance = 0;
var getPhoneNumber = function (placeId){
nokia.places.manager.getPlaceData({
placeId: placeId,
basicInfo: true,
onComplete: function (data, status) {
if ( data.contacts.phone === undefined ){
alert ("Unknown");
} else {
alert(data.contacts.phone[0].value);
}
}
});
};
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
myData = data;
if (requestStatus == "OK") {
// The function findPlaces() and reverseGeoCode() of return results in slightly different formats
locations = data.results ? data.results.items : [data.location];
// We check that at least one location has been found
if (locations.length > 0) {
// Remove results from previous search from the map
if (resultSet) map.objects.remove(resultSet);
// Convert all found locations into a set of markers
resultSet = new nokia.maps.map.Container();
for (i = 0, len = locations.length; i < len; i++) {
marker = new nokia.maps.map.StandardMarker(locations[i].position, { text: i+1 });
marker.title = locations[i].title;
marker.position = locations[i].position;
marker.vicinity = locations[i].vicinity;
marker.placeId = locations[i].placeId;
marker.addListener("click", defaultHandler);
resultSet.objects.add(marker);
if (locations[i].distance > maxDistance){
maxDistance = locations[i].distance;
}
}
// Next we add the marker(s) to the map's object collection so they will be rendered onto the map
map.objects.add(resultSet);
// We zoom the map to a view that encapsulates all the markers into map's viewport
map.zoomTo(resultSet.getBoundingBox(), false);
progressUiElt.innerHTML = locations.length + " places found in the '" + searchCat + "' category within " + maxDistance + "m of "+ data.search.location.address.city ;
} else {
alert("Your search produced no results!");
}
} else {
alert("The search request failed");
}
};
// Binding of DOM elements to several variables so we can install event handlers.
var progressUiElt = document.getElementById("progress");
searchByCategory = function(searchCenter , category){
// Make a place search request
searchCat = category;
progressUiElt.innerHTML = "Looking for places in the '" + category + "' category...'";
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
searchCenter: searchCenter,
limit: 100,
});
}
// Search for Hotels in Mumbai
searchByCategory( new nokia.maps.geo.Coordinate(18.975, 72.825833), "hotel" );
</script>
</body>
</html>
The data from the JavaScript API can be seen below:
Regarding phone numbers, please bear in mind the Restrictions from the Terms and Conditions
(ii) You will not copy, translate, modify, or create a derivative work
(including creating or contributing to a database) of, or publicly
display any Content or any part thereof except as explicitly permitted
under this Agreement. For example, the following are prohibited:
...
(iii) creating mailing lists or telemarketing lists based on the Content; or
(iv) exporting, writing, or saving the Content to a third party's
location-based platform or service ;