JohnMars—ParametricObject

CityGrabber by @john_a_mars extracts meshes from Nokia Maps

Last year, I was introduced to migurski/NokiaWebGL via Clement Valla’s 3d-maps-minus–3d. NokiaWebGL is a Python module that extracts the 3D tiles, i.e., buildings, textures, and terrains, from Here.com née Nokia Maps. Using chaosct/ofxPython, I was able to implement that module in openFrameworks.

I modified migurski’s existing code slightly to better utilize the WaveFront OBJ spec (they were creating multiple OBJs and simply downloading the JPEG textures; I refactored the code to combine all the meshes into a single file, as well as create MTL files that linked the textures). In order to display the OBJs in OF, I used the built-in ofxAssimpModelViewer, but had to update it to this fix (which is currently in the master branch, but not on 0.8.4).

At its current stage, the objects created by the app are not printable; they’re simply two-sided surfaces. My next step is to devise a way to create a parametric base in the OBJ, by somehow determining the edges of the model extruding them downward. My end goal is to make a website that will automatically generate these objects and ship them off to Shapeways to print. For that, I’ll need to find a way to embed OF on a server, and come up with an effective front-end GUI.

The project’s ofApp.cpp:

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

    ofDisableArbTex();
    ofEnableBlendMode(OF_BLENDMODE_ALPHA);
    glShadeModel(GL_SMOOTH);

    python.init();
    python.executeScript("sandbox.py");
    
    ofxPythonObject myApp = python.getObject("MyApp");
    
    float lat = 40.709620;
    float lon = -74.009086;
    float zoom = 19;
    
    if (myApp) {
        python_program = myApp();
    }
    if (python_program) {
        python_program.attr("lat") = python.evalString(ofToString(lat));
        python_program.attr("lon") = python.evalString(ofToString(lon));
        python_program.attr("zoom") = python.evalString(ofToString(zoom));
        
        python_program.method("go");
        
        model.loadModel("objs/LAT" + ofToString((int)lat) + "_LON" + ofToString((int)lon) + "_Z" + ofToString((int)zoom) + ".obj");
        
        for (int i = 0; i < model.getNumMeshes(); i++) {
            model.getMesh(i).getVertices();
        }
        
        base.set(model.getSceneMax().x - model.getSceneMin().x, model.getSceneMax().y - model.getSceneMin().y);
    }
    
    cam.setDistance(1000);
}

//--------------------------------------------------------------
void ofApp::update(){

}

//--------------------------------------------------------------
void ofApp::draw(){

    ofSetColor(255);
    
    ofEnableDepthTest();
    ofEnableSeparateSpecularLight();
    ofEnableLighting();
    light.enable();
    light.setDirectional();
    light.setOrientation(ofVec3f(45, 90));
    
    cam.begin();

    model.setPosition(model.getSceneCenter().x, model.getSceneCenter().y, 0);
    model.drawFaces();

    
    cam.end();
    
    light.disable();
    ofDisableLighting();
    ofDisableSeparateSpecularLight();
    ofDisableDepthTest();

}

A little snippet of the python code

class MyApp(object):

    def __init__(self):
        pass

    def go(self):

        self.p = get_projection()

        print self.lat, self.lon, self.zoom

        self.loc = Location(self.lat, self.lon)

        self.coord = self.p.locationCoordinate(self.loc).zoomTo(self.zoom)

        self.textures = get_tile_data(self.coord)

        #
        # Output .obj files and JPEGs locally.
        #

        if os.path.exists('../../../data/objs/LAT%d_LON%d_Z%d.obj' % (self.lat, self.lon, self.zoom)) == False:

            obj = open('../../../data/objs/LAT%d_LON%d_Z%d.obj' % (self.lat, self.lon, self.zoom), 'w')
            obj.write('o LAT%d_LON%d_Z%d\n' % (self.lat, self.lon, self.zoom))
            obj.write('\nmtllib LAT%d_LON%d_Z%d.mtl\n' % (self.lat, self.lon, self.zoom))

            mtl = open('../../../data/objs/LAT%d_LON%d_Z%d.mtl' % (self.lat, self.lon, self.zoom), 'w')

            lastnumvert = 1
            for (i, (vertices, faces, image_url)) in enumerate(self.textures):

                obj.write('\ng mesh%d =====================\n' % (i))
                obj.write('usemtl mtl%d' % (i))

                obj.write("\n# List of vertices (x y z)\n")
                for (x, y, z, u, v) in vertices:
                    obj.write('v %.1f %.1f %.1f\n' % (x, y, z))

                obj.write("\n\n# List of texture coordinates (u v)\n")
                for (x, y, z, u, v) in vertices:
                    obj.write('vt %.6f %.6f\n' % (u, v))

                obj.write("\n\n# List of face definitions\n")
                for (v0, v1, v2) in faces:
                    obj.write('f %d/%d %d/%d %d/%d\n' % (v0+lastnumvert, v0+lastnumvert, v1+lastnumvert, v1+lastnumvert, v2+lastnumvert, v2+lastnumvert))
                lastnumvert += len(vertices)

                mtl.write("newmtl mtl%d\n" % (i))
                mtl.write("map_Ka LAT%d_LON%d_Z%d_%d.jpg\n"  % (self.lat, self.lon, self.zoom, i))
                mtl.write("map_Kd LAT%d_LON%d_Z%d_%d.jpg\n"  % (self.lat, self.lon, self.zoom, i))

                jpg = open('../../../data/objs/LAT%d_LON%d_Z%d_%d.jpg' % (self.lat, self.lon, self.zoom, i), 'w')
                jpg.write(urlopen(image_url).read())
                jpg.close()

            mtl.close()
            obj.close();

            print "SAVED"
        else:
            print "ALREADY EXISTS"

Full code available here: https://github.com/marsman12019/nokiaGrabber