My problem: I'm working on a Android (ArcGis) Attribute Editor following the sample code from their tutorials. The problem occurs when I have more than 1 ArcGisDynamicLayer and, more important, more than 1 ArcGisFeatureLayer.
My question: What is the (correct) approach to IDENTIFY the right Feature Layer to query when I tap on a "field" displayed on the map?
I read a similar question (Arcgis SDK for Android - Query multiple feature layers), but I didn't understand how to use IdentifyTask using only a single tap.
EDIT: I tried to get the graphics that I tapped on to compare it to the graphics from every feature layer. If they are equal, that's the feature layer that I'm looking for. But that's not working, because my feature layers don't seem to have any graphics.
mapView.setOnSingleTapListener(new OnSingleTapListener() {
public void onSingleTap(float x, float y) {
/*...construct query...*/
Graphic g = null;
for (Layer layer : mapView.getLayers()) {
if (layer instanceof ArcGISFeatureLayer) {
ArcGISFeatureLayer fLayer = (ArcGISFeatureLayer) layer;
// Get the Graphic at location x,y
final int[] ids = fLayer.getGraphicIDs(x, y, 10, 1);
if (ids != null && ids.length != 0) {
g = fLayer.getGraphic(ids[0]);
System.out.println("Graphic: " + g);
}
}
/*...loop through every FeatureLayer and verify like :...*/
for(...)
if(layer.getGraphic(g.getUid()) != null && && g.equals(layer.getGraphic(g.getUid()))) {
/* call the select features method and implement the callbacklistener */
/* rest of the story */
}
}
Related
Edit: Show what the mapManager.filter() method does.
I have an Android app with ~20k markers being draw on the map and with filters option so the markers are being drawn and remove a lot.
I'm currently using this clustering library because it's way more efficient for displaying a large amount of markers than the classic google map library. But it's not supported and hasn't been updated since 3 years and i haven't found any alternatives.
Here is a video of the bug.
Here is my code that is call when i click on an filtering option in my app:
private void filter(){
//currentMapManager contain all the filters option and also the array list
//of the unfiltered markers (~20,000 markers)
//it's filter method retrieve the current filtering option enable (marker type, and other specs)
//And return markers from the full arraylist that match those filters options.
ArrayList<MarkerModel> filteredList = currentMapManager.filter(currentMapManager.getAllMarkers());
if (clusterManager != null) {
clusterManager.setItems(new ArrayList<>());
clusterManager.onCameraIdle();
clusterManager.setItems(filteredList);
clusterManager.onCameraIdle();
}
Here the mapManager.filter() method.
public ArrayList<MarkerModel> filter(List<MarkerModel> currentMarkers) {
ArrayList<Object> filtersArray = filters.getFilters();
/* filtersArray is an array like this :
* ['type', ['typeFilter1', 'typeFilter2'],
* 'date', dateTimestamp,
* 'withPictures', true,
* ....]
*/
return filter(currentMarkers, filtersArray);
}
private ArrayList<MarkerModel> filter(List<MarkerModel> currentMarkers,
ArrayList<Object> fieldsAndValues) {
if (fieldsAndValues.size() % 2 == 1) {
throw new IllegalArgumentException("Missing value in call to filterList().
There must be an even number of arguments that alternate between
field names and values");
} else {
//Here the arraylist that we are returning
ArrayList<MarkerModel> filteredMarkers = new ArrayList<>();
List<Object> argumentList = new ArrayList();
Collections.addAll(argumentList, fieldsAndValues);
if (!currentMarkers.isEmpty()) {
for (int j = 0; j < currentMarkers.size(); j++) {
MarkerModel marker = currentMarkers.get(j);
boolean isInFilter = true;
//Check fieldsAndValue filters...
//If isInFilter stay to true we
filteredMarkers.add(marker);
}
}
return filteredMarkers;
}
}
I think the problem is that the cluster library is not supposed to have so much redrawn and it broke at some point from reading and working on 20k items arraylist.
I have look through all the forks branch of this library and look/try to understand the library source code but hasn't find any solution at my problem...
If you need other part of my source code to understand more tell me i will add it.
Thanks a lot if you can help me i have been struggling a lot with this issue...
I need to draw graph like the image i have uploaded , i am using MP chart library and develop a graph but want to customize it according to my requirement but not able to find solution for my requirements my basic requirement is for x axis i want to show custom values at axis like 5-11 12-18 but i am passing value to x axis like this
private ArrayList<String> setXAxisValues() {
ArrayList<String> xVals = new ArrayList<String>();
xVals.add("10");
xVals.add("20");
xVals.add("30");
xVals.add("30.5");
xVals.add("40");
return xVals;
}
So it is showing x values like this 10 20 30 so on so i want my graph to be built upon using these x value which is happening right now but want to show custom value at bottom like 5-11 etc and this value is dynamic coming from Api response so please help me about this , Waiting for positive and early response Thanks in Advance
To format the x-axis values, you should use the setValueFormatter method which takes a callback interface were you have to implement the getFormattedValue method which is called before drawing the x-axis value.
xAxis.setValueFormatter((value, axis) -> {
String res = "";
try {
// get current position of x-axis
int currentPosition = (int) value;
// check if position between array bounds
if (currentPosition > -1 && currentPosition < maxSize) {
// get value from formatted values array
res = xVals.get(currentPosition);
}
} catch (Exception e) {
// handle exception
}
return res;
});
For line charts I find that if I have multiple y values for a single x, it works fine as long as it isn't the final x. If it's the final x, it only displays the first entry. Is there a known workaround for this?
Example:
//firstTimestamp is earlier than secondTimestamp
data.add(new Entry(firstTimestamp, 10));
data.add(new Entry(firstTimestamp, 20)); //won't show unless you uncomment below
data.add(new Entry(firstTimestamp, 30)); //won't show unless you uncomment below
//data.add(new Entry(secondTimestamp, 40));
Graph when second timestamp commented out:
Graph with second timestamp uncommented (note that the 20 and 30 are now included, whereas they weren't before):
Edit:
I believe I've found the cause of this problem and can fix it in the following way, by changing
public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer {
// ... lines removed ... //
public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) {
float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX()));
float low = chart.getLowestVisibleX();
float high = chart.getHighestVisibleX();
Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN);
Entry entryTo = dataSet.getEntryForXValue(high, Float.NaN, DataSet.Rounding.UP);
min = entryFrom == null ? 0 : dataSet.getEntryIndex(entryFrom);
max = entryTo == null ? 0 : dataSet.getEntryIndex(entryTo);
range = (int) ((max - min) * phaseX);
}
// ... lines removed ... //
}
to this, which I believe will resolve the issue:
public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer {
// ... lines removed ... //
public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) {
float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX()));
float low = chart.getLowestVisibleX();
float high = chart.getHighestVisibleX();
Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN);
//my edits here
int indexTo = dataset.getEntryIndex(high, Float.NaN, DataSet.Rounding.UP);
List<Entry> values = dataset.getValues();
while (indexTo + 1 < values.size() && values.get(indexTo + 1).getX() == high) {
indexTo++;
}
Entry entryTo = values.get(indexTo);
//my edits end here
min = entryFrom == null ? 0 : dataSet.getEntryIndex(entryFrom);
max = entryTo == null ? 0 : dataSet.getEntryIndex(entryTo);
range = (int) ((max - min) * phaseX);
}
// ... lines removed ... //
}
How can I subclass this / use these edits?
Please note that the only supported use case for LineChart entries is adding them ordered ascending. This is documented in the wiki:
Please be aware that this library does not officially support drawing LineChart data from an Entry list not sorted by the x-position of the entries in ascending manner.
The reason for this is that the renderer is optimised for unique ascending order entries.
If you want to get around this, I suggest you have a look at the source for LineChartRenderer. You will have to place breakpoints and find the issue that is causing it to render in the way you have demonstrated. Then you can consider subclassing the renderer to meet your requirement. Essentially you would be removing the optimisation in order to support the extra use case (non-unique values).
EDIT: If you are unwilling to manipulate the existing object graph to obtain the behaviour you want, you might want to consider forking the library with the change. Then you can build a .jar of your fork and include it in your Android project. Refer to the instructions in the answers below for the same:
How to make a .jar from an Android Studio project
How to add a .jar as a library in Android Studio
quick question. I am developing a top-down 2d Platformer game with lots of enemies in the map (at least a hundred spawn at the start of each level). Each enemy uses an AI that searches the map for objects with a specified tag, sorts each object into a list based on their distance, then reacts to the object closest to them.
My code works, but the thing is, if the machine my game is running on is slow, then my game lags. I want to be able to port my game to Android and iOS with low end specs.
In pursuit of putting less strain on the CPU, is there a better way to write my AI?
Here is my code:
void Start () {
FoodTargets = new List<Transform>(); // my list
SelectedTarget = null; // the target the enemy reacts to
myTransform = transform;
AddAllFood ();
}
public void AddAllFood()
{
GameObject[] Foods = GameObject.FindGameObjectsWithTag("Object");
foreach (GameObject enemy in Foods)
AddTarget (enemy.transform);
}
public void AddTarget(Transform enemy)
{
if (enemy.GetComponent<ClassRatingScript>().classrating != 1) { // classrating is an attribute each enemy has that determines their identity (like if they are a plant, a herbivore or a carnivore)
FoodTargets.Add (enemy); // adds the object to the list
}
}
private void SortTargetsByDistance() // this is how I sort according to distance, is this the fastest and most efficient way to do this?
{
FoodTargets.Sort (delegate(Transform t1, Transform t2) {
return Vector3.Distance(t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
});
}
private void TargetEnemy() // this is called every 4 frames
{
if (SelectedTarget == null) {
SortTargetsByDistance ();
SelectedTarget = FoodTargets [1];
}
else {
SortTargetsByDistance ();
SelectedTarget = FoodTargets [1];
}
}
if (optimizer <= 2) { // this is a variable that increments every frame and resets to 0 on the 3rd frame. Only every 3rd frame is the target enemy method is called.
optimizer++;
} else {
TargetEnemy ();
// the rest are attributes that the AI considers when reacting to their target
targetmass = SelectedTarget.GetComponent<MassScript> ().mass;
targetclass = SelectedTarget.GetComponent<ClassRatingScript> ().classrating;
mass = this.GetComponent<MassScript> ().mass;
classrating = this.GetComponent<ClassRatingScript> ().classrating;
distance = Vector3.Distance (transform.position, SelectedTarget.transform.position);
optimizer = 0;
}
Is there a more optimized way of doing this? Your help will be much appreciated. Thanks in advance!
I'm not awfully familiar with C# or Unity but I would look very carefully at what sorting algorithm your sorting method is using. If all you want is the closest Game Object, then sorting isn't necessary.
The fastest sorting algorithms, such as Quicksort, are O(n*log(n)). That is to say that the time it takes to sort a collection of n objects is bounded by some constant multiple of n*log(n). If you just want the k closest objects, where k << n, then you can perform k iterations of the Bubble Sort algorithm. This will have time-complexity O(k*n), which is much better then before.
However, if you only need the single closest object, then just find the closest object without sorting (pseudocode):
float smallestDistance = Inf;
object closestObject = null;
foreach object in objectsWithTag {
float d = distance(object, enemy);
if (d < smallestDistance) {
smallestDistance = d;
closestObject = object;
}
}
This extremely simple algorithm has time complexity O(n).
I am 99% sure that this cannot be done, however I thought I would ask to be certain.
I am attempting to create an application that calculates the required dice roll for an action in a popular tabletop war game.
The following is this calculation in Java
int x = ((WSattacker * 2) - WSdefender);
int y = (WSattacker - WSdefender);
String result;
// Calculation for a +5
if (x <= -1) {
result = "5+";
}
// Calculation for a +4
else if (x >= 0 && y <= 0) {
result = "4+";
}
// Calculation for a +3
else if (y > 0) {
result = "3+";
} else {
result = "Error";
}
return result;
Now my issue is that to avoid copywriter infringement I cannot mention the name of the game in my application, and probably cannot hard code the above calculation in the app.
This means that it is difficult to tell a potential user what the app will do.
The only solution I can think of is to make the application generic and allow the user to input the calculation required in the form of an equation.
An equation that I can place anonymously on a public board or similar.
Therefore my questions are as follows.
Is there another way of going about this?
If no, is it possible to condense the above code into a single expression/ equationi.e. one that removes the if and else statements
To answer question 2:
result = test_condition_1 ? result2_if_true : (test_condition_2 ? result2_if_true : test3_or_result2);
You can then build up 'compound' test conditions this way, and it's based upon ternary operators.
EDIT
Ternary operators are a short-hand way of writing if..then..else statments, and more information can be found in the wiki-link above. An example of its use is below, which you can compile and run:
public class TernaryTest {
public static void main(String [] args){
int x = 14;
int y = 5;
String result = ( x <= 10 ) ? "Less than 10" : "More than 10";
System.out.println("Result is: " + result);
}
}
Try running it and see the result as you change the value of x to understand how it works. Then it's possible to extend it to include and else by replacing the "more than 10" string.