{"id":28450249,"url":"https://github.com/tangrams/ofxvectortile","last_synced_at":"2025-07-01T13:31:03.595Z","repository":{"id":21713653,"uuid":"25035184","full_name":"tangrams/ofxVectorTile","owner":"tangrams","description":null,"archived":false,"fork":false,"pushed_at":"2016-02-05T15:47:40.000Z","size":27222,"stargazers_count":52,"open_issues_count":2,"forks_count":11,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-05-24T07:44:16.831Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tangrams.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-10-10T12:48:49.000Z","updated_at":"2025-03-21T22:23:11.000Z","dependencies_parsed_at":"2022-08-17T23:15:25.136Z","dependency_job_id":null,"html_url":"https://github.com/tangrams/ofxVectorTile","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tangrams%2FofxVectorTile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tangrams%2FofxVectorTile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tangrams%2FofxVectorTile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tangrams%2FofxVectorTile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tangrams","download_url":"https://codeload.github.com/tangrams/ofxVectorTile/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tangrams%2FofxVectorTile/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":258422543,"owners_count":22698491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-06T15:06:53.618Z","updated_at":"2025-07-01T13:31:03.588Z","avatar_url":"https://github.com/tangrams.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)\n\n## VectorTile\n\nThis addon is a wrapper from a [Mapzen](mapzen.com) [Tangram](https://github.com/tangrams) experiment on 3D Labels \n\n## Install\n\nInstall addons dependences: \n\n```\n\tcd openFrameworks/addons\n\tgit clone --depth 1 http://github.com/patriciogonzalezvivo/ofxGlmTools.git\n\tgit clone --depth 1 http://github.com/jefftimesten/ofxJSON.git\n```\n\n## Add to project\n\nInstall ```ofxVectorTile``` to your project using the ProjectGenerator, dragging folder or Adam’s [OFPlugin](https://github.com/admsyn/OFPlugin). \n\nOn the project Properties (the blue icons with the name of your project) go to `Building Settings` \u003e `Linking` \u003e `Other Linker Flags` and add `-lcurl`\n\n## Dependences\n\n- [ofxGlmTools](https://github.com/patriciogonzalezvivo/ofxGlmTools)\n- [ofxJSON](https://github.com/jefftimesten/ofxJSON)\n\n## Sources\n\n- [glmTile](https://github.com/tangrams/glmTile)\n\n\t* [cURL](http://curl.haxx.se/libcurl/)\n\t* [JsonCpp](https://github.com/open-source-parsers/jsoncpp)\n\t* [Fontstash](https://github.com/memononen/fontstash)\n\n\n## The Problem\nLabels in maps are a huge headache, and have been for a long time. There are lots of solutions for 2D mapping, but not so many when you work in 3D, and especially not when your map shifts between 2D and 3D. \n\n## The Experiments\n\nThe main trick is a 2D projection of the geometry to place the text, within the canvas. Like a heads-up display layer between the 3D geometry and the camera.\n\nIn the canvas: \n![projection](images/projection.png)\n\nPros:\n\n* Fonts don’t need to be resized on zoom\n\n* No off-screen computations\n\n* We know exactly what the user sees\n\nCons:\n\n* Occlusion becomes a problem: Should labels be visible through buildings? Which labels to prioritize, how to prioritize them? For now, these have no occlusion. \n\n### Line Labels\n\n* Fit and repeat\n\n```\n\tfloat lastSeed = 0.0;\n\tfor (int i = 0; i \u003c _anchorLine.size()-1; i++) {\n\t\tfloat offset = _anchorLine.getDistances()[i];\n\t\tfloat segmentLength = _anchorLine.getDistances()[i+1]-_anchorLine.getDistances()[i];//_anchorLine.getPolars()[i].r;\n\n\t\t//  Fits?\n\t\t//\n\t\tif( segmentLength \u003e= (m_label.width+_maxDistance) ){\n\t\t\t//  How many times?\n\t\t\tint nTimes = segmentLength/(m_label.width+_maxDistance);\n            \n\t\t\t//  At what distance between each other?\n\t\t\tfloat margin = (segmentLength-m_label.width*(float)nTimes)/((float)nTimes+1.0);\n            \n\t\t\t//  Add anchors points for seeds\n\t\t\tfloat seed = margin;\n\t\t\tfor (int i = 0; i \u003c nTimes; i++) {\n\t\t\t\tfloat potentialSeed = offset + seed ;\n\t\t\t\tif( potentialSeed-lastSeed \u003e _minDistance ){\n\t\t\t\t\tlastSeed = potentialSeed;\n\t\t\t\t\t_anchorLine.marks.push_back(lastSeed);\n\t\t\t\t\tseed += m_label.width+margin;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ( segmentLength \u003e= m_label.width+_minDistance){\n\t\t\t//  Only one time\n\t\t\t//\n\t\t\tfloat margin = (segmentLength-m_label.width)*0.5;\n\t\t\tfloat potentialSeed = offset + margin ;\n\t\t\tif( potentialSeed-lastSeed \u003e _minDistance){\n\t\t\t\tlastSeed = potentialSeed;\n\t\t\t\t_anchorLine.marks.push_back(lastSeed);\n\t\t\t}\n\t\t}\n\t}\n```\n\n![01](images/01.gif)\n\n* Fade according to tilt angle\n\n```\n\tfloat angle = glm::dot(glm::normalize( *m_cameraPos - shapes[0].getCentroid()),glm::vec3(0.,0.,1.));\n\tm_alpha = lerpValue(m_alpha,powf( CLAMP(angle,0.01,1.0), 1.15 ),0.1);\n```\n\n![02](images/02.gif)\n\n* Curved path following\n\n```\n\tfloat angle = PI;\n\tglm::vec3 diff = _anchorLine[0]-_anchorLine[_anchorLine.size()-1];\n\tangle = atan2f(-diff.y, diff.x);\n        \n\tif(angle \u003c PI*0.5 \u0026\u0026 angle \u003e -PI*0.5){\n\t\tfor (int i = m_text.length()-1; i \u003e=0 ; i--) {\n\t\t\tglm::vec3 src = _anchorLine.getPositionAt(_offset);\n\t\t\tif(screen.inside(src)){\n\t\t\t\tdouble rot = _anchorLine.getAngleAt(_offset);\n\t\t\t\tglPushMatrix();\n\t\t\t\tglTranslated(src.x, src.y, src.z);\n\t\t\t\tglScalef(1,-1,1);\n\t\t\t\tglRotated(rot*RAD_TO_DEG, 0, 0, -1);\n\t\t\t\tglScaled(-1, -1, 1);\n\t\t\t\tglTranslated(-m_lettersWidth[i], 0, 0);\n\t\t\t\tglTranslatef(0., -m_label.height*0.5,0.);\n\t\t\t\tm_font-\u003edrawString( std::string(1,m_text[i]), m_alpha );\n\t\t\t\tglPopMatrix();\n\t\t\t\t_offset += m_lettersWidth[i];\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (int i = 0; i \u003c m_text.length(); i++) {\n\t\t\tglm::vec3 src = _anchorLine.getPositionAt(_offset);\n\t\t\tif(screen.inside(src)){\n\t\t\t\tdouble rot = _anchorLine.getAngleAt(_offset);\n\t\t\t\tglPushMatrix();\n\t\t\t\tglTranslated(src.x, src.y, src.z);\n\t\t\t\tglScalef(1,-1,1);\n\t\t\t\tglRotated(rot*RAD_TO_DEG, 0, 0, -1);\n\t\t\t\tglTranslatef(0., -m_label.height*0.5,0.);\n\t\t\t\tm_font-\u003edrawString( std::string(1,m_text[i]), m_alpha );\n\t\t\t\tglPopMatrix();\n\t\t\t\t_offset += m_lettersWidth[i];\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n```\n\n![07](images/07.gif)\n\n### Point Labels\n\n* Project top points \n\n```\n\tglm::ivec4 viewport;\n\tglm::mat4x4 mvmatrix, projmatrix;\n\tglGetIntegerv(GL_VIEWPORT, \u0026viewport[0]);\n\tglGetFloatv(GL_MODELVIEW_MATRIX, \u0026mvmatrix[0][0]);\n\tglGetFloatv(GL_PROJECTION_MATRIX, \u0026projmatrix[0][0]);\n\tm_anchorPoint = glm::project(m_centroid+m_offset, mvmatrix, projmatrix, viewport);\n```\n\n![03](images/03.gif)\n\n* Mutual occlusion using depth sort and bounding boxes\n\n```\n\tbool depthSort(const glmFeatureLabelPointRef \u0026_A, const glmFeatureLabelPointRef \u0026_B){\n\t\treturn _A-\u003egetAnchorPoint().z \u003c _B-\u003egetAnchorPoint().z;\n\t}\n\n\t…\n\n\tstd::sort(pointLabels.begin(),pointLabels.end(), depthSort);\n\tfor (int i = 0; i \u003c pointLabels.size(); i++) {\n\t\tif(pointLabels[i]-\u003ebVisible){\n\t\t\tfor (int j = i-1; j \u003e= 0 ; j--) {\n\t\t\t\tif (pointLabels[i]-\u003eisOver( pointLabels[j].get() ) ){\n\t\t\t\t\tpointLabels[i]-\u003ebVisible = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n```\n\n\n![04](images/04.gif)\n\n* Occlude line labels with bounding box\n\n```\n\tdouble rot = _anchorLine.getAngleAt(_offset);\n\tglmRectangle boundingBox = glmPolyline(m_label,angle).getBoundingBox();\n\tboundingBox.translate(_anchorLine.getPositionAt(_offset+m_label.width*0.5));\n\n\tbool bOver = false;\n\tfor (int i = 0; i \u003c pointLabels-\u003esize(); i++ ){\n\t\tif(pointLabels-\u003eat(i)-\u003ebVisible){\n\t\t\tif( boundingBox.intersects(pointLabels-\u003eat(i)-\u003egetLabel(0) ) ){\n\t\t\t\tbOver = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n```\n\n![06](images/06.gif)\n\n* Street level point of view\n![05](images/05.gif)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftangrams%2Fofxvectortile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftangrams%2Fofxvectortile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftangrams%2Fofxvectortile/lists"}