示例#1
0
/**
 * Parse the color string, and return the corresponding color-int.
 * Supported formats are:
 * #RRGGBB
 * #AARRGGBB
 */
int parseColor(string colorString) {
	if (colorString[0] == '#') {
		// Use a long to avoid rollovers on #ffXXXXXX
		char** end;
		long color = strtol(colorString.c_str() + 1, NULL, 16);
		if (colorString.size() == 7) {
			// Set the alpha value
			color |= 0x00000000ff000000;
		} else if (colorString.size() != 9) {
			osmand_log_print(LOG_ERROR, "Unknown color %s", colorString.c_str());
		}
		return (int) color;
	}
	osmand_log_print(LOG_ERROR, "Unknown color %s", colorString.c_str());
	return -1;
}
示例#2
0
RenderingRule::RenderingRule(map<string, string>& attrs, RenderingRulesStorage* storage) {
	properties.reserve(attrs.size());
	intProperties.assign(attrs.size(), -1);
	map<string, string>::iterator it = attrs.begin();
	int i = 0;
	for (; it != attrs.end(); it++) {
		RenderingRuleProperty* property = storage->PROPS.getProperty(it->first.c_str());
		if (property == NULL) {
			osmand_log_print(LOG_ERROR, "Property %s was not found in registry", it->first.c_str());
			return ;
		}
		properties.push_back(property);

		if (property->isString()) {
			intProperties[i] = storage->getDictionaryValue(it->second);
		} else if (property->isFloat()) {
			if (floatProperties.size() == 0) {
				// lazy creates
				floatProperties.assign(attrs.size(), - 1);
			}
			floatProperties[i] = property->parseFloatValue(it->second);
		} else {
			intProperties[i] = property->parseIntValue(it->second);
		}
		i++;
	}
}
示例#3
0
void RenderingRulesStorage::parseRulesFromXmlInputStream(const char* filename, RenderingRulesStorageResolver* resolver) {
	XML_Parser parser = XML_ParserCreate(NULL);
	RenderingRulesHandler* handler = new RenderingRulesHandler(resolver, this);
	XML_SetUserData(parser, handler);
	XML_SetElementHandler(parser, RenderingRulesHandler::startElementHandler, RenderingRulesHandler::endElementHandler);
	FILE *file = fopen(filename, "r");
	if (file == NULL) {
		osmand_log_print(LOG_ERROR, "File can not be open %s", filename);
		return;
	}
	char buffer[512];
	bool done = false;
	while (!done) {
		fgets(buffer, sizeof(buffer), file);
		int len = strlen(buffer);
		if (feof(file) != 0) {
			done = true;
		}
		if (XML_Parse(parser, buffer, len, done) == XML_STATUS_ERROR) {
			return;
		}
	}

	RenderingRulesStorage* depends = handler->getDependsStorage();
	if (depends != NULL) {
		// merge results
		// dictionary and props are already merged
		map<std::string,  RenderingRule*>::iterator it = depends->renderingAttributes.begin();
		for(;it != depends->renderingAttributes.end(); it++) {
			map<std::string,  RenderingRule*>::iterator o = renderingAttributes.find(it->first);
			if (o != renderingAttributes.end()) {
				std::vector<RenderingRule*>::iterator list = it->second->ifElseChildren.begin();
				for (;list != it->second->ifElseChildren.end(); list++) {
					o->second->ifElseChildren.push_back(*list);
				}
			} else {
				renderingAttributes[it->first] = it->second;
			}
		}

		for (int i = 0; i < SIZE_STATES; i++) {
			if (depends->tagValueGlobalRules[i].empty()) {
				continue;
			}
			HMAP::hash_map<int, RenderingRule*>::iterator it = depends->tagValueGlobalRules[i].begin();
			for (; it != depends->tagValueGlobalRules[i].end(); it++) {
				HMAP::hash_map<int, RenderingRule*>::iterator o = tagValueGlobalRules[i].find(it->first);
				RenderingRule* toInsert = it->second;
				if (o != tagValueGlobalRules[i].end()) {
					toInsert = createTagValueRootWrapperRule(it->first, o->second);
					toInsert->ifElseChildren.push_back(it->second);
				}
				tagValueGlobalRules[i][it->first] = toInsert;
			}
		}

	}
}
示例#4
0
void RenderingRulesStorage::registerGlobalRule(RenderingRule* rr, int state) {
	int tag = rr->getIntPropertyValue(this->PROPS.R_TAG->attrName);
	if (tag == -1) {
		osmand_log_print(LOG_ERROR, "Attribute tag should be specified for root filter ");
	}
	int value = rr->getIntPropertyValue(this->PROPS.R_VALUE->attrName);
	if (value == -1) {
		// attrsMap.toString()
		osmand_log_print(LOG_ERROR, "Attribute tag should be specified for root filter ");
	}
	int key = (tag << SHIFT_TAG_VAL) + value;
	RenderingRule* toInsert = rr;
	RenderingRule* previous = tagValueGlobalRules[state][key];

	if (previous != NULL) {
		// all root rules should have at least tag/value
		toInsert = createTagValueRootWrapperRule(key, previous);
		toInsert->ifElseChildren.push_back(rr);
	}
	tagValueGlobalRules[state][key] = toInsert;
}
示例#5
0
int main(int argc, char **argv) {
	if (argc <= 1) {
		// 1. Test Rendering rule storage
//		testRenderingRuleStorage("/home/victor/projects/OsmAnd/git/DataExtractionOSM/src/net/osmand/render/",
//				"test_depends.render.xml"
//				"default.render.xml"
//				);
		// 2. Test simple rendering
		printUsage("");
		return 1;
	}
	const char* f = argv[1];
	if (f[0] == '-') {
		// command
		if (f[1]=='v') {
			if (argc < 2) {
				printUsage("Missing file parameter");
			} else {
				VerboseInfo* vinfo = new VerboseInfo(argc, argv);
				printFileInformation(argv[argc -1], vinfo);
			}
		} else if (f[1]=='r') {
			if (argc < 2) {
				printUsage("Missing file parameter");
			} else {
				RenderingInfo* info = new RenderingInfo(argc, argv);
				char s[100];
				for (int i = 1; i != argc; ++i) {
					if (sscanf(argv[i], "-renderingInputFile=%s", s)) {
						BinaryMapFile* mf = initBinaryMapFile(s);
						osmand_log_print(LOG_INFO, "Init %d (success) binary map file %s.", mf->version,
								mf->inputName.c_str());
					}
				}
				runSimpleRendering(info->renderingFileName, info->imagesFileName, info);
				for (int i = 1; i != argc; ++i) {
					if (sscanf(argv[i], "-renderingInputFile=%s", s)) {
						closeBinaryMapFile(s);
					}
				}
				delete info;
			}
		} else {
			printUsage("Unknown command");
		}
	} else {
		printFileInformation(f, NULL);
	}

	SkGraphics::PurgeFontCache();
	purgeCachedBitmaps();
}
示例#6
0
SkBitmap* RenderingContext::getCachedBitmap(const std::string& bitmapResource) {
    if (defaultIconsDir.size() > 0) {
        string fl = string(defaultIconsDir + "h_" + bitmapResource + ".png");
        FILE* f = fopen(fl.c_str(), "r");
        if (f == NULL) {
            fl = string(defaultIconsDir + "g_" + bitmapResource + ".png");
            f = fopen(fl.c_str(), "r");
        }
        if (f != NULL) {
            fclose(f);
            osmand_log_print(LOG_INFO, "Open file %s", fl.c_str());
            SkBitmap* bmp = new SkBitmap();
            if (!SkImageDecoder::DecodeFile(fl.c_str(), bmp)) {
                return NULL;
            }
            return bmp;
        }
    }
    return NULL;
}
示例#7
0
void runSimpleRendering( string renderingFileName, string resourceDir, RenderingInfo* info) {
	SkColor defaultMapColor = SK_ColorLTGRAY;

	if (info->width > 10000 || info->height > 10000) {
		osmand_log_print(LOG_ERROR, "We don't rendering images more than 10000x10000 ");
		return;
	}

	osmand_log_print(LOG_INFO, "Rendering info bounds(%d, %d, %d, %d) zoom(%d), width/height(%d/%d) tilewidth/tileheight(%d/%d) fileName(%s)",
			info->left, info->top, info->right, info->bottom, info->zoom, info->width, info->height, info->tileWX, info->tileHY, info->tileFileName.c_str());
	RenderingRulesStorage* st = new RenderingRulesStorage(renderingFileName.c_str());
	st->parseRulesFromXmlInputStream(renderingFileName.c_str(), NULL);
	RenderingRuleSearchRequest* searchRequest = new RenderingRuleSearchRequest(st);
	ResultPublisher* publisher = new ResultPublisher();
	SearchQuery q(floor(info->left), floor(info->right), ceil(info->top), ceil(info->bottom), searchRequest, publisher);
	q.zoom = info->zoom;

	ResultPublisher* res = searchObjectsForRendering(&q, true, "Nothing found");
	osmand_log_print(LOG_INFO, "Found %d objects", res->result.size());

	SkBitmap* bitmap = new SkBitmap();
	bitmap->setConfig(SkBitmap::kRGB_565_Config, info->width, info->height);

	size_t bitmapDataSize = bitmap->getSize();
	void* bitmapData = malloc(bitmapDataSize);
	bitmap->setPixels(bitmapData);

	osmand_log_print(LOG_INFO, "Initializing rendering style and rendering context");
	ElapsedTimer initObjects;
	initObjects.start();

	RenderingContext rc;
	rc.setDefaultIconsDir(resourceDir);
	searchRequest->clearState();
	searchRequest->setIntFilter(st->PROPS.R_MINZOOM, info->zoom);
	if (searchRequest->searchRenderingAttribute(A_DEFAULT_COLOR)) {
		defaultMapColor = searchRequest->getIntPropertyValue(searchRequest->props()->R_ATTR_COLOR_VALUE);
	}
	searchRequest->clearState();
	searchRequest->setIntFilter(st->PROPS.R_MINZOOM, info->zoom);
	if (searchRequest->searchRenderingAttribute(A_SHADOW_RENDERING)) {
		rc.setShadowRenderingMode(searchRequest->getIntPropertyValue(searchRequest->props()->R_ATTR_INT_VALUE));
		//rc.setShadowRenderingColor(searchRequest->getIntPropertyValue(searchRequest->props()->R_SHADOW_COLOR));
	}
	rc.setLocation(
			((double)info->left)/getPowZoom(31-info->zoom),
			((double)info->top)/getPowZoom(31-info->zoom)
			);
	rc.setDimension(info->width, info->height);
	rc.setZoom(info->zoom);
	rc.setRotate(0);
	rc.setDensityScale(1);
	osmand_log_print(LOG_INFO, "Rendering image");
	initObjects.pause();
	SkCanvas* canvas = new SkCanvas(*bitmap);
	canvas->drawColor(defaultMapColor);
	doRendering(res->result, canvas, searchRequest, &rc);
	osmand_log_print(LOG_INFO, "End Rendering image");
	osmand_log_print(LOG_INFO, "Native ok (init %d, rendering %d) ", initObjects.getElapsedTime(),
			rc.nativeOperations.getElapsedTime());
	SkImageEncoder* enc = SkImageEncoder::Create(SkImageEncoder::kPNG_Type);
	if (enc != NULL && !enc->encodeFile(info->tileFileName.c_str(), *bitmap, 100)) {
		osmand_log_print(LOG_ERROR, "FAIL to save tile to %s", info->tileFileName.c_str());
	} else {
		osmand_log_print(LOG_INFO, "Tile successfully saved to %s", info->tileFileName.c_str());
	}
	delete enc;
	delete publisher;
	delete searchRequest;
	delete st;
	delete canvas;
	delete bitmap;
	free(bitmapData);
	return;
}
示例#8
0
	static void startElementHandler(void *data, const char *tag, const char **atts) {
		RenderingRulesHandler* t = (RenderingRulesHandler*) data;
		int len = strlen(tag);
		string name(tag);
		if (len == 6 && "filter" == name) {
			map<string, string> attrsMap;
			if (t->st.size() > 0 && t->st.top().isGroup()) {
				attrsMap.insert(t->st.top().groupAttributes.begin(), t->st.top().groupAttributes.end());
			}
			parseAttributes(atts, attrsMap);
			RenderingRule* renderingRule = new RenderingRule(attrsMap,t->storage);
			if (t->st.size() > 0 && t->st.top().isGroup()) {
				t->st.top().children.push_back(renderingRule);
			} else if (t->st.size() > 0 && !t->st.top().isGroup()) {
				RenderingRule* parent = t->st.top().singleRule;
				t->st.top().singleRule->ifElseChildren.push_back(renderingRule);
			} else {
				t->storage->registerGlobalRule(renderingRule, t->state);

			}
			GroupRules gr(renderingRule);
			t->st.push(gr);
		} else if ("groupFilter" == name) { //$NON-NLS-1$
			map<string, string> attrsMap;
			parseAttributes(atts, attrsMap);
			RenderingRule* renderingRule = new RenderingRule(attrsMap,t->storage);
			if (t->st.size() > 0 && t->st.top().isGroup()) {
				GroupRules parent = ((GroupRules) t->st.top());
				t->st.top().addGroupFilter(renderingRule);
			} else if (t->st.size() > 0 && !t->st.top().isGroup()) {
				t->st.top().singleRule->ifChildren.push_back(renderingRule);
			} else {
				osmand_log_print(LOG_ERROR, "Group filter without parent");
			}
			t->st.push(GroupRules(renderingRule));
		} else if ("group" == name) { //$NON-NLS-1$
			GroupRules groupRules;
			if (t->st.size() > 0 && t->st.top().isGroup()) {
				groupRules.groupAttributes.insert(t->st.top().groupAttributes.begin(), t->st.top().groupAttributes.end());
			}
			parseAttributes(atts, groupRules.groupAttributes);
			t->st.push(groupRules);
		} else if ("order" == name) { //$NON-NLS-1$
			t->state = RenderingRulesStorage::ORDER_RULES;
		} else if ("text" == name) { //$NON-NLS-1$
			t->state = RenderingRulesStorage::TEXT_RULES;
		} else if ("point" == name) { //$NON-NLS-1$
			t->state = RenderingRulesStorage::POINT_RULES;
		} else if ("line" == name) { //$NON-NLS-1$
			t->state = RenderingRulesStorage::LINE_RULES;
		} else if ("polygon" == name) { //$NON-NLS-1$
			t->state = RenderingRulesStorage::POLYGON_RULES;
		} else if ("renderingAttribute" == name) { //$NON-NLS-1$
			map<string, string> attrsMap;
			parseAttributes(atts, attrsMap);
			string attr = attrsMap["name"];
			map<string, string> empty;
			RenderingRule* root = new RenderingRule(empty,t->storage);
			t->storage->renderingAttributes[attr] = root;
			t->st.push(GroupRules(root));
		} else if ("renderingProperty" == name) {
			map<string, string> attrsMap;
			parseAttributes(atts, attrsMap);
			string attr = attrsMap["attr"];
			RenderingRuleProperty* prop;
			string type = attrsMap["type"];
			if ("boolean" == type) {
				prop = RenderingRuleProperty::createInputBooleanProperty(attr);
			} else if ("string" == type) {
				prop = RenderingRuleProperty::createInputStringProperty(attr);
			} else {
				prop = RenderingRuleProperty::createInputIntProperty(attr);
			}
			prop->description = attrsMap["description"];
			prop->name = attrsMap["name"];
			string possible = attrsMap["possibleValues"];
			if (possible != "") {
				int n;
				int p = 0;
				while ((n = possible.find(',', p)) != string::npos) {
					prop->possibleValues.push_back(possible.substr(p, n));
					p = n + 1;
				}
				prop->possibleValues.push_back(possible.substr(p));
			}
			t->storage->PROPS.registerRule(prop);
		} else if ("renderingStyle" == name) {
			map<string, string> attrsMap;
			parseAttributes(atts, attrsMap);
			string depends = attrsMap["depends"];
			if (depends.size() > 0 && t->resolver != NULL) {
				t->dependsStorage = t->resolver->resolve(depends, t->resolver);
			}
			if (t->dependsStorage != NULL) {
				// copy dictionary
				t->storage->dictionary = t->dependsStorage->dictionary;
				t->storage->dictionaryMap = t->dependsStorage->dictionaryMap;
				t->storage->PROPS.merge(t->dependsStorage->PROPS);
			} else if (depends.size() > 0) {
				osmand_log_print(LOG_ERROR, "!Dependent rendering style was not resolved : %s", depends.c_str());
			}
			//renderingName = attrsMap["name"];
		} else {
			osmand_log_print(LOG_WARN, "Unknown tag : %s", name.c_str());
		}

	}
示例#9
0
// returns true if coastlines were added!
bool processCoastlines(std::vector<MapDataObject*>&  coastLines, int leftX, int rightX, int bottomY, int topY, int zoom,
		bool showIfThereIncompleted, bool addDebugIncompleted, std::vector<MapDataObject*>& res) {
	std::vector<coordinates> completedRings;
	std::vector<coordinates > uncompletedRings;
	std::vector<MapDataObject*>::iterator val = coastLines.begin();
	long long dbId = 0;
	for (; val != coastLines.end(); val++) {
		MapDataObject* o = *val;
		int len = o->points.size();
		if (len < 2) {
			continue;
		}
		dbId = o->id >> 1;
		coordinates* cs = new coordinates();
		int px = o->points.at(0).first;
		int py = o->points.at(0).second;
		int x = px;
		int y = py;
		bool pinside = leftX <= x && x <= rightX && y >= topY && y <= bottomY;
		if (pinside) {
			cs->push_back(int_pair(x, y));
		}
		for (int i = 1; i < len; i++) {
			x = o->points.at(i).first;
			y = o->points.at(i).second;
			bool inside = leftX <= x && x <= rightX && y >= topY && y <= bottomY;
			bool lineEnded = calculateLineCoordinates(inside, x, y, pinside, px, py, leftX, rightX, bottomY, topY, *cs);
			if (lineEnded) {
				combineMultipolygonLine(completedRings, uncompletedRings, *cs);
				// create new line if it goes outside
				cs = new coordinates();
			}
			px = x;
			py = y;
			pinside = inside;
		}
		combineMultipolygonLine(completedRings, uncompletedRings, *cs);
	}
	if (completedRings.size() == 0 && uncompletedRings.size() == 0) {
		return false;
	}
	if (uncompletedRings.size() > 0) {
		unifyIncompletedRings(uncompletedRings, completedRings, leftX, rightX, bottomY, topY, dbId, zoom);
	}
	if (addDebugIncompleted) {
		// draw uncompleted for debug purpose
		for (int i = 0; i < uncompletedRings.size(); i++) {
			MapDataObject* o = new MapDataObject();
			o->points = uncompletedRings[i];
			o->types.push_back(tag_value("natural", "coastline_broken"));
			res.push_back(o);
		}

	}
	if (!showIfThereIncompleted && uncompletedRings.size() > 0) {
		return false;
	}
	bool clockwiseFound = false;
	for (int i = 0; i < completedRings.size(); i++) {
		bool clockwise = isClockwiseWay(completedRings[i]);
		clockwiseFound = clockwiseFound || clockwise;
		MapDataObject* o = new MapDataObject();
		o->points = completedRings[i];
		if (clockwise) {
			o->types.push_back(tag_value("natural", "coastline"));
		} else {
			o->types.push_back(tag_value("natural", "land"));
		}
		o->id = dbId;
		o->area = true;
		res.push_back(o);
	}

	if (!clockwiseFound && uncompletedRings.size() == 0) {
		// add complete water tile
		MapDataObject* o = new MapDataObject();
		o->points.push_back(int_pair(leftX, topY));
		o->points.push_back(int_pair(rightX, topY));
		o->points.push_back(int_pair(rightX, bottomY));
		o->points.push_back(int_pair(leftX, bottomY));
		o->points.push_back(int_pair(leftX, topY));
		o->id = dbId;
		o->types.push_back(tag_value("natural", "coastline"));
		osmand_log_print(LOG_ERROR,  "!!! Isolated islands !!!");
		res.push_back(o);

	}
	return true;
}
示例#10
0
void unifyIncompletedRings(std::vector<std::vector<int_pair> >& toProccess, std::vector<std::vector<int_pair> >& completedRings,
		int leftX, int rightX, int bottomY, int topY, long dbId, int zoom) {
	std::set<int> nonvisitedRings;
	std::vector<coordinates > incompletedRings(toProccess);
	toProccess.clear();
	std::vector<coordinates >::iterator ir = incompletedRings.begin();
	int j = 0;
	for (j = 0; ir != incompletedRings.end(); ir++,j++) {
		int x = ir->at(0).first;
		int y = ir->at(0).second;
		int sx = ir->at(ir->size() - 1).first;
		int sy = ir->at(ir->size() - 1).second;
		bool st = y == topY || x == rightX || y == bottomY || x == leftX;
		bool end = sy == topY || sx == rightX || sy == bottomY || sx == leftX;
		// something goes wrong
		// These exceptions are used to check logic about processing multipolygons
		// However this situation could happen because of broken multipolygons (so it should data causes app error)
		// that's why these exceptions could be replaced with return; statement.
		if (!end || !st) {
			osmand_log_print(LOG_ERROR, "Error processing multipolygon");
			toProccess.push_back(*ir);
		} else {
			nonvisitedRings.insert(j);
		}
	}
	ir = incompletedRings.begin();
	for (j = 0; ir != incompletedRings.end(); ir++, j++) {
		if (nonvisitedRings.find(j) == nonvisitedRings.end()) {
			continue;
		}
		int x = ir->at(ir->size() - 1).first;
		int y = ir->at(ir->size() - 1).second;
		// 31 - (zoom + 8)
		const int EVAL_DELTA = 6 << (23 - zoom);
		const int UNDEFINED_MIN_DIFF = -1 - EVAL_DELTA;
		while (true) {
			int st = 0; // st already checked to be one of the four
			if (y == topY) {
				st = 0;
			} else if (x == rightX) {
				st = 1;
			} else if (y == bottomY) {
				st = 2;
			} else if (x == leftX) {
				st = 3;
			}
			int nextRingIndex = -1;
			// BEGIN go clockwise around rectangle
			for (int h = st; h < st + 4; h++) {

				// BEGIN find closest nonvisited start (including current)
				int mindiff = UNDEFINED_MIN_DIFF;
				std::vector<std::vector<int_pair> >::iterator cni = incompletedRings.begin();
				int cnik = 0;
				for (;cni != incompletedRings.end(); cni++, cnik ++) {
					if (nonvisitedRings.find(cnik) == nonvisitedRings.end()) {
						continue;
					}
					int csx = cni->at(0).first;
					int csy = cni->at(0).second;
					if (h % 4 == 0) {
						// top
						if (csy == topY && csx >= safelyAddDelta(x, -EVAL_DELTA)) {
							if (mindiff == UNDEFINED_MIN_DIFF || (csx - x) <= mindiff) {
								mindiff = (csx - x);
								nextRingIndex = cnik;
							}
						}
					} else if (h % 4 == 1) {
						// right
						if (csx == rightX && csy >= safelyAddDelta(y, -EVAL_DELTA)) {
							if (mindiff == UNDEFINED_MIN_DIFF || (csy - y) <= mindiff) {
								mindiff = (csy - y);
								nextRingIndex = cnik;
							}
						}
					} else if (h % 4 == 2) {
						// bottom
						if (csy == bottomY && csx <= safelyAddDelta(x, EVAL_DELTA)) {
							if (mindiff == UNDEFINED_MIN_DIFF || (x - csx) <= mindiff) {
								mindiff = (x - csx);
								nextRingIndex = cnik;
							}
						}
					} else if (h % 4 == 3) {
						// left
						if (csx == leftX && csy <= safelyAddDelta(y, EVAL_DELTA)) {
							if (mindiff == UNDEFINED_MIN_DIFF || (y - csy) <= mindiff) {
								mindiff = (y - csy);
								nextRingIndex = cnik;
							}
						}
					}
				} // END find closest start (including current)

				// we found start point
				if (mindiff != UNDEFINED_MIN_DIFF) {
					break;
				} else {
					if (h % 4 == 0) {
						// top
						y = topY;
						x = rightX;
					} else if (h % 4 == 1) {
						// right
						y = bottomY;
						x = rightX;
					} else if (h % 4 == 2) {
						// bottom
						y = bottomY;
						x = leftX;
					} else if (h % 4 == 3) {
						y = topY;
						x = leftX;
					}
					ir->push_back(int_pair(x, y));
				}

			} // END go clockwise around rectangle
			if (nextRingIndex == -1) {
				// it is impossible (current start should always be found)
			} else if (nextRingIndex == j) {
				ir->push_back(ir->at(0));
				nonvisitedRings.erase(j);
				break;
			} else {
				std::vector<int_pair> p = incompletedRings.at(nextRingIndex);
				ir->insert(ir->end(), p.begin(), p.end());
				nonvisitedRings.erase(nextRingIndex);
				// get last point and start again going clockwise
				x = ir->at(ir->size() - 1).first;
				y = ir->at(ir->size() - 1).second;
			}
		}

		completedRings.push_back(*ir);
	}

}