开发者

Consume MySQL WKB geometry on iOS for MapKit

I know how to convert MySQL geometry from binary to text and then transfer/convert it for use in MapKit. However, this seems unnecessarily inefficient. I'm looking for a way to transport the binary shapes (like polygons) to their Mapkit relatives (like MkPolygon) with bandwidth efficiency in mind.

Is there a way of directly consuming these binary shapes and converting in iOS? Is the binary format sufficiently easy to parse? I'd like to avoid converting to text then back to binary until it's at least on the mobile device, so as to avoid the network bloat of transmitting millions of double precision floats depicted as character strings.

I've found Michael Weismann's ShapeKit on GitHub, which takes GEOS geometry and spits out MapKit objects. However, ShapeKit relies on WKT as the starting point, unless you have a fully instantiated GEOS object. Therefore, I hacked it up to build GEOS geometry from WKB bytes (by way of stuffing the MySQL WKB data into NSData), but it seems like the binary returned from MySQL isn't quite what it was expecting. Perhaps there's an endian issue ... ?

@implementation ShapeKitGeometry
@synthesize wktGeom,geomType, projDefinition ,geosGeom, numberOfCoords;

- (id) init
{
    self = [super init];
    if (self != nil) {
        // initialize GEOS library
        handle = initGEOS_r(notice, log_and_exit);
    }
    return self;
}

-(id)initWithWKB:(const unsigned char *) wkb size:(size_t)wkb_size {
    [self init];
    GEOSWKBReader *WKBReader = GEOSWKBReader_create_r(handle);
    self.geosGeom = GEOSWKBReader_read_r(handle, WKBReader, wkb, wkb_size);
    GEOSWKBReader_destroy_r(handle, WKBReader);
    self.geomType = [NSString stringWithUTF8String:GEOSGeomType_r(handle, geosGeom)];
    return self;
}

// .... later in my appDe开发者_运维百科legate:
- (ShapeKitPolygon *)loadWKBPolygonFromFile:(NSString *)file {
    ShapeKitPolygon * poly = nil;
    NSString *path = [[NSBundle mainBundle] pathForResource:file ofType:@"plist"];
    NSDictionary *stupidDict = [NSDictionary dictionaryWithContentsOfFile:path];
    NSData *geomData = [stupidDict objectForKey:@"shape"];
    if (geomData && [geomData length]) {
        poly = [[[ShapeKitPolygon alloc] initWithWKB:[geomData bytes] size:[geomData length]] autorelease];
    }
    return poly;
}

Thoughts?


Can't you select the polygons from the MySQL sever using the C API, then convert there, or are those extension's not available on the iPhone?

using: SELECT AsBinary(column_name) FROM table_name;


I've figured it out. Starting with Michael Weismann's ShapeKit (as noted in the initial question), I've crafted a few additional methods that will parse the WKB binary polygons (including any interior rings or "holes") and spit out the appropriate MKPolygon suitable for MapKit.

Problem solved....

 /* make sure you grab your Polygon data column from MySQL with the following:
    "select AsBinary(shapeColumn) from tableName;"


  .... add the first three methods found in the question to ShapeGeometry.mm 
    and your application's map view controller (the NSData usage example)

  ALSO, add this method further down in that file, for ShapeKitPolygon
*/

@implementation ShapeKitPolygon
@synthesize geometry;//,numberOfCoords;

-(id)initWithWKB:(const unsigned char *) wkb size:(size_t)wkb_size{
    size_t length = wkb_size;

    [super initWithWKB:wkb size:length];
    GEOSCoordSequence *sequence = nil;

    int numInteriorRings = GEOSGetNumInteriorRings_r(handle, geosGeom);
    NSMutableArray *interiors = [[NSMutableArray alloc] init];
    int interiorIndex = 0;
    for (interiorIndex = 0; interiorIndex< numInteriorRings; interiorIndex++) {
        const GEOSGeometry *interior = GEOSGetInteriorRingN_r(handle, geosGeom, interiorIndex);
        sequence = GEOSCoordSeq_clone_r(handle, GEOSGeom_getCoordSeq_r(handle, interior));
        unsigned int numCoordsInt = 0;
        GEOSCoordSeq_getSize_r(handle, sequence, &numCoordsInt); 
        CLLocationCoordinate2D coordsInt[numCoordsInt];
        for (int coord = 0; coord < numCoordsInt; coord++) {
            double xCoord = NULL;

            GEOSCoordSeq_getX_r(handle, sequence, coord, &xCoord);

            double yCoord = NULL;
            GEOSCoordSeq_getY_r(handle, sequence, coord, &yCoord);
            coordsInt[coord] = CLLocationCoordinate2DMake(yCoord, xCoord);
        }
        [interiors addObject:[MKPolygon polygonWithCoordinates:coordsInt count:numCoordsInt]];
        GEOSCoordSeq_destroy_r(handle, sequence);       
    }
    const GEOSGeometry *exterior = GEOSGetExteriorRing_r(handle, geosGeom);
    sequence = GEOSCoordSeq_clone_r(handle, GEOSGeom_getCoordSeq_r(handle, exterior));
    GEOSCoordSeq_getSize_r(handle, sequence, &numberOfCoords);
    CLLocationCoordinate2D coordsExt[numberOfCoords];
    for (int coord = 0; coord < numberOfCoords; coord++) {
        double xCoord = NULL;
        GEOSCoordSeq_getX_r(handle, sequence, coord, &xCoord);

        double yCoord = NULL;
        GEOSCoordSeq_getY_r(handle, sequence, coord, &yCoord);
        coordsExt[coord] = CLLocationCoordinate2DMake(yCoord, xCoord);
    }
    if ([interiors count])
        geometry = [MKPolygon polygonWithCoordinates:coordsExt count:numberOfCoords interiorPolygons:interiors];
    else
        geometry = [MKPolygon polygonWithCoordinates:coordsExt count:numberOfCoords];

    GEOSCoordSeq_destroy_r(handle, sequence);
    [interiors release];

    return self;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜