Show or hide featues by zoom level and importance - arcgis

Using arcgis for JS 4.24 MapView.
I have a feature layer with features of different importance to the user. The importance is defined in an attribute of the feature. What I want to achieve is, that less important features only show on a certain zoom level.
What I tried so far:
Clustering: is not what I want since it is grouping on occurrences not importance.
Unique Value Renderer: Does not seem to support visibility by zoom level but is otherwise exactly what I would use.
Setting the min-max visibility: This applies to all features and cannot be configured by attribute value.
What did I miss? Any other ways to apply custom styles to feature layers?

I guess a simple solution would be to create as many FeatureLayer instances as importances you have. In each of this instances you set,
definitionExpression to the importance level,
maxScale and minScale to constraint the visibility
This solution would work if the number of importance levels are "reasonable" (let say, less than 10 or so, you will have to test it).
Another solution would be to listen for scale changes in the map view (could be the updating event), and then apply a filter (could be definitionExpression again) with the correspondent importance. With this you do not need to have many instances of the same service.
EDIT
The reason for using updating, is to avoid doing stuff while the view is changing, something like this,
view.watch("updating", function (value) {
if (!value) {
// now is the moment to check view properties
// for example
if (view.scale < MIN_SCALE) {
doSomething();
};
}
});

Related

cytoscape.js layout that allows child positioning within compound (e.g. like dot's rank)

I am trying to use cytoscape to replace my dot output and make it interactive (move nodes and compounds, expand/collapse compounds, etc.)
When a graph is first loaded, the user should be presented with a default layout though. However, I am struggling to find a layout/config that supports what dot calls rank.
In my graph I have compound nodes that represent components.
Components contain other components and/or states, transitions, variables.
Each component can specify inputs and outputs.
In dot I tried to add some form of flow within the system (rankdir=LR;) by positioning the inputs on the left (rank=source;) and the outputs on the right (rank=sink;).
Other elements have no rank and are hence freely positioned.
I then specified cluster subgraphs containing all recursive components.
Now, here is what I have in dot. I hope it explains what I would like to end up with.
First, I already saw this question, but as far as I understood it's for manual positioning, rather than layouts.
I haven't found a layout that fully supports positioning nodes within the compound.
I looked into using the cytoscape.js-cola layout with the following options:
layout: {
name: 'cola',
flow: { axis: 'y', minSeparation: 40 },
avoidOverlap: true
}
I ended up with this
As you can see, there is some flow, but not as nicely as in dot.
I tried adding some function for the alignment parameter, but as far as I understood I can only specify the absolute coordinates (e.g. return {'x': 0};). This basically allows me to align ALL inputs, rather than all inputs of a compound.
Here is a CodePen of my example to play around with: https://codepen.io/anon/pen/GEaOQQ
In the Javascript you can see the comments of
You could try Klay: https://github.com/cytoscape/cytoscape.js-klay
If any of the existing layouts don't meet your requirements, you can use any algorithm you'd like by writing a layout extension. You could port the exact layout you're using with dot/graphviz, if you like.

Improving performance of mouseover event in EaselJS

From the manual:
The latter four events have some overhead associated with them, so you
need to enable them with stage.enableMouseOver(frequency). The
frequency parameter indicates how many times per second EaselJS should
calculate what is currently under the pointer. A higher number is more
responsive, but also more computationally expensive.
I need to enable a mouseover functionality only on a certain class of objects on my otherwise crowded stage. Is there a way to enable the mouseover checking only for certain objects as opposed for the whole stage? Or is EaselJS only checking objects with a "mouseover / mouseout, and rollover / rollout" listener? What about the pointer property - it works only if enableMouseOver is enabled - is that checked for all objects or only for those with pointer property other than default?
Is EaselJS internally doing some space partitioning like k d trees to boost the performance?
You can prevent any object from receiving a mouse event by setting mouseEnabled=false.
myBitmap.mouseEnabled = false;
If you have a large amount of items (such as particles), make sure they are in a container, and set mouseEnabled AND mouseChildren=false on the container, and the Stage will not check any of the container's children.
myContainer.mouseEnabled = myContainer.mouseChildren = false;
If you still want to know when a container's general area is clicked, you can swap out the default mouse behaviour with a hitArea, which is used in place of its actual contents.
var hitArea = new createjs.Shape();
hitArea.graphics.drawRect(0, 0, 500, 500);
myContainer.hitArea = hitArea;
Hope that helps!
For improving the performance i used cache if any object is static
I used mouseEnabled =false;
I did not render any text or images inside the canvas i used mix of
angular or plain javascript to render the dom element

layout doesn't work when adding nodes to graph dynamically

When I add nodes to the graph statically via the elements property of
$('#cy').cytoscape(..)
the layout option works but when I add them via
cy.add({..})
the layout is ignored. I can apply new layout only on these events(click, mouseover, mouseout) like this:
cy.on('mouseover', function(event) {
cy.layout({name: "grid"});
});
and the layout changes. Tried with other events: ready, done and load but it doesn't work.
Is there a normal way way to change the layout when elements are added dynamically?
You can't call a second layout while the initialisation layout is running. Configure your initialisation properly (http://cytoscape.github.io/cytoscape.js/#core/initialisation) to have all the data and options you require.
As for cy.add(): Don't try using cy.add() on load unless you specify everything you need (incl. position) for those elements. Or, you'll have to at least wait for layoutstop before running a new layout. In general, you're better off using the initialisation options to do things for you rather than having to worry about edge cases and event synchronising yourself.

How would you abstract this functionality?

Observe the following classes and their containment:
Section has an ItemList which has many Items
But lets say an Item is rendered a different color based on some complicated algorithm that is based off the Section.name it is contained in.
What's the proper/best way to go about abstracting this functionality? Is there a common design pattern that occurs in situations like these? Or is the inherent design flawed?
You should separate your data structures/models from your logic and processing of them. That said, I would make your Item have a Section reference on it referencing it's Section, when you add an Item to the ItemList, ensure the add method looks at the ItemLists Section (parent) and sets the reference on the Item. Same goes for the setter on the ItemList in the Section, it would have to iterate each Item and set the Section.
Alternatively, you could make the Section set on the getter of the ItemList as lazy semantics, that would be entirely up to you depending on the use of your Section the performance statistics would be different between these two approaches.
Further, I would write some form of renderer that took an Item and knew how to render it which would look at the Section on the Item and the Name on that Section.
You may want to render an entire section, but I would write that renderer separate and it would use the ItemRenderer to render each Item.
As an aside, you may want to use a form of IObservableCollection and have the Item implement INotifyPropertyChanged as well so that you could then maintain synchrony both between the rendered version and the item, and synchronize the Item with the Section it exists in by an event registration that updates the Section property appropriately.
What does the rendering? If it's something in Section, or outside of all of these, but accessing the Items from the Section, then there's nothing more needed.
If it's in Item, then you just need to make sure Item knows what Section it belongs to, or can obtain it (e.g. by having an ID it can look up on - useful with languages that make dealing with circular references tricky, but not needless hassle otherwise).
Since the first case causes no difficulty, it's clearly to be preferred over the second, so the closest to a design-pattern is that if you find yourself doing this sort of work in the contained item, to try and move it up to the container, or outside of them all. Beyond that, there isn't much of a problem to need solving.
I would build an ItemRenderer class that knows the algorithm, and pass in the references to each Item you would like to render and the Section it was contained in.
It may also make sense to let each Item know the section it belongs to, but I would still let the ItemRenderer handle the rendering.

Dojo 1.4.2 Tree Grid "expando click" event? persist state?

Question:
Given a DOJO TreeGrid, how can I capture the event when a user clicks the expando ("+") button to expand a row, and store the specific row number or associated item's identifier? I'd like to do this for the express purpose of completely deleting the TreeGrid from the DOM, rebuilding it, and restoring it's state once rebuilt (i.e. programmatically expanding the rows that the user has previously expanded).
Background:
So I've got a custom DOJO TreeGrid, hooked up to a custom QueryReadStore, in my app. It was constructed using the following tutorial:
http://www.ibm.com/developerworks/web/library/wa-dojotreegrid/index.html?ca=drs-
Pretty interesting tutorial, but it might be irrelevant to my question because it doesn't really squash any functionality, it only seems to augment it.
Anyway, googling around for a moment, I found a nice function in the DOJO forums that I can use to programmatically expand a row, given the specific row index. Works perfectly.
The problem is that I haven't been able to find a good way to capture the expando click event, and relate it to a specific "parent item row" index in the grid.
Details aside, I'd like to get the row index of every row that the user has expanded, into an array (and delete the index of a row that the user collapses, obviously), so I can destroy this TreeGrid, and faithfully rebuild it, with the user's selections expanded properly.
I'm not really a novice to DOJO, but I'm certainly no expert by any means. I've done a fair bit of googling, and FireBugging, and haven't really been able to find anything that I can use to do this.
Suggestions? Anybody done something similar before? Stupid question with obvious answer that I've missed? I'm totally misguided and am going about it all wrong? Thanks everybody!
Something similar to this would probably work, this is how the dijit.Tree implementation wouldve looked;
var expandedNodes = {}
dijit.tree._onExpandoClick = function (args /* object wrap for args.node */) {
var treeNode = args.node,
path = treeNode.getTreePath(),
id = treeNode.getIdentity();
expandedNodes[id] = path;
}
I am not 100% sure im being strictly correct but for the TreeGrid you will have to look through code of dojox/grid/_TreeView.js (link). The _setOpen would be an entrypoint from which you can 'hook' the onclick action. from there, find the expandoCell.openStates hash, this is a true/false variable set, indexed by itemId. This hash is most likely what you need as your state