Cesium get coordinates from Entity loaded from KML - javascript

I am trying to draw an arrowhead for each point in a kml file. For this I plan to fetch coordinates for each point by getById. So far I am getting an error:
Uncaught TypeError: Cannot read property 'position' of undefined (on line 14)
Here is my code:
var src = Cesium.KmlDataSource.load('../../My_KML/plots.kml', options);
viewer.dataSources.add(src).then(function(data) {viewer.flyTo(data);});
//-------------------********--------------**********-----------------//
var point = viewer.entities.getById('geom_20102');
var entities = viewer.entities;
var cartographicPosition = Cesium.Cartographic.fromCartesian(point.position.getValue(Cesium.JulianDate.now()));
var latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
var longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);
var line1 = entities.add({
polyline : {
positions : Cesium.Cartesian3.fromDegreesArrayHeights([longitude, latitude, 360, longitude + 1, latitude + 1, 400]),
width : 10,
followSurface : false,
material : new Cesium.PolylineArrowMaterialProperty(Cesium.Color.BLUE)
}
});
I have specified the element with id 'geom_20102' as a linestring wrapped around by a placemark in the kml. Also I would like to know which id to specify as both placemark and linestring have an id. Or am I confusing kml id with entity id?
I am new to Cesium.Js and I followed this example partially:
Cesium Workshop
KML snippet:
<Placemark id="feat_20125">
<name>874</name>
<styleUrl>#stylesel_20102</styleUrl>
<LineString id="geom_20102">
<coordinates>104.99108,10.4118,247.3 72.991075,26.25412,247.6</coordinates>
<altitudeMode>relativeToGround</altitudeMode>
</LineString>
</Placemark>

Two things are going on here.
First, the Cesium.KmlDataSource.load() function returns a JavaScript "Promise" which represents the eventual completion (or failure) of an asynchronous operation. At the time the viewer.entities is referenced in the code, the KML file has not yet loaded so the collection in viewer.entities is empty and calling getById() on it will return undefined. You should only access the viewer.entities or data.entities after the async Promise is completed and the "then" callback is invoked. Only at that time are the entities populated.
var src = Cesium.KmlDataSource.load('../../My_KML/plots.kml', options);
viewer.dataSources.add(src).then(function(data) {
var entities = data.entities;
console.log("f=" + entities.getById('feat_20125')); // f=[object Object]
console.log("g=" + entities.getById('geom_20102')); // undefined
viewer.flyTo(data);
});
Next, notice that the 'feat_20125' returns an object but the 'geom_20102' is not found. Only the "id" on the placemarks are populated when KML is converted into Cesium entities. Ids on any other KML element are discarded.

Related

how to assign a returned value from a function in a listener to variable and split it

In the code posted below I have the method MousePosition which has coordinateFormat as an attribute.
The createStringXY() returns a string encapsulates the longitude and latuitude, and they are comma separated. The latter function
keeps providing the long and lat values as the mouse moves.
What I want to achieve is to assign the values generated from createStringXY() to a variable and then split the string.
ngOnInit() {
var mousePositionControl = new MousePosition({
className: 'custom-mouse-position',
coordinateFormat: createStringXY(7),
projection: 'EPSG:4326',
// comment the following two lines to have the mouse position
// be placed within the map.
target: document.getElementById('mouse-position'),
undefinedHTML: '', //for what to be rendered when the mouse leaves map scope: values https://openlayers.org/en/latest/apidoc/module-ol_control_MousePosition-MousePosition.html
});
}
If I understand your question correctly, you can create a new variable. Use split() to split the string.
var result = createStringXY(7)
var [long, lat] = result.split(",")
// Or doing it in one line
// var [long, lat] = createStringXY(7).split(",")
Hope it answers your question.

How to pass in coordinates to LatLng function in Leaflet

I'm trying to figure out how to pass in coordinates to the L.LatLng function in leaflet so that it can map the coordinates.
I can successfully load in the data, and it looks like this:
//This is the structure of the geojson data, as an example
var SanFranciscoData = {"type":"FeatureCollection", "features": [
{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.97593699999999,32.889954000000046]}
//Load in the geojson
d3.json("data/dataPoints.json", function(SFData) {
var SFData = SanFranciscoData.features
})
//pass in coordinates to the L.LatLng leaflet function
SFData.forEach(function(d) {
d.latLong = new L.LatLng(d.features.geometry.coordinates[1],
d.features.geometry.coordinates[0]);
})
I've also tried, as I've seen done in examples:
var coords = SFData.feature.geometry.coordinates;
Both methods above give the same result: the coordinates are undefined. What am I doing wrong? I'm not sure how to access the coordinates array using object notation in order to access the lat / longs.
Ok.. this worked:
SFData.forEach(function(d) {
var coords = d.geometry.coordinates
console.log(coords)
d.latLong = new L.LatLng(coords[1],
coords[0]);
})
Seems as if coordinates needed to be defined withing the .forEach function!

How to clamp GeoJSON data format to terrain in Cesium Sandcastle?

I have terrain view in Cesium Sandcastle and I have loaded roads data in GeoJSON format, they are lines. I want to clamp them on terrain, like this example (in drop-down menu choose "Sample line positions and draw with depth test disabled") -> http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Ground%20Clamping.html&label=Tutorials
In the example, the line you see is defined within code, but I have data (roads) on my PC which is loaded in app. When loaded, roads are flat (under the terrain) and somehow I have to clamp them on terrain but don't know how.
I have tried using the existing code from the example but haven't succeed.
This is my code for now:
//Add terrain
var viewer = new Cesium.Viewer('cesiumContainer');
var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({
url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles',
requestWaterMask : true,
requestVertexNormals : true
});
viewer.terrainProvider = cesiumTerrainProviderMeshes;
viewer.scene.globe.depthTestAgainstTerrain = true;
//Load data (roads)
var dataSource = Cesium.GeoJsonDataSource.load('../../SampleData/ceste_rab_okvir.geojson');
viewer.dataSources.add(dataSource);
viewer.zoomTo(dataSource);
I know there is Cesium.GeoJsonDataSource.clampToGround, but as I'm not a developer, I don't understand how to write it in my code.
Does anyone knows how to do it? Or maybe there is another way to clamp roads to terrain?
Thanks in advance.
I've figured it out. It should be written like this:
//Add terrain
var viewer = new Cesium.Viewer('cesiumContainer');
var cesiumTerrainProviderMeshes = new Cesium.CesiumTerrainProvider({
url : 'https://assets.agi.com/stk-terrain/v1/tilesets/world/tiles',
requestWaterMask : true,
requestVertexNormals : true
});
viewer.terrainProvider = cesiumTerrainProviderMeshes;
viewer.scene.globe.depthTestAgainstTerrain = true;
//Load data (roads)
Cesium.GeoJsonDataSource.clampToGround = true;
var dataSource = Cesium.GeoJsonDataSource.load('../../SampleData/ceste_rab_okvir.geojson');
viewer.dataSources.add(dataSource);
viewer.zoomTo(dataSource);

Openlayers 3 - WFS not updating on map pan

I was using a CQL Filter to return my points based on a certain type. I then added a BBOX to the filter to limit the results only to the current extent of the map. This was all working correctly. The only issue is if I pan the map it does not reload the WFS call. So if I zoom into a point the wfs filters correctly, if I then pan to an area where I know there is a point there will be nothing there unless I change the zoom level. This will update the WFS.
In my ServerVector loader parameter I call a function updateWFSURL(extent). I use this to build the WFS url based on the filter criteria to be passed to the CQL_FILTER. I currently have no loading strategy, I tried using createTile and bbox but neither gave me the proper result.
Here is the code I am using to add the BBOX to my CQL_FILTER. More filters are added after and this filter is appended to the WFS url.
var coord1 = ol.proj.transform([extent[0], extent[1]], 'EPSG:3857', 'EPSG:4326');
var coord2 = ol.proj.transform([extent[2], extent[3]], 'EPSG:3857', 'EPSG:4326');
var filter = "&CQL_FILTER=BBOX(GEOM, " + coord1 + ", " + coord2 + ", 'EPSG:4326')";
The workaround I found was to capture the map moveend event and call the updateWFSURL(extent) from there. This will update properly on a pan but causes the updateWFSURL to be called twice on zoom since it gets fired from the WFS loader and moveend event.
The solution I am looking for is to either have my WFS update properly on the pan or how to determine the difference between a pan/zoom so I can not call the function in moveend if it was a zoom.
Added more code below. Removed the geoserver url and layer name I am using and stripped out some of CQL Filter that was just filtering on an ID. This wfs is not updating the map on pan without my work around.
//this function gets called on page load
function loadPointAssets() {
// Source retrieving WFS data in GeoJSON format using JSONP technique
pointAssetVector = new ol.source.ServerVector({
format: new ol.format.GeoJSON(),
loader: function (extent, resolution, projection) {
//this gets called whenever the map zooms
updateWFSURL(extent);
},
projection: 'EPSG:3857'
});
//cluster markers adds the markers to the map from the pointAssetVector
clusterMarkers();
}
function updateWFSURL(extent) {
//transform the extent to coordinates can be passed in as lat/long
var coord1 = ol.proj.transform([extent[0], extent[1]], 'EPSG:3857', 'EPSG:4326');
var coord2 = ol.proj.transform([extent[2], extent[3]], 'EPSG:3857', 'EPSG:4326');
var filter = "&CQL_FILTER=BBOX(GEOM, " + coord1 + ", " + coord2 + ", 'EPSG:4326')";
$.ajax({
//create the url with the filter
url: 'http://myGeoserverURL/wfs?' +
'service=WFS&request=GetFeature&' +
'version=1.1.0&typename=LayerName&outputFormat=text/javascript&' +
'format_options=callback:loadFeatures&' +
'srsname=EPSG:4326' + filter,
dataType: 'jsonp'
});
pointAssetVector.clear(true);
}
// Executed when data is loaded by the $.ajax method.
var loadFeatures = function (response) {
//clear the features before adding them to the source again
pointAssetVector.clear();
pointAssetVector.addFeatures(pointAssetVector.readFeatures(response));
};
You cannot use CQL filter and BBOX together in the same WFS request. If you have a CQL property filter then you need to add the BBOX filter as a CQL filter.
For example, this BBOX filter:
bbox: extent.join(',') + ',EPSG:3857'
Is equivalent to this CQL filter:
cql_filter: "BBOX(geometry," + extent.join(',') + ")"

Integrate Google Map with Markers from HTTPrequest Javascript

Hi there I am Using appcelerator, and I want to integrate a map with an array of markers I am getting from a HTTPRequest...
I am effing lost, totally lost.
This is how the map looks like:
var mapview = Titanium.Map.createView({
mapType: Titanium.Map.STANDARD_TYPE,
region: {latitude:33.74511, longitude:-84.38993,
latitudeDelta:0.01, longitudeDelta:0.01},
animate:true,
regionFit:true,
userLocation:true,
annotations:[mountainView]
});
And I have the example of 1 marker hardcoded ...
var mountainView = Titanium.Map.createAnnotation({
latitude:37.390749,
longitude:-122.081651,
title:"Appcelerator Headquarters",
subtitle:'Mountain View, CA',
pincolor:Titanium.Map.ANNOTATION_RED,
animate:true,
leftButton: '../images/appcelerator_small.png',
myid:1 // CUSTOM ATTRIBUTE THAT IS PASSED INTO EVENT OBJECTS
});
So yo create the marker and in the annotations section you add it to the map, the thing here is that I am getting the markers from this:
var url = "http://myURLwithMyParameters";
var xhr = Ti.Network.createHTTPClient({
onload: function(e) {
// this function is called when data is returned from the server and available for use
// this.responseText holds the raw text return of the message (used for text/JSON)
var result = this.responseText;
var xml = Ti.XML.parseString(result);
var items = xml.documentElement.getElementsByTagName("marker");
var name = xml.documentElement.getElementsByTagName("name");
var value = xml.documentElement.getElementsByTagName("address");
var data = [];
for (var i=0;i<items.length;i++) {
data.push({
name: items.item[i].getElementsByTagName("name")[0].textContent,
address: items.item[i].getElementsByTagName("address")[0].textContent
})
Does any one know how to integrate this?
I think I must build the map in the same function as the markers, but I've tried several options and haven't found ANY example of this in the web.
Any clue would be very appreciated.
Thank you in advance.
If all you have is an address, you'll need to forward geocode those addresses to get lat/long coordinates. Those coords are required to place annotations on the map. Check the docs at forwardGeocoder(). There's an example in the KitchenSink

Categories