I am trying to put thousands of points on a Cesium map and running into problems with Firefox crashing. I am required to use Firefox. The map seems to be able to display 15,000 points (as images). However, it is also almost unusable. Zooming and panning have huge delays and eventually crash. Does anyone know how many points the limit should be? Also, is there a better way to display these points then the way I am doing it? I really hope it's me and not Cesium. I heard that creating czml and then passing it in is slower so I have the following javascript test:
function test(){
for (var i=0; i<15000; i++){
tempLat +=1;
tempLon +=1;
if(tempLat>90){
tempLat=0;
tempLon=0;
}
addBillboard(scene, ellipsoid, tempLat,tempLon);
}
}
//this is from the sandcastle examples for cesium.
function addBillboard(scene, ellipsoid,tempLat,tempLon) {
var primitives = scene.primitives;
var image = new Image();
image.onload = function() {
var billboards = new Cesium.BillboardCollection();
var textureAtlas = scene.context.createTextureAtlas({image : image});
billboards.textureAtlas = textureAtlas;
billboard = billboards.add({
position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(tempLat, tempLon)),
imageIndex : 0
});
primitives.add(billboards);
};
image.src = '../images/Cesium_Logo_overlay.png';
}
Your code is creating 15,000 BillboardCollection primitives, each with one Billboard each. You will have much better performance if you create one BillboardCollection and add 15,000 Billboards to that collection.
Here's a working example you can use with the b28 release. Paste this into the b28 Sandcastle: http://cesiumjs.org/releases/b28/Apps/Sandcastle/index.html?src=Billboards.html&label=Showcases
Note that some of these APIs will be changing in the upcoming release. Always check CHANGES.md for a list of breaking changes.
require(['Cesium'], function(Cesium) {
"use strict";
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var primitives = scene.primitives;
var ellipsoid = viewer.scene.globe.ellipsoid;
var billboards = new Cesium.BillboardCollection();
var image = new Image();
image.onload = function() {
var textureAtlas = scene.createTextureAtlas({image : image});
billboards.textureAtlas = textureAtlas;
primitives.add(billboards);
};
image.src = '../images/Cesium_Logo_overlay.png';
function addBillboard(tempLat, tempLon) {
billboards.add({
position : ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(tempLat, tempLon)),
imageIndex : 0
});
}
var tempLat = 0;
var tempLon = 0;
for (var i = 0; i < 15000; i++){
tempLat += 1;
tempLon += 1;
if (tempLat > 90){
tempLat = 0;
tempLon = 0;
}
addBillboard(tempLat, tempLon);
}
Sandcastle.finishedLoading();
});
Related
I have a UK county shape file (Multipolygon) using EPSG:4326 in my geoserver. I'm using open layers 3 to load this shape file in my application as below :
source = new ol.source.XYZ({url: '/gmaps?zoom={z}&x={x}&y={y}&Layers=UKCounties', crossOrigin: "anonymous"});
countiesLayer = new ol.layer.Tile({source: source});
map.addLayer(countiesLayer);
This works well. I have a requirement to get users current location which is done as
var coordinate = geolocation.getPosition();
I'm able to retrieve the correct lat & long here. Ex : Lat = 53.797534899999995, Lng = -1.5449. now I need to check which of the counties (polygon) these points are in using open layers 3 & Javascript.
Using Geoserver WFS, I'm able to get the bounding box of each of the counties as
$.each(features, function(index, eachFeature) {
var bbox = eachFeature.properties.bbox;
if (bbox != null) {
var bottomLeft = ([bbox[0], bbox[1]]);
var topRight = ([bbox[2], bbox[3]]);
var extent = new ol.extent.boundingExtent([bottomLeft, topRight]);
if (ol.extent.containsXY(extent1,lat,long)) {
alert("got the feature");
}
}
});
The issue is my code doesn't print the alert statement.I've also tried using
if (ol.extent.containsXY(extent,long,lat))
and
var XY = ol.proj.transform([long, lat], 'EPSG:4326', 'EPSG:3857');
if (ol.extent.containsXY(extent,XY[0],XY[1]))
if (ol.extent.containsXY(extent,XY[1],XY[0]))
But none of these print the alert. Is there anything wrong in this?
Before answering your question, I did not know the method of "ol.extent.containsXY".
I used my poor logic! I detected a feature if in a polygon by follwing :
transform feature and container(polygon) to coordinate [lon, lat]
detect the container if contain the feature
extent array rule [minLon, minLat, maxLon, maxLat]
code snippet: (my destinationPro:'EPSG:3857', sourcePro:'EPSG:4326')
QyGIS.prototype.isInner = function(featureExtent, containerExtent) {
var featureLonLat = ol.proj.transformExtent(featureExtent, destinationPro, sourcePro);
var containerLonLat = ol.proj.transformExtent(containerExtent, destinationPro, sourcePro);
// in my condition, the feature is a point, so featureLonLat[0] = featureLonLat[2], featureLonLat[1] = featureLonLat[3]. what's more extent have four value in a array so the loop length is 4
for (var i = 0; i < featureLonLat.length; i++) {
/* actually:
featureLonLat[0] < containerLonLat[0] || featureLonLat[0] > containerLonLat[2]
featureLonLat[1] < containerLonLat[1] || featureLonLat[1] > containerLonLat[3]
featureLonLat[2] < containerLonLat[0] || featureLonLat[2] > containerLonLat[2]
featureLonLat[3] < containerLonLat[1] || featureLonLat[3] > containerLonLat[3]
*/
if (featureLonLat[i] < containerLonLat[i % 2] || featureLonLat[i] > containerLonLat[i % 2 + 2]) {
return false;
}
}
return true;
};
QyGIS.prototype.getInnerFeatures = function(layerName, extent) {
var self = this;
var layer = self.getLayer(layerName);
if (layer) {
var source = layer.getSource();
var features = source.getFeatures();
for (var i = 0; i < features.length; i++) {
var curFeatureExtent = features[i].getGeometry().getExtent();
if (self.isInner(curFeatureExtent, extent)) {
console.log(features[i].get('name') + 'in area');
}
}
}
};
At last, sorry for my poor english if my answer confuse you .
I had to use
var XY = ol.extent.applyTransform(extent, ol.proj.getTransform("EPSG:3857", "EPSG:4326"));
instead of
var XY = ol.proj.transform([long, lat], 'EPSG:4326', 'EPSG:3857');
and it works.
2 Questions:
What will be the most officiant way to loop through all of the Images on a given page and open each one in a new tab.
Same idea but instead open in a new tab I would like to push different images instead of the given ones. The idea is to build a widget that will inject cat photos instead of the normal photos of websites.
I answered 2 in the code, and 1 in the comment of the first code block
var allImages = document.getElementsByTagName('img');
for(var i = 0; i < allImages.length ; i++) {
// to open all photos in new tabs:
// window.open(allImages[i].src, '_blank');
allImages[i].src = 'url_to_cat_image';
}
you can also do the same with jquery:
// change all photos to the same one:
var allImages = $('img');
allImages.attr('src', 'url_to_cat_image');
If you want each photo to be a different image, just replace the url_to_cat_image with a function that returns a random cat image in the first code block. For jquery, you can use .each and a random cat url function.
Here's a way of doing it with vanilla js. As mentioned in the comments, using the document.images node-list is the best method for getting the images, since it's a static list and need not be assembled in response to dom queries.
As mentioned, browsers will block the attempts to open new windowss, labelling them as pop-ups.
Here's a demo:
function forEachNode(nodeList, func) {
for (var i = 0, n = nodeList.length; i < n; i++) func(nodeList[i], i, nodeList);
}
function onBtnClicked() {
var diskUrl =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC" +
"3RAAABdElEQVQoU22SLX/CMBCH/3E4IueorKNysh+hH6ESR93mqBuOuuEWiYyczB" +
"zycJPBIa+uU9nvmlD2wonmLulz78q+c4DIMH7Hk0UfOJ5/75KtrOWg9RxEwHqdfr" +
"xz9F9AXX9Ag8fXG3jssX6a3yUFwtCjqgnsDbK8gjIHDtnDHM6dsdks/oFXSNKuVg" +
"5VXoA+HZQxHLJsDvt+xu7lN/gTYgbqxqHMM3hPUGbvQ5YvYO0Ju91yivgX4oETWI" +
"AvBNV1PhTFAuZwwttrBO9B0u2qkVSzBG4jKL2yhxNYtDSSmx5HM0LyxgTVthEUkT" +
"qY+2mO0cHVUZrrOF8P1T5TKIpl8tTDHdvfnZ0BeqbBXN6WYiCoRsB8OUUiamEPHY" +
"qyhNlbYAZ0+w6eirRFUpSHapoIeu5Hj/TZwV8I5WOFZlUn0MA5DS3lANCSarOikE" +
"nRzDBHAi4dum1MV1IcI25beK6mvdUgKLHqlQsCSsRJpAlXIzUomvH+G/9qC1CEZi" +
"AJAAAAAElFTkSuQmCC";
forEachNode(document.images, function(elem) {
elem.oldSrc = elem.src;
console.log(elem.src);
window.open(elem.src);
elem.src = diskUrl
});
}
function onResetBtn() {
forEachNode(document.images, function(elem) {
if (elem.oldSrc != undefined) elem.src = elem.oldSrc;
});
}
<button onclick='onBtnClicked()'>Open pop-ups, change image sources</button>
<button onclick='onResetBtn();'>Reset</button>
<br>
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAHCAYAAAAvZezQAAAAKElEQVQIW2MUder+/3pfKSMDFDCCBEBsmCBcACaIIgASxK0CxQxkWwA6axnpT9J3PwAAAABJRU5ErkJggg==' />
<img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAHCAYAAAAvZezQAAAAJElEQVQIW2NkQAKiTt3/GWF8EAfEBgvAOGABZA52FVjNQBYEAPsjDS+SFXnjAAAAAElFTkSuQmCC' />
This is how you loop through all the images in a page.
I've also added example of opening in a new tab or replacing the source.
using native JavaScript no dependencies required.
// Array of cat photos
var arr = ['cat1.jpg', 'cat2.jpg', 'cat3.jpg'....]
var elem = document.getElementsByTagName('img');
for (var i = 0; i < elem.length; i++) {
var src = elem[i].getAttribute('src');
// Open in a new tab
if (src) window.open(src);
// Replace with a random cat photo
elem[i].src = arr[Math.floor(Math.random() * arr.length)];
}
Using vanilla javascript :
var images = document.querySelectorAll("img");
for(var i = 0;i < images.length;i++){
var image = images[i];
window.open(image.src,"_blank");
}
// changing for a kitty image
var images = document.querySelectorAll("img");
for(var i = 0;i < images.length;i++){
var image = images[i];
image.src = "http://placekitten.com/600/338";
}
However, if there are more than 1 image the browser will prevent that (see the popup message and allow that behaviour).
Js beginner here.
I have a function like this:
generateSteps: function() {
var stepsLength = this.data.steps.length;
var dataStepsInit = this.data.steps;
for (var i = 0; i < stepsLength; i++) {
var stepsItem = dataStepsInit[i].ITEM;
var arrayItem = this.animationNodes[stepsItem - 1];
var transition = this.animationParameters[i].transition;
var options = this.animationParameters[i].options;
var speed = this.animationParameters[i].speed;
var delay = this.animationParameters[i].delay;
arrayItem.delay(delay).show(transition, options, speed);
if (dataStepsInit[i].AUDIOID) {
var audioClass = dataStepsInit[i].AUDIOID;
var audioPlayer = this.template.find("audio." + audioClass);
setTimeout(playAudioOnDelay,delay);
};
var playAudioOnDelay = function() {
audioPlayer[0].pause();
audioPlayer[0].currentTime = 0;
audioPlayer[0].play();
};
}
}
What it does is generate data from JSON and display animated elements one by one on delay. Animation part work fine. I can assign required animations and delay to DOM elements and show them in right order.
But what I want to do in the same time is also to play an audio on delay (so I use setTimeout). Everything is almost fine, I play audio in right time (correct delay value) but I always play the same audio (which is last element) because audioPlayer always is the same DOM node.
I think this have something to do with this or I mixed a scope?
Try this:
generateSteps: function() {
var stepsLength = this.data.steps.length;
var dataStepsInit = this.data.steps;
for (var i = 0; i < stepsLength; i++) {
var stepsItem = dataStepsInit[i].ITEM;
var arrayItem = this.animationNodes[stepsItem - 1];
var transition = this.animationParameters[i].transition;
var options = this.animationParameters[i].options;
var speed = this.animationParameters[i].speed;
var delay = this.animationParameters[i].delay;
arrayItem.delay(delay).show(transition, options, speed);
if (dataStepsInit[i].AUDIOID) {
var audioClass = dataStepsInit[i].AUDIOID;
var audioPlayer = this.template.find("audio." + audioClass);
setTimeout(playAudioOnDelay(audioPlayer),delay);
};
}
function playAudioOnDelay(audioPlayer){
return function(){
audioPlayer[0].pause();
audioPlayer[0].currentTime = 0;
audioPlayer[0].play();
}
}
}
Essentially, your problem looks like this: http://jsfiddle.net/po0rLnwo/
The solution is : http://jsfiddle.net/gpfuo1s8/
Check the console in your browser.
I want to draw image array with drawImage after all the images are loaded.There is a render problem with drawImage(), tried to solve with setTimeout() but its not working all the time.
Here is my code;
while(FocusItem.length>0)
{
FocusItem.pop();
}
ftx=canvas.getContext('2d');
focusImageBackground = new Image();
focusImageBackground.src = "./images/odaklanma/odaklanmaBackground.jpg";
if(RandomSoru==15)
finishSoru=true;
if(finishSoru)
{
RandomSoru = Math.floor((Math.random() * 15)+1);
tempRandomSoru=RandomSoru;
}
if(RandomSoru==tempRandomSoru)
{
RandomSoru = Math.floor((Math.random() * 15)+1);
}
var soru = new Object();
soru["image"] = new Image();
soru.image.src = './images/odaklanma/level/'+RandomSoru+'/soru.png';
soru["x"] = 341;
soru["y"] = 140;
FocusItem.push(soru);
var dogru = new Object();
dogru["image"] = new Image();
dogru.image.src = './images/odaklanma/level/'+RandomSoru+'/dogru.png';
dogru["x"] = xDogru;
dogru["y"] = 280;
FocusItem.push(dogru);
var yanlis = new Object();
yanlis["image"] = new Image();
yanlis.image.src = './images/odaklanma/level/'+RandomSoru+'/yanlis.png';
yanlis["x"] = xYanlis1;
yanlis["y"] = 280;
FocusItem.push(yanlis);
var yanlis2 = new Object();
yanlis2["image"] = new Image();
yanlis2.image.src = './images/odaklanma/level/'+RandomSoru+'/yanlis1.png';
yanlis2["x"] = xYanlis2;
yanlis2["y"] = 280;
FocusItem.push(yanlis2);
}
if(focusImageBackground.complete){
if(FocusItem[0].image.complete && FocusItem[1].image.complete && FocusItem[2].image.complete && FocusItem[3].image.complete)
drawFocus();
else
setTimeout(drawFocus,600);
}
else
focusImageBackground.onload=function(){
if(FocusItem[0].image.complete && FocusItem[1].image.complete && FocusItem[2].image.complete && FocusItem[3].image.complete)
drawFocus();
else
setTimeout(drawFocus,600);
}
function drawFocus(){
ftx.drawImage(focusImageBackground,0,0);
for (var i=0; i<FocusItem.length; i++){
FocusItem[i].image.onload=function(){
ftx.drawImage (FocusItem[i].image, FocusItem[i].x, FocusItem[i].y);
}
}
}
I'd suggest loading all your images, then when they are all done, you can call the rest of your code. I don't quite follow what you're trying to do with all the rest of your code, but here's a simple way to load an array of image URLs and know when they are done.
This is the general idea (I've left out lots of your code that has nothing to do with the central issue of knowing when all the images are loaded) and I've also tried to DRY up your code:
function createImagesNotify(srcs, fn) {
var imgs = [], img;
var remaining = srcs.length;
for (var i = 0; i < srcs.length; i++) {
img = new Image();
imgs.push(img);
img.onload = function() {
--remaining;
if (remaining == 0) {
fn(srcs);
}
};
// must set .src after setting onload handler
img.src = srcs[i];
}
return(imgs);
}
// here's your starting array of filenames
var fnames = ["soru.png", "dogru.png", "yanlis.png", "yanlis1.png"];
// insert your process to create RandomSoru here
var randomSoru = ....;
// build full urls array
var urls = [];
for (var i = 0; i < fnames; i++) {
urls.push('./images/odaklanma/level/' + RandomSoru + '/' + fnames[i]);
}
// load all images and call callback function when they are all done loading
var imgs = createImagesNotify(urls, function() {
// all images have been loaded here
// do whatever you want with all the loaded images now (like draw them)
});
This code is based on an earlier answer of mine here: Cross-browser solution for a callback when loading multiple images?
I have a question about getting a specific animation added to some xml data I'm pulling. I need some advice on how to make the data move from left to right or vise versa.Just for example I downloaded a rss feed from BBC world news, so it's just an xml file. Both the flash and xml are saved in the same folder and I can get in flash and display the data. Here's are the code so far:
import flash.text.TextField;
import flash.sampler.StackFrame;
import flash.display.MovieClip;
var yPlacement:int = 20;
var xPlacement:int = 30;
var distance:int = 60;
var loader:URLLoader = new URLLoader();
loader.load(new URLRequest("bbc-worldnews-rss.xml"));
loader.addEventListener(Event.COMPLETE, handleComplete);
function handleComplete(event:Event):void
{
var rawXML:XML = new XML(loader.data);
rawXML.ignoreWhite = true;
//trace(rawXML.channel.image.url);
var items:XMLList = rawXML.channel.item;
//trace("Total new items", items.length());
for each (var item:XML in items)
{
//trace(item.title);
var feedTitle:String = item.title.toString();
var myText:TextField = new TextField();
myText.text = feedTitle;
myText.autoSize = TextFieldAutoSize.LEFT;
myText.x = 2;
myText.y = 2;
var clip_mc = new MovieClip();
clip_mc.addChild(myText);
addChild(clip_mc);
clip_mc.y = yPlacement;
clip_mc.x = xPlacement;
yPlacement = yPlacement + distance;
}
//trace("First item title:", item[0].title);
}
I also know the code that makes text move side to side but I don't know how to incorporate it my codes above:
onClipEvent ( load ) {
startPoint = 1280; //this is where the clip will start
endPoint = -1080; //this is where the clip will end, and restart to the startPoint.
speed = 5; //this is how many pixels the text will move each frame.
}
onClipEvent ( enterFrame ) {
this._x -= speed; //you are telling the MC to move to the left 5 pixels each frame.
if (this._x <= endPoint ) { //if your clip goes beyond the end point.
this._x = startPoint; //go back to the starting point.
}
}
I hope I'm not confusing anyone, I just need to get the data I get from xml file to move side to side... I may be complete off course but I would GREATLY appreciate anyone's help!
Thank you,
For starters, the second snippet of code you posted is actually ActionScript 2, not 3.
You'd need to update that snippet to AS3 in order for this to work. Try something like this:
var startPoint:int = 1280;
var endPoint:int = -1080;
var speed:int = 5;
function moveMC(mc:MovieClip):void {
mc.addEventListener(Event.ENTER_FRAME, tick);
}
function tick(e:Event):void {
e.currentTarget.x -= speed;
if (e.currentTarget.x <= endPoint) {
e.currentTarget.x = startPoint;
}
}
You could then call moveMC() after you've added your newly created MovieClip to the stage.
Edit: You can use that snippet right in your for each loop like this:
for each (var item:XML in items)
{
//trace(item.title);
var feedTitle:String = item.title.toString();
var myText:TextField = new TextField();
myText.text = feedTitle;
myText.autoSize = TextFieldAutoSize.LEFT;
myText.x = 2;
myText.y = 2;
var clip_mc = new MovieClip();
clip_mc.addChild(myText);
addChild(clip_mc);
clip_mc.y = yPlacement;
clip_mc.x = xPlacement;
yPlacement = yPlacement + distance;
//takes in reference to MovieClip, start point, end point and speed
moveMC(clip_mc, 1280, -1080, 5);
}
function moveMC(mc:MovieClip, startPoint:int, endPoint:int, speed:int):void {
mc.startPoint = startPoint;
mc.endPoint = endPoint;
mc.speed = speed;
mc.addEventListener(Event.ENTER_FRAME, tick);
}
function tick(e:Event):void {
var mc:MovieClip = e.currentTarget as MovieClip;
mc.x -= mc.speed;
if (mc.x <= mc.endPoint) {
mc.x = mc.startPoint;
}
}