I've searched a lot but I can't find what I'm doing wrong. I'm trying to get 2 numbers that are stored in Firebase (uses a json file) and then create google maps markers with one of these numbers being the latitude and the other the longitude.
__block int i =0;
while (i < 5) {
i++;
NSString *url = [NSString stringWithFormat:#"---myfirebaseurl---", i];
Firebase *ref = [[Firebase alloc] initWithUrl:url];
[ref observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
if (snapshot.value == [NSNull null]){
i = 5;
}
if (snapshot.value != [NSNull null]){
NSString *latitudestring = snapshot.value[#"latitude"];
NSString *longitudestring = snapshot.value[#"longitude"];
long latitude = [latitudestring longLongValue];
long longitude = [longitudestring longLongValue];
NSLog(#"latitude: %li and longitude: %li", latitude, longitude);
CLLocationCoordinate2D position = CLLocationCoordinate2DMake(latitude, longitude);
GMSMarker *marker = [GMSMarker markerWithPosition:position];
marker.map = mapview;
marker.title = (#"%#", snapshot.value[#"nome"]);
marker.snippet = (#"%#", snapshot.value[#"endereco"]);
}
}
];
}
Sorry if the code it's kinda messy.
This error message appears: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString objectForKeyedSubscript:]
And the google maps markers aren't created at all. Any insight on this?
I found the answer just by changing FEEventTypeChildAdded to FEEventTypeValue. I can't really explain the reasoning, but at least it works.
Related
I am working on an app that composes multiple video clips taken by the user. The clips are recorded on the camera, and overlayed with another video and then the recorded clips are composed together into one long clip. The length of each clip is determined by the overlaying video file.
I am using an AVAssetExportSession and exportAsynchronouslyWithCompletionHandler. The odd thing is this works with some clips and not others. The real problem is that the exporter doesn't report any errors or failures, just zero progress and never calls the completion handler.
I don't even know where to begin looking to find out what the issue is. Here's the function I use to compose the clips together
- (void) setupAndStitchVideos:(NSMutableArray*)videoData
{
// Filepath to where the final generated video is stored
NSURL * exportUrl = nil;
// Contains information about a single asset/track
NSDictionary * assetOptions = nil;
AVURLAsset * currVideoAsset = nil;
AVURLAsset * currAudioAsset = nil;
AVAssetTrack * currVideoTrack = nil;
AVAssetTrack * currAudioTrack = nil;
// Contains all tracks and time ranges used to build the final composition
NSMutableArray * allVideoTracks = nil;
NSMutableArray * allVideoRanges = nil;
NSMutableArray * allAudioTracks = nil;
NSMutableArray * allAudioRanges = nil;
AVMutableCompositionTrack * videoTracks = nil;
AVMutableCompositionTrack * audioTracks = nil;
// Misc time values used when calculating a clips start time and total length
float animationLength = 0.0f;
float clipLength = 0.0f;
float startTime = 0.0f;
CMTime clipStart = kCMTimeZero;
CMTime clipDuration = kCMTimeZero;
CMTimeRange currRange = kCMTimeRangeZero;
// The final composition to be generated and exported
AVMutableComposition * finalComposition = nil;
// Cancel any already active exports
if (m_activeExport)
{
[m_activeExport cancelExport];
m_activeExport = nil;
}
// Initialize and setup all composition related member variables
allVideoTracks = [[NSMutableArray alloc] init];
allAudioTracks = [[NSMutableArray alloc] init];
allVideoRanges = [[NSMutableArray alloc] init];
allAudioRanges = [[NSMutableArray alloc] init];
exportUrl = [NSURL fileURLWithPath:[MobveoAnimation getMergeDestination]];
finalComposition = [AVMutableComposition composition];
videoTracks = [finalComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
audioTracks = [finalComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
assetOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
animationLength = m_animation.videoDuration;
// Define all of the audio and video tracks that will be used in the composition
for (NSDictionary * currData in videoData)
{
currVideoAsset = [AVURLAsset URLAssetWithURL:[currData objectForKey:KEY_STITCH_VIDEO_URL] options:assetOptions];
currAudioAsset = [AVURLAsset URLAssetWithURL:[currData objectForKey:KEY_STITCH_AUDIO_URL] options:assetOptions];
currVideoTrack = [[currVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
NSArray *audioTracks = [currAudioAsset tracksWithMediaType:AVMediaTypeAudio];
if ( audioTracks != nil && audioTracks.count > 0 )
{
currAudioTrack = audioTracks[0];
}
else
{
currAudioTrack = nil;
}
clipLength = animationLength * [(NSNumber*)[currData objectForKey:KEY_STITCH_LENGTH_PERCENTAGE] floatValue];
clipStart = CMTimeMakeWithSeconds(startTime, currVideoAsset.duration.timescale);
clipDuration = CMTimeMakeWithSeconds(clipLength, currVideoAsset.duration.timescale);
NSLog(#"Clip length: %.2f", clipLength);
NSLog(#"Clip Start: %lld", clipStart.value );
NSLog(#"Clip duration: %lld", clipDuration.value);
currRange = CMTimeRangeMake(clipStart, clipDuration);
[allVideoTracks addObject:currVideoTrack];
if ( currAudioTrack != nil )
{
[allAudioTracks addObject:currAudioTrack];
[allAudioRanges addObject:[NSValue valueWithCMTimeRange:currRange]];
}
[allVideoRanges addObject:[NSValue valueWithCMTimeRange:currRange]];
startTime += clipLength;
}
[videoTracks insertTimeRanges:allVideoRanges ofTracks:allVideoTracks atTime:kCMTimeZero error:nil];
if ( allAudioTracks.count > 0 )
{
[audioTracks insertTimeRanges:allAudioRanges ofTracks:allAudioTracks atTime:kCMTimeZero error:nil];
}
for ( int i = 0; i < allVideoTracks.count - allAudioTracks.count; ++i )
{
CMTimeRange curRange = [allVideoRanges[i] CMTimeRangeValue];
[audioTracks insertEmptyTimeRange:curRange];
}
// Delete any previous exported video files that may already exist
[[NSFileManager defaultManager] removeItemAtURL:exportUrl error:nil];
// Begin the composition generation and export process!
m_activeExport = [[AVAssetExportSession alloc] initWithAsset:finalComposition presetName:AVAssetExportPreset1280x720];
[m_activeExport setOutputFileType:AVFileTypeQuickTimeMovie];
[m_activeExport setOutputURL:exportUrl];
NSLog(#"Exporting async");
[m_activeExport exportAsynchronouslyWithCompletionHandler:^(void)
{
NSLog(#"Export complete");
// Cancel the update timer
[m_updateTimer invalidate];
m_updateTimer = nil;
// Dismiss the displayed dialog
[m_displayedDialog hide:TRUE];
m_displayedDialog = nil;
// Re-enable touch events
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
// Report the success/failure result
switch (m_activeExport.status)
{
case AVAssetExportSessionStatusFailed:
[self performSelectorOnMainThread:#selector(videoStitchingFailed:) withObject:m_activeExport.error waitUntilDone:FALSE];
break;
case AVAssetExportSessionStatusCompleted:
[self performSelectorOnMainThread:#selector(videoStitchingComplete:) withObject:m_activeExport.outputURL waitUntilDone:FALSE];
break;
}
// Clear our reference to the completed export
m_activeExport = nil;
}];
}
EDIT:
Thanks to Josh in the comments I noticed there were error parameters I wasn't making use of. In the case where it is failing now I am getting the ever so useful "Operation could not be completed" error on inserting the time ranges of the video tracks:
NSError *videoError = nil;
[videoTracks insertTimeRanges:allVideoRanges ofTracks:allVideoTracks atTime:kCMTimeZero error:&videoError];
if ( videoError != nil )
{
NSLog(#"Error adding video track: %#", videoError);
}
Output:
Error adding video track: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x17426dd00 {NSUnderlyingError=0x174040cc0 "The operation couldn’t be completed. (OSStatus error -12780.)", NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed}
It is worth noting however that nowhere in this entire codebase is urlWithString used instead of fileUrlWithPath so that isn't the problem.
Judging from your for in enumeration of the videoData array, after you've Initialized the composition member variables, it looks as if you're blocking the calling thread. Although accessing each AVAssetTrack instance is permitted, the values for the keys are not always immediately available and run synchronously..
Instead, try registering for change notifications using AVSynchronousKeyValueLoading protocols. Apple's documentation should help you straighten out the issue and get you on your way!
Here are a few more Apple recommendations I've aggregated for AVFoundation:
Hopefully this will do the trick! Good luck and let me know if you have any further questions/problems.
I'm receiving a list of latitude and longitudes by the web service and geocoding then to get the full address.
The problem is that after some time, they become null, and the geocoder stop working!
Can somebody help me?
Here's the code that I'm using to get the address:
-(void)getAddressArray:(void (^)(id response))completion{
for (int i=0; i<[favoritos count]; i++) {
double latitude = [[[favoritos valueForKey:#"latitude"] objectAtIndex:i] doubleValue];
double longitude = [[[favoritos valueForKey:#"longitude"] objectAtIndex:i] doubleValue];
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(latitude, longitude);
CLLocation * locationAtual = [[CLLocation alloc]initWithLatitude:location.latitude longitude:location.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:locationAtual completionHandler:^(NSArray *placemarks, NSError *error) {
NSDictionary *dicAddress;
CLPlacemark *place = [placemarks firstObject];
if (placemarks) {
NSLog(#"%#",place.addressDictionary);
if ([place.addressDictionary valueForKey:#"Thoroughfare"] == [NSNull null] || ![place.addressDictionary valueForKey:#"Thoroughfare"]) {
dicAddress = #{#"id":[[favoritos objectAtIndex:i] valueForKey:#"id"],#"address":#"Endereço não encontrado!",#"complemento":[[favoritos objectAtIndex:i] valueForKey:#"references"],#"number":[[favoritos valueForKey:#"numero"] objectAtIndex:i],#"name":[[favoritos valueForKey:#"name"] objectAtIndex:i], #"latitude":[[favoritos valueForKey:#"latitude"] objectAtIndex:i], #"longitude":[[favoritos valueForKey:#"longitude"] objectAtIndex:i]};
}
[address addObject:dicAddress];
if ([address count] == [favoritos count])
completion(address);
}
}];
}
Thanks!
Check the error passed in to the block, probably you are sending to many request and the rate limit is reached.
There is rate limit for reverseGeocodeLocation:completionHandler:
Geocoding requests are rate-limited for each app, so making too many
requests in a short period of time may cause some of the requests to
fail. When the maximum rate is exceeded, the geocoder passes an error
object with the value kCLErrorNetwork to your completion handler.
I have a problem when displaying a route in MapView if route has waypoints. I use DIRECTIONS GOOGLE API:
https://developers.google.com/maps/documentation/directions/
The route is not drawn using the road.
Any help? Thanks!!
The trouble images:
The code:
reponse = [self getRouteFromGoogleApiWithPoints:arrayOfCoords andMode:#"driving" error:error];
if(reponse == nil)
{
UIAlertView *succes= [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"txtError", nil) message:NSLocalizedString(#"ServerError", nil) delegate: self cancelButtonTitle: #"OK" otherButtonTitles: nil];
[succes show];
}
else
{
NSArray *routes = [reponse objectForKey:#"routes"];
NSDictionary *route = [routes objectAtIndex:0];
NSDictionary *polylineOverview = [route objectForKey:#"overview_polyline"];
NSString *polylinePoints = [polylineOverview objectForKey:#"points"];
NSArray *decodedPolyline = [self decodePolyLine:polylinePoints];
CLLocationCoordinate2D coords[[decodedPolyline count]];
int i=0;
for(CLLocation* loc in decodedPolyline)
{
CLLocationCoordinate2D c;
c.latitude = loc.coordinate.latitude;
c.longitude = loc.coordinate.longitude;
coords[i]=c;
i++;
}
MKPolyline *line = [MKPolyline polylineWithCoordinates:(CLLocationCoordinate2D*)coords count:decodedPolyline.count];
[mapview addOverlay:line];
[mapview setNeedsDisplay];
}
}
}
+ (NSMutableArray *)decodePolyLine: (NSString *)encoded {
NSInteger len = [encoded length];
NSInteger index = 0;
NSMutableArray *array = [[NSMutableArray alloc] init];
NSInteger lat=0;
NSInteger lng=0;
while (index < len) {
NSInteger b;
NSInteger shift = 0;
NSInteger result = 0;
do {
b = [encoded characterAtIndex:index++] - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
NSInteger dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = [encoded characterAtIndex:index++] - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
NSInteger dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
lng += dlng;
NSNumber *latitude = [[NSNumber alloc] initWithFloat:lat * 1e-5];
NSNumber *longitude = [[NSNumber alloc] initWithFloat:lng * 1e-5];
CLLocation *loc = [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]];
[array addObject:loc];
}
return array;
}
+(NSDictionary*)getRouteFromGoogleApiWithPoints:(NSArray*)arrayOfPoints andMode:(NSString*)mode error:(NSError **)error
{
NSDictionary *result = nil;
NSString *waypoints = #"";
CLLocation* origin = [arrayOfPoints objectAtIndex:0];
CLLocation* destination = [arrayOfPoints objectAtIndex:arrayOfPoints.count - 1];
// Create the waypoints
for(int i = 1; i < arrayOfPoints.count - 2; i++)
{
CLLocation* current = [arrayOfPoints objectAtIndex:i];
waypoints = [waypoints stringByAppendingString:[NSString stringWithFormat:#"%f,%f%%7C",current.coordinate.latitude,current.coordinate.longitude]];
}
CLLocation* lastWaypoint = [arrayOfPoints objectAtIndex:arrayOfPoints.count - 2];
waypoints = [waypoints stringByAppendingString:[NSString stringWithFormat:#"%f,%f",lastWaypoint.coordinate.latitude,lastWaypoint.coordinate.longitude]];
NSString *urlString =[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&waypoints=%#&sensor=true&mode=%#",origin.coordinate.latitude,origin.coordinate.longitude,destination.coordinate.latitude,destination.coordinate.longitude,waypoints,mode];
NSDictionary* response = [self getDictionaryFromURLString:urlString withParameters:nil error:error];
if (response != nil)
{
if ([[response objectForKey:#"status"] isEqualToString:#"OK"])
{
result = response;
NSLog(#"%#",response);
}
else
{
if (error)
{
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:[response objectForKey:#"status"] forKey:NSLocalizedDescriptionKey];
*error = [[NSError alloc] initWithDomain:#"AppName" code:2 userInfo:details];
}
}
}
else
{
if(error)
{
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:NSLocalizedString(#"ErrorServidor", nil) forKey:NSLocalizedDescriptionKey];
*error = [NSError errorWithDomain:#"AppName" code:1 userInfo:details];
return nil;
}
}
return result;
}
First off, according to the Google Terms & Conditions you're not allowed to use their directions/data on someone else's maps.
Secondly, the first is partly imposed because people have different data, place roads in different spots, break the roads into different segments etc etc and thus instructions don't line up.
If you want to show directions in your map you'll have to find another source. Maybe you should consider the CloudMade API.
Now you could use Google Maps SDK for iOS. That's the only legal option if you need to display Google Directions API under iOS6.
You can use Mapkit direction request API.
Direction request api has some limitation for consecutive calls made otherwise it works similar as google direction api.
There are ways to fetch direction for driving, walking and transit.
Also there is a way to get alternate routes.
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
MKPlacemark *sourcePlacemark = [[MKPlacemark alloc] initWithCoordinate:SourceCLLocationCorodinate).coordinate addressDictionary:nil];
MKMapItem *sourceMapItem = [[MKMapItem alloc] initWithPlacemark:sourcePlacemark];
[request setSource:sourceMapItem];
MKPlacemark *destPlacemark = [[MKPlacemark alloc] initWithCoordinate:destCLLocationCoordinate addressDictionary:nil];
MKMapItem *destMapItem = [[MKMapItem alloc] initWithPlacemark:destPlacemark];
[request setDestination:destMapItem];
[request setTransportType:MKDirectionsTransportTypeAny];
request.requestsAlternateRoutes = NO;
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
if(![directions isCalculating])
{
[directions calculateDirectionsWithCompletionHandler:
^(MKDirectionsResponse *response, NSError *error) {
if (error)
{
// Handle Error
NSLog(#"Error for this particular call");
}
else
{
for (MKRoute * route in response.routes)
{
//Add the route.polyline to the mapkit overlay
}
}];
}
I have this code:
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
NSNumber* existingpoints = [[NSNumber alloc]init];
existingpoints =[NSNumber numberWithInt:0];
// This is important if you only want to receive one tap and hold event
if (sender.state == UIGestureRecognizerStateEnded)
{
[self.mapView removeGestureRecognizer:sender];
}
else {
do {
int z = 1;
existingpoints =[NSNumber numberWithInt:z];
// Here we get the CGPoint for the touch and convert it to latitude and longitude coordinates to display on the map
CGPoint point = [sender locationInView:self.mapView];
CLLocationCoordinate2D locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
// Then all you have to do is create the annotation and add it to the map
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init]; annotationPoint.coordinate = locCoord;
NSString *latitude = [[NSString alloc] initWithFormat:#"%f",locCoord.latitude];
NSString *longitude = [[NSString alloc] initWithFormat:#"%f", locCoord.longitude];
annotationPoint.title = #"Event";
annotationPoint.subtitle = [NSString stringWithFormat:#"%# & %#", latitude, longitude];
[mapView addAnnotation:annotationPoint];
[[NSUserDefaults standardUserDefaults]setObject:latitude forKey:#"FolderLatitude"];
[[NSUserDefaults standardUserDefaults]setObject:longitude forKey:#"FolderLongitude"];
} while ([existingpoints intValue] == 0);
}
}
...but the problem is that when I hold, and then drag more than one pin is added. I want to add only one pin. So I tried the do method but it doesn't work. I can't understand, because when I executed the code I turn the value of the NSNumber to 1, and the while says = 0 to run the code.
Please Help!!
Your current code is prone to have quite a number of memory leaks. For example:
NSNumber* existingpoints = [[NSNumber alloc] init];
existingpoints = [NSNumber numberWithInt:0];
Is leaking because you leave the first instance of existingpoints with retain value of 1 and not freeing it anywhere. Unless you're using ARC. You can optimize the above code with just one instruction:
NSNumber* existingpoints = [NSNumber numberWithInt:0];
And retain it if you need to keep it somewhere (but i belive it's not the case).
Analyzing the code, I'd recommend NOT to use existingpoints as an NSNumber. Use an NSInteger instead (which is not an object, just a typedef to long).
Here's my rewritten code:
-(void)handleLongPressGesture:(UIGestureRecognizer*)sender {
NSInteger existingpoints = 0;
// This is important if you only want to receive one tap and hold event
if (sender.state == UIGestureRecognizerStateEnded) {
[self.mapView removeGestureRecognizer:sender];
}
else {
do {
int z = 1;
existingpoints = z;
// Here we get the CGPoint for the touch and convert it to latitude and longitude coordinates to display on the map
CGPoint point = [sender locationInView:self.mapView];
CLLocationCoordinate2D locCoord = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
// Then all you have to do is create the annotation and add it to the map
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = locCoord;
NSString *latitude = [NSString stringWithFormat:#"%f",locCoord.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", locCoord.longitude];
annotationPoint.title = #"Event";
annotationPoint.subtitle = [NSString stringWithFormat:#"%# & %#", latitude, longitude];
[mapView addAnnotation:annotationPoint];
[[NSUserDefaults standardUserDefaults] setObject:latitude forKey:#"FolderLatitude"];
[[NSUserDefaults standardUserDefaults] setObject:longitude forKey:#"FolderLongitude"];
[annotationPoint release]; // Remove this if you're using ARC.
} while (existingpoints == 0);
}
}
Note that I've also changed the code for creating latitude and longitude for not to create any memory leaks when using ARC.
EDIT:
Further analyzing your code, I don't see why this method would be dropping two pins at once. Maybe you could check if your method is not being called twice?
More: Why do you have a do/while loop if you just want it to run once? (but maybe you're just paving your ground to further ahead)
I am trying to find and set the location as shown below but I keep getting the error 'Invalid Initializer'
CLLocationCoordinate2D location =[self.mapView addressLocation];
Where addressLocation method is as show below
-(CLLocationCoordinate2D) addressLocation {
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps/geo?q=%#&output=csv",
[searchBar.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
//NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString]];
NSError* error;
NSString *locationString = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString] encoding:NSASCIIStringEncoding error:&error];
NSArray *listItems = [locationString componentsSeparatedByString:#","];
double latitude = 0.0;
double longitude = 0.0;
if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:#"200"]) {
latitude = [[listItems objectAtIndex:2] doubleValue];
longitude = [[listItems objectAtIndex:3] doubleValue];
}
else {
//Show error
}
CLLocationCoordinate2D location;
location.latitude = latitude;
location.longitude = longitude;
return location;
}
Can you advise why I am getting the 'Invalid initializer' error? I have already imported the corelocation location framework and also imported the header files. So not sure what is wrong.
You are invoking [self.mapView addressLocation]. Shouldn't it be [self addressLocation]? I'm presuming mapView is the MKMapView in your class and doesn't have any addressLocation method.