Find total no of Close polygons iOS - ios

I want to find total number of close shape.
In image, there are 6 no of close polygons .
I have tried following method
for (NSInteger i = 0; i < [arrLinesInfo count]; i++) {
NSDictionary *dictLineInfo = [arrLinesInfo objectAtIndex:i];
startPoint = CGPointMake([[dictLineInfo valueForKey:#"line_start_point_x"] doubleValue], [[dictLineInfo valueForKey:#"line_start_point_y"] doubleValue]);
endPoint = CGPointMake([[dictLineInfo valueForKey:#"line_end_point_x"] doubleValue], [[dictLineInfo valueForKey:#"line_end_point_y"] doubleValue]);
[self isCircularRoute:startPoint withEndPoint:endPoint];
}
-(void) isCircularRoute:(CGPoint) lineStartPoint withEndPoint:(CGPoint) lineEndPoint
{
NSPredicate *pre= [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"
(self.line_end_point_x == '%f' && self.line_end_point_y == '%f') OR
(self.line_start_point_x == '%f' && self.line_start_point_y == '%f') OR
(self.line_end_point_x == '%f' && self.line_end_point_y == '%f') OR
(self.line_start_point_x == '%f' && self.line_start_point_y == '%f')", lineStartPoint.x,
lineStartPoint.y,
lineStartPoint.x,
lineStartPoint.y,
lineEndPoint.x,
lineEndPoint.y,
lineEndPoint.x,
lineEndPoint.y]];
NSMutableArray *arrSamePointRef = [[arrLinesInfo filteredArrayUsingPredicate:pre] mutableCopy];
arrSamePointRef = [[arrSamePointRef filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"
(self.line_start_point_x != '%f' && self.line_start_point_y != '%f') &&
(self.line_end_point_x != '%f' && self.line_end_point_y != '%f')", lineStartPoint.x
, lineStartPoint.y
, lineEndPoint.x
, lineEndPoint.y]]] mutableCopy];//[arrSamePointRef removeObject:dictLineInfo];
if(arrSamePointRef.count > 2){
totalPolygon = totalPolygon + 1;
}
NSLog(#"totalPolygon : ===== %tu", totalPolygon);
for (NSDictionary *dictSingleLine in arrSamePointRef) {
CGPoint newStartPoint = CGPointMake([[dictSingleLine valueForKey:#"line_start_point_x"] doubleValue], [[dictSingleLine valueForKey:#"line_start_point_y"] doubleValue]);
CGPoint newEndPoint = CGPointMake([[dictSingleLine valueForKey:#"line_end_point_x"] doubleValue], [[dictSingleLine valueForKey:#"line_end_point_y"] doubleValue]);
[self isCircularRoute:newStartPoint withEndPoint:newEndPoint];
}
}
This is go in infinite loop.
I have all start point and end point object in array.
array object like below
[
{
"point_start_lbl" : "a",
"point_end_lbl" : "b",
"line_start_point_x" : 200,
"line_start_point_y" : 10,
"line_end_point_x" : 100,
"line_end_point_y" : 10,
}, ...
]
Please help me.
Thanks in advance.

You definitely have a closed polygon if you have an ordered list of edges such that each edge ends on the vertex that the next starts on and no edge is repeated within the list.
I'm unclear about your data structure but I might therefore:
Define an object, Edge that identifies two vertices.
For each vertex, create an array containing every single edge that touches that vertex.
Then, something like, in Swift-ish pseudocode:
var successfulPaths: [Edge] = []
for edge in edges
{
let usedEdges = [edge]
attemptTraversalFrom(edge.vertex1, edge.vertex2, usedEdges, successfulPaths)
attemptTraversalFrom(edge.vertex2, edge.vertex1, usedEdges, successfulPaths)
}
print("There were \(successfulPaths.count) successful paths")
[...]
func attemptTraversalFrom(startingVertex, endingVertex, usedEdges, successfulPaths) {
let vertexEdges = endingVertex.edges
for edge in (edges not in usedEdges) {
let newEndingVertex =
(edge.vertex1 == endingVertex) ? edge.vertex2 : edge.vertex1
if newEndingVertex == startingVertex {
successfulPaths.add(usedEdges)
return
} else {
let newUsedEdges = userEdges.addItem(edge)
attemptTraversalFrom(startingVertex, newEndingVertex, newUsedEdges, successfulPaths)
}
}
// Note: will automatically fall through to here and return
// without adding anything to `successfulPaths` if there are
// no further traversable edges
}
Extemporaneous, etc. A bit like the recursive part of Dijkstra's pathfinding algorithm, except that potential paths are accumulated rather than shorter paths eliminating longer ones prior to complete evaluation.

Related

Save multiple values with same keys in NSMutableDictionary then store the dictionary in NSMutableArray

I know this question has been asked many times but not got the solution or i might not understood that's why posting the question.
for (int web=0; web<[web_arr count]; web++) {
AdditionalBookmark *web_book=[[AdditionalBookmark alloc]init];
UITextField *txtNam = (UITextField*)[self.view viewWithTag:1100+web];
NSString *txtName = txtNam.text;
UITextField *txtLin = (UITextField*)[self.view viewWithTag:1200+web];
NSString *txtLink = txtLin.text;
if ((txtName && txtName.length > 0)||(txtLink && txtLink.length > 0))
{
web_book.additionalBookmark_Name = txtName;
web_book.additionalBookmark_Link= txtLink;
web_book.contactDetails_Id=self.contactDetailsinfo.contactDetailsId;
web_book.additionalBookmark_Id=[[[web_arr objectAtIndex:web] valueForKey:#"WeblinkId"] intValue];
[dictWebLinks setObject:txtName forKey:#"WeblinkName"];
[dictWebLinks setObject:txtLink forKey:#"WeblinkUrl"];
[arrForWebLinks addObject:dictWebLinks];
}
[db updateIntoAdditionalBookmarkWithObject:web_book];
}
[dictUpdate setObject:arrForWebLinks forKey:#"Weblink"];
The problem am facing is this suppose there are two items in the array
Printing description of web_arr:
<__NSArrayM 0x170e4db60>(
{
WeblinkId = 9;
WeblinkName = Hdhd;
WeblinkUrl = "ie.comh";
},
{
WeblinkId = 10;
WeblinkName = Hd;
WeblinkUrl = "nd.com";
}
)
what i am getting is below:
Printing description of arrForWebLinks:
<__NSArrayM 0x170e4db30>(
{
WeblinkName = Hd;
WeblinkUrl = "nd.com";
},
{
WeblinkName = Hd;
WeblinkUrl = "nd.com";
}
)
dictionary is replacing the value for same key holding the latest value only, how can i mange to save the values in same format as in web_arr.
Any help is appreciable.
You are adding the same dictionary to the array over and over:
[dictWebLinks setObject:txtName forKey:#"WeblinkName"];
[dictWebLinks setObject:txtLink forKey:#"WeblinkUrl"];
[arrForWebLinks addObject:dictWebLinks];
You need to create a new one each time:
[arrForWebLinks addObject:#{
#"WeblinkName" : txtName,
#"WeblinkUrl" : txtLink
}];
(and remove dictWebLinks).

How to see if NSArray, created with valueForKey is empty

I got an array:
NSArray *itemsArray = [self.tournamentDetails.groups valueForKey:#"Items"];
Where self.tournamentDetails.groups is an NSArray, build with a JSON string from a webRequest.
Items is sometimes empty and sometimes it contains objects. So i need an if statement to check if its empty or not. I've tried some different things like:
if ([itemsArray count]!=0)
if (!itemsArray || !itemsArray.count)
Problem is that if my Items object from valueForKey is empty then the itemsArray still contains an object looking like this
<__NSArrayI 0x178abef0>(
<__NSArrayI 0x16618c30>(
)
)
When theres items inside my Items object it looks like this:
<__NSArrayI 0x18262b70>(
<__NSCFArray 0x181e3a40>(
{
BirthDate = 19601006T000000;
ClubName = "Silkeborg Ry Golfklub";
ClubShortName = "";
CompletedResultSum = {
Actual = {
Text = 36;
Value = 36;
};
ToPar = {
Text = "";
Value = 0;
};
};
}
)
)
Meaning that [itemsArray count] is always equal to 1 or more, and then it jumps into the if statement when it should not.
Anyone know how i can create an if statement where if itemsArray contains "Items:[]" it will be skipped and if itemsArray contains "Items:[lots of objects]" it will run?
EDIT: Solution is to check up on the first index like this if([[itemsArray objectAtIndex:0] count] != 0) then run code.
Try this:
if(itemsArray && itemsArray.count>0) //be sure that it has value){
for(NSArray *item in itemsArray){
if(item.count > 0){
// you have an NSDictionary. Will process it
}else{
//item.count == 0 : this is an empty NSArray.
}
}
}
more simpler
for (id obj in itemsArray)
{
if([obj isKindOfClass:[NSArray class]])
{
if(obj.count > 0)
{
//your if condition here
}
}
}

Objective-C - How to get sum of Boleans?

Through the code below, I can get an output such as :
0
1
1
What I want is to output the sum of these booleans values, in my case the result will be : 2 because we have 0+1+1
The code [Update] :
-(void)markers{
CLLocationCoordinate2D tg = CLLocationCoordinate2DMake(location.latitude, location.longitude);
GMSCoordinateBounds *test = [[GMSCoordinateBounds alloc]initWithPath:path];
BOOL test3 = [test containsCoordinate:tg];
{
if (test3 == 1)
{
marker.position = CLLocationCoordinate2DMake(location.latitude, location.longitude);
}else if (test3 == 0)
{
marker.position = CLLocationCoordinate2DMake(0, 0);
}
}
}
}
Rather than sum BOOLs, which is counterintuitive, loop over whatever you are using to get the BOOL values, and if you get YES, increment a counter. This will be the number of YESs that you have.
If you have an array of BOOLs, you could just filter the array with a predicate to get the YES values and the length of the resulting array is the number of YESs that you have.
Edited to add code samples following OP comments
Incrementing a counter
NSUInteger numberOfBools = 0;
CLLocationCoordinate2D tg = CLLocationCoordinate2DMake(location.latitude, location.longitude);
GMSCoordinateBounds *test = [[GMSCoordinateBounds alloc]initWithPath:path];
if ([test containsCoordinate:tg1]) { ++numberOfBools; }
if ([test containsCoordinate:tg2]) { ++numberOfBools: }
... // other tests here;
// numberOfBools now contains the number of passing tests.
Edited Again, after the full code was added
// snipped code above here
// This is where you add the counter and initialise it to 0
NSUInteger numberOfBools = 0;
for (NSDictionary *dictionary in array)
{
// Snip more code to this point
BOOL test3 = [test containsCoordinate:tg];
{
if (test3)
{
// This is where you increment the counter
++numberOfBools;
// Snip more code to the end of the for block
}
// Now numberOfBools shows how many things passed test3
int sum = (test3 ? 1 : 0) + (testX ? 1 : 0) + (testY ? 1 : 0);
And not so weird variant:
#define BOOL_TO_INT(val) ((val) ? 1 : 0)
int sum = BOOL_TO_INT(test3) + BOOL_TO_INT(testX) + BOOL_TO_INT(testY);
You can just add BOOLs since bools are just integers. e.g. :
int sum = 0;
CLLocationCoordinate2D tg = CLLocationCoordinate2DMake(location.latitude, location.longitude);
GMSCoordinateBounds *test = [[GMSCoordinateBounds alloc]initWithPath:path];
BOOL test3 = [test containsCoordinate:tg];
//Obtain bolean values :
BOOL testX = /* other test */;
BOOL testY = /* other test */;
sum = test3 + testX + testY
This is a bad idea however, as BOOLs aren't necessarily 1 or 0. They are 0 and not 0
BOOL is just a typedef-ed char: typedef signed char BOOL; YES and NO are 1 and 0, but BOOL variable = 2 is perfectly valid
For example:
- (int) testX
{
if(inState1) return 1;
if(inState2) return 2;
else return 0;
}
BOOL textXResult = [self testX]; //Might return 2, this is still equivalent to YES.
The best solution is to iterate your BOOLs and instead count the number of YESes.
Another way to do this is to assume that if any one value is false, then the entire array is false, so, loop over the array until a false value is found, then break:
BOOL retval = true; //return holder variable
/*'boolsNumArray' is an NSArray of NSNumber instances, converted from BOOLs:
//BOOL-to-NSNumber conversion (makes 'boolsNumArray' NSArray, below)!
'[myNSArray addObject:[NSNumber numberWithBool:yourNextBOOL]]'
*/
for(NSNumber* nextNum in boolsNumArray) {
if([nextNum boolValue] == false) {
retval = false;
break;
}
}
return retval;

Get an array of 3 ordered values whilst favouring a specified value

Sorry for the somewhat generic title, if anyone has a better suggestion please let me know.
Basically I am writing a custom leaderboard view whereby I want to show 3 scores only. If possible it will show the current user's score in the middle but, if the user is at the top or the bottom of the list, it should still show 3 scores but itll show another users above or below the list.
e.g.
Me (If I am top, then show 2 below)
User 1
User 2
or
User 1
Me (usual case where I am in the middle of two scores)
User 2
or
User 1
User 2
Me (If I am bottom show two scores above me)
I have a function written that does the first part of this but doesnt take into account edge cases which is what I am struggling with. Can anyone please advise?
-(void)getNearbyScores:(int)score{
GCLeaderboardScore *closestScoreAbove = nil; //Custom container for GC properties
GCLeaderboardScore *closestScoreBelow = nil; //Contains playerID, score, alias etc
if ([playerScores count] == 0){ //playerScores is an NSMutableDictionary
return;
}
for (NSString* key in playerScores) {
GCLeaderboardScore *playerScore = (GCLeaderboardScore *)[playerScores objectForKey:key];
if ((closestScoreAbove == nil || closestScoreAbove->score > playerScore->score) && playerScore->score > score){
closestScoreAbove = playerScore;
}
else if ((closestScoreBelow == nil || closestScoreAbove->score < playerScore->score) && playerScore->score < score){
closestScoreBelow = playerScore;
}
}
me->score = score;
me->rank = 1;
if (closestScoreAbove != nil) {
me->rank = closestScoreAbove->rank + 1;
nearbyScores = [NSMutableArray arrayWithObjects: closestScoreAbove, me, closestScoreBelow, nil];
}
else {
nearbyScores = [NSMutableArray arrayWithObjects: me, closestScoreBelow, nil];
}
}
Assuming there is a me GCLeaderboardScore object, the method below should return an array with the desired GCLeaderboardScore objects (untested):
-(NSArray *)getNearbyScores {
if(playerScores.count==0) return nil;
// Create an array sorted by score
NSArray *sortedByScore=[playerScores sortedArrayUsingComparator: ^(id object1, id object2) {
GCLeaderboardScore *score1=object1;
GCLeaderboardScore *score2=object2;
if(score1->score < score2->score) return NSOrderedAscending;
if(score1->score > score2->score) return NSOrderedDescending;
return NSOrderedSame;
}];
// Find index of me
NSUInteger idx=[sortedByScore indexOfObject:me];
// If me not found, return nil
if(idx==NSNotFound) return nil;
// Ideally we want to show the player before and behind
idx=MAX(0,(NSInteger)idx-1);
// maxIdx will be idx+2 or index of last object if lower
NSUInteger maxIdx=MIN(sortedByScore.count-1,idx+2);
// In case we are last, show two previous results (if array large enough)
if (maxIdx > 3)
idx=MAX(0,maxIdx-3);
// And return the objects, may be 1..3 objects
return [sortedByScore subarrayWithRange:NSMakeRange(idx,maxIdx-idx+1)];
}
I assume you have an array of scores. (Actual implementation can be adapted to your code)
Initialize:
firstScoreAbove = VERY_LARGE_SCORE; secondScoreAbove = VERY_LARGE_SCORE + 1;
firstScoreBelow = -1; secondScoreBelow = -2;
Scan the array elements.
if ( newScore > myScore ) {
if ( newScore < firstScoreAbove ) {
secondScoreAbove = firstScoreAbove;
firstScoreAbove = newScore;
} else if ( newScore < secondScoreAbove ) {
secondScoreAbove = newScore;
}
} else {
// similarly for below.
}
After scanning,
If firstScoreAbove has not changed, then myScore is top and output the two below scores.
If firstScoreBelow has not changed, then myScore is lowest and output the two above scores.
Else Output firstScoreAbove, myScore, firstScoreBelow.

iOS Large SQLite data set slow performance

I have an application that I am working on for iOS that is to display trails on a map based on the bounding box of the current view. So as the user moves around the map it will load the appropriate set of trails.
The SQLite table is LARGE, 260k rows, where each row is a point on a trail. The table has a primary key (int), latitude (float), longitude (float) and a name (varchar). The latitude and longitude columns are indexed. When I query I am looking for where the latitude is between the lower right and upper left latitude and the longitude is between the upper left and lower right longitude. The query works flawlessly on desktop as well as on the phones as it returns the expected results. The issue is that on my Mac the query returns instantaniously where as on the phone it may take as little as 4 seconds before returning anything. The bottle neck does appear to be the database query and I'm starting to think its a limitation of the hardware.
I have tried using CoreData which is where I noticed the issue first. Then I moved to using FMDB to access the database and am still having issues.
I have done no tweaking to the database or to the connection.
The guts of the queryForTrails method
if( ![db open] ) {
[db release];
NSLog(#"Error opening DB: %#", dbPath);
}
FMResultSet *trails = [db executeQueryWithFormat:#"SELECT zname, zlatitude, zlongitude FROM ztrail WHERE ztype = 1 and zlatitude BETWEEN %# and %# AND zlongitude BETWEEN %# and %# order by zname", lrLat, ulLat, ulLon,lrLon];
//Start to load the map with data
NSString *lastTrailName=#"";
int idx = 0;
CLLocationCoordinate2D *trailCoords = nil;
NSUInteger coordSize = 20;
trailCoords = malloc(sizeof(CLLocationCoordinate2D)*coordSize);
while( [trails next] ) {
NSString *trailName = [trails stringForColumnIndex:0];
NSString *lat = [trails stringForColumnIndex:1];
NSString *lon = [trails stringForColumnIndex:2];
if( [lastTrailName compare:trailName] != NSOrderedSame ) {
if(idx > 0) {
[trailLines addObject:[MKPolyline polylineWithCoordinates:trailCoords count:idx]];
free(trailCoords);
idx = 0;
coordSize = 20;
}
lastTrailName = trailName;
trailCoords = malloc(sizeof(CLLocationCoordinate2D)*coordSize);
}
if(idx == coordSize) {
coordSize *= 2;
trailCoords = realloc(trailCoords, sizeof(CLLocationCoordinate2D) * coordSize);
}
trailCoords[idx++] = CLLocationCoordinate2DMake([lat doubleValue], [lon doubleValue]);
}
//Build the new polyline
[trailLines addObject:[MKPolyline polylineWithCoordinates:trailCoords count:idx]];
//NSLog(#"Num Trails: %d", [trailLines count]);
free(trailCoords);
//NSLog(#"Num of Points %d for %#",idx, lastTrailName);
if( [trailLines count] > 0 ) {
dispatch_async(dispatch_get_main_queue(),^{
[mapView addOverlays:trailLines];
});
}
I can provide some NSLog data if needed. I will also be doing the same application for Android so I'd like to try and work out the performance issues now.

Resources