private static Long getApproximateZoomLevel(double[] lengths,
			List<TileMatrix> tileMatrices, double length) {

		Long lengthZoomLevel = null;

		double minLength = lengths[0];
		double maxLength = lengths[lengths.length - 1];

		// Length is zoomed in further than available tiles
		if (length < minLength) {
			double levelsIn = Math.log(length / minLength) / Math.log(.5);
			long zoomAbove = (long) Math.floor(levelsIn);
			long zoomBelow = (long) Math.ceil(levelsIn);
			double lengthAbove = minLength * Math.pow(.5, zoomAbove);
			double lengthBelow = minLength * Math.pow(.5, zoomBelow);
			lengthZoomLevel = tileMatrices.get(tileMatrices.size() - 1)
					.getZoomLevel();
			if (lengthAbove - length <= length - lengthBelow) {
				lengthZoomLevel += zoomAbove;
			} else {
				lengthZoomLevel += zoomBelow;
			}
		}
		// Length is zoomed out further than available tiles
		else if (length > maxLength) {
			double levelsOut = Math.log(length / maxLength) / Math.log(2);
			long zoomAbove = (long) Math.ceil(levelsOut);
			long zoomBelow = (long) Math.floor(levelsOut);
			double lengthAbove = maxLength * Math.pow(2, zoomAbove);
			double lengthBelow = maxLength * Math.pow(2, zoomBelow);
			lengthZoomLevel = tileMatrices.get(0).getZoomLevel();
			if (length - lengthBelow <= lengthAbove - length) {
				lengthZoomLevel -= zoomBelow;
			} else {
				lengthZoomLevel -= zoomAbove;
			}
		}
		// Length is between the available tiles
		else {
			int lengthIndex = Arrays.binarySearch(lengths, length);
			if (lengthIndex < 0) {
				lengthIndex = (lengthIndex + 1) * -1;
			}
			double zoomDistance = Math.log(length / lengths[lengthIndex])
					/ Math.log(.5);
			long zoomLevelAbove = getTileMatrixAtLengthIndex(tileMatrices,
					lengthIndex).getZoomLevel();
			zoomLevelAbove += Math.round(zoomDistance);
			lengthZoomLevel = zoomLevelAbove;
		}

		return lengthZoomLevel;
	}
private static Long getApproximateZoomLevel ( double [ ] lengths , List < TileMatrix > tileMatrices , double length ) { Long lengthZoomLevel = null ; double minLength = lengths [ 0 ] ; double maxLength = lengths [ lengths . length - 1 ] ; if ( length < minLength ) { double levelsIn = Math . log ( length / minLength ) / Math . log ( .5 ) ; long zoomAbove = ( long ) Math . floor ( levelsIn ) ; long zoomBelow = ( long ) Math . ceil ( levelsIn ) ; double lengthAbove = minLength * Math . pow ( .5 , zoomAbove ) ; double lengthBelow = minLength * Math . pow ( .5 , zoomBelow ) ; lengthZoomLevel = tileMatrices . get ( tileMatrices . size ( ) - 1 ) . getZoomLevel ( ) ; if ( lengthAbove - length <= length - lengthBelow ) { lengthZoomLevel += zoomAbove ; } else { lengthZoomLevel += zoomBelow ; } } else if ( length > maxLength ) { double levelsOut = Math . log ( length / maxLength ) / Math . log ( 2 ) ; long zoomAbove = ( long ) Math . ceil ( levelsOut ) ; long zoomBelow = ( long ) Math . floor ( levelsOut ) ; double lengthAbove = maxLength * Math . pow ( 2 , zoomAbove ) ; double lengthBelow = maxLength * Math . pow ( 2 , zoomBelow ) ; lengthZoomLevel = tileMatrices . get ( 0 ) . getZoomLevel ( ) ; if ( length - lengthBelow <= lengthAbove - length ) { lengthZoomLevel -= zoomBelow ; } else { lengthZoomLevel -= zoomAbove ; } } else { int lengthIndex = Arrays . binarySearch ( lengths , length ) ; if ( lengthIndex < 0 ) { lengthIndex = ( lengthIndex + 1 ) * - 1 ; } double zoomDistance = Math . log ( length / lengths [ lengthIndex ] ) / Math . log ( .5 ) ; long zoomLevelAbove = getTileMatrixAtLengthIndex ( tileMatrices , lengthIndex ) . getZoomLevel ( ) ; zoomLevelAbove += Math . round ( zoomDistance ) ; lengthZoomLevel = zoomLevelAbove ; } return lengthZoomLevel ; }
Get the approximate zoom level for length using the factor of 2 rule
between zoom levels

@param lengths
sorted lengths
@param tileMatrices
tile matrices
@param length
length in default units
@return approximate zoom level