@SuppressWarnings("unchecked")
    public static <ViewType extends View<? extends ViewModelType>, ViewModelType extends ViewModel> ViewModelType createViewModel(
            ViewType view) {
        final Class<?> viewModelType = TypeResolver.resolveRawArgument(View.class, view.getClass());
        if (viewModelType == ViewModel.class) {
            return null;
        }
        if (TypeResolver.Unknown.class == viewModelType) {
            return null;
        }
        return (ViewModelType) DependencyInjector.getInstance().getInstanceOf(viewModelType);
    }

@ SuppressWarnings ( "unchecked" ) public static < ViewType extends View < ? extends ViewModelType > , ViewModelType extends ViewModel > ViewModelType createViewModel ( ViewType view ) { final Class < ? > viewModelType = TypeResolver . resolveRawArgument ( View . class , view . getClass ( ) ) ; if ( viewModelType == ViewModel . class ) { return null ; } if ( TypeResolver . Unknown . class == viewModelType ) { return null ; } return ( ViewModelType ) DependencyInjector . getInstance ( ) . getInstanceOf ( viewModelType ) ; }

Creates a viewModel instance for a View type. The type of the view is
determined by the given view instance.

For the creation of the viewModel the {@link DependencyInjector} is used.

@param view
the view instance that is used to find out the type of the
ViewModel
@param <ViewType>
the generic view type
@param <ViewModelType>
the generic viewModel type
@return the viewModel instance or null if the viewModel type
can't be found or the viewModel can't be created.

Creates a viewModel instance for a View type . The type of the view is determined by the given view instance .


void loadCountries() {
		InputStream iso3166Resource = this.getClass().getResourceAsStream(ISO_3166_LOCATION);
		if (iso3166Resource == null) {
			throw new IllegalStateException("Can't find the list of countries! Expected location was:"
					+ ISO_3166_LOCATION);
		}

		XmlConverter<Country> countryConverter = new XmlConverter<>("iso_3166_entry", Country.class);

		try {
			DataReader<Country> dataSource = new InputStreamSource<>( iso3166Resource, countryConverter );

			ListDataProvider<Country> listDataProvider = new ListDataProvider<>(dataSource);

			listDataProvider.setResultObservableList(countries);

			Worker<ObservableList<Country>> worker = listDataProvider.retrieve();
			// when the countries are loaded we start the loading of the subdivisions.
			worker.stateProperty().addListener(obs -> {
				if (worker.getState() == Worker.State.SUCCEEDED) {
					loadSubdivisions();
				}
			});
		} catch (IOException e) {
			LOG.error("A problem was detected while loading the XML file with the available countries.", e);
		}
	}

Load all countries from the XML file source with DataFX.

Load all countries from the XML file source with DataFX .


void loadSubdivisions() {

		InputStream iso3166_2Resource = this.getClass().getResourceAsStream(ISO_3166_2_LOCATION);

		if (iso3166_2Resource == null) {
			throw new IllegalStateException("Can't find the list of subdivisions! Expected location was:"
					+ ISO_3166_2_LOCATION);
		}

		XmlConverter<ISO3166_2_CountryEntity> converter = new XmlConverter<>("iso_3166_country",
				ISO3166_2_CountryEntity.class);

		ObservableList<ISO3166_2_CountryEntity> subdivisionsEntities = FXCollections.observableArrayList();

		try {

			DataReader<ISO3166_2_CountryEntity> dataSource =
					new InputStreamSource<>( iso3166_2Resource, converter );

			ListDataProvider<ISO3166_2_CountryEntity> listDataProvider = new ListDataProvider<>(dataSource);

			listDataProvider.setResultObservableList(subdivisionsEntities);

			Worker<ObservableList<ISO3166_2_CountryEntity>> worker = listDataProvider.retrieve();
			worker.stateProperty().addListener(obs -> {
				if (worker.getState() == Worker.State.SUCCEEDED) {

					subdivisionsEntities.forEach(entity -> {
						if (entity.subsets != null && !entity.subsets.isEmpty()) {

							Country country = findCountryByCode(entity.code);

							if (!countryCodeSubdivisionMap.containsKey(country)) {
								countryCodeSubdivisionMap.put(country, new ArrayList<>());
							}

							List<Subdivision> subdivisionList = countryCodeSubdivisionMap.get(country);

							entity.subsets.forEach(subset -> {
								subset.entryList.forEach(entry -> {
									subdivisionList.add(new Subdivision(entry.name, entry.code, country));
								});
							});

							String subdivisionName = entity.subsets.stream()
									.map(subset -> subset.subdivisionType)
									.collect(Collectors.joining("/"));

							countryCodeSubdivisionNameMap.put(country, subdivisionName);
						}
					});

					inProgress.set(false);
				}
			});
		} catch (IOException e) {
			LOG.error("A problem was detected while loading the XML file with the available subdivisions.", e);
		}

	}

Load all subdivisions from the XML file source with DataFX.

Load all subdivisions from the XML file source with DataFX .


public StringProperty field(StringGetter<M> getter, StringSetter<M> setter) {
		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new));
	}

public StringProperty field ( StringGetter < M > getter , StringSetter < M > setter ) { return add ( new BeanPropertyField <> ( this :: propertyWasChanged , getter , setter , SimpleStringProperty :: new ) ) ; }

Add a new field of type String to this instance of the wrapper. This method is used for model elements that are
following the normal Java-Beans-standard i.e. the model fields are only available via getter and setter methods
and not as JavaFX Properties.

Example:

ModelWrapper{@code<Person>} personWrapper = new ModelWrapper{@code<>}();

StringProperty wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value)
-> person.setName(value));

// or with a method reference
StringProperty wrappedNameProperty = personWrapper.field(Person::getName, Person::setName);

@param getter
a function that returns the current value of the field for a given model element. Typically you will
use a method reference to the getter method of the model element.
@param setter
a function that sets the given value to the given model element. Typically you will use a method
reference to the setter method of the model element.

@return The wrapped property instance.

Add a new field of type String to this instance of the wrapper . This method is used for model elements that are following the normal Java - Beans - standard i . e . the model fields are only available via getter and setter methods and not as JavaFX Properties .


public StringProperty immutableField(StringGetter<M> getter, StringImmutableSetter<M> immutableSetter){
		return addImmutable(new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, SimpleStringProperty::new));
	}

public StringProperty immutableField ( StringGetter < M > getter , StringImmutableSetter < M > immutableSetter ) { return addImmutable ( new ImmutableBeanPropertyField <> ( this :: propertyWasChanged , getter , immutableSetter , SimpleStringProperty :: new ) ) ; }

Add a new immutable field of type String to this instance of the wrapper. This method is used for immutable
model elements that have getters to get values for it's fields but not setters.
Instead, immutables have methods that take a new value for a field and return a new cloned instance of the
model element with only this field updated to the new value. The old model instance isn't changed.

Example:

ModelWrapper{@code<Person>} personWrapper = new ModelWrapper{@code<>}();

StringProperty wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value)
-> {
Person clone = person.withName(value);
return clone;
});

// or with a method reference
StringProperty wrappedNameProperty = personWrapper.field(Person::getName, Person::withName);

@param getter
a function that returns the current value of the field for a given model element. Typically you will
use a method reference to the getter method of the model element.
@param immutableSetter
a function that returns a clone of this the given model element that has the given value set. Typically you will use a method
reference to the immutable setter method of the model element.

@return The wrapped property instance.

Add a new immutable field of type String to this instance of the wrapper . This method is used for immutable model elements that have getters to get values for it s fields but not setters . Instead immutables have methods that take a new value for a field and return a new cloned instance of the model element with only this field updated to the new value . The old model instance isn t changed .


public static <ViewType extends JavaView<? extends ViewModelType>, ViewModelType extends ViewModel> JavaViewStep<ViewType, ViewModelType> javaView(
            Class<? extends ViewType> viewType) {
        return new JavaViewStep<>(viewType);
    }

public static < ViewType extends JavaView < ? extends ViewModelType > , ViewModelType extends ViewModel > JavaViewStep < ViewType , ViewModelType > javaView ( Class < ? extends ViewType > viewType ) { return new JavaViewStep <> ( viewType ) ; }

This method is the entry point of the Fluent API to load a java based
view.

@param viewType
the type of the view that should be loaded.
@param <ViewType>
the type of the View that should be loaded. This type has to
implement {@link de.saxsys.mvvmfx.JavaView}.
@param <ViewModelType>
the type of the ViewModel. This type has to implement
{@link de.saxsys.mvvmfx.ViewModel}.

@return a builder step that can be further configured and then load the
actual view.

This method is the entry point of the Fluent API to load a java based view .


public static <ViewType extends FxmlView<? extends ViewModelType>, ViewModelType extends ViewModel> FxmlViewStep<ViewType, ViewModelType> fxmlView(
            Class<? extends ViewType> viewType) {
        return new FxmlViewStep<>(viewType);
    }

public static < ViewType extends FxmlView < ? extends ViewModelType > , ViewModelType extends ViewModel > FxmlViewStep < ViewType , ViewModelType > fxmlView ( Class < ? extends ViewType > viewType ) { return new FxmlViewStep <> ( viewType ) ; }

This method is the entry point of the Fluent API to load a fxml based
View.

@param viewType
the type of the view that should be loaded.
@param <ViewType>
the generic type of the View that should be loaded. This type
has to implement {@link de.saxsys.mvvmfx.FxmlView}.
@param <ViewModelType>
the generic type of the ViewModel. This type has to implement
{@link de.saxsys.mvvmfx.ViewModel}.

@return a builder step that can be further configured and then load the
actual view.

This method is the entry point of the Fluent API to load a fxml based View .


private <T, U> void clearMap(Map<T, Set<U>> map, BiConsumer<T, U> consumer) {
		for (T observable : map.keySet()) {
			for (U listener : map.get(observable)) {
				consumer.accept(observable, listener);
			}
		}
		map.clear();
	}

private < T , U > void clearMap ( Map < T , Set < U > > map , BiConsumer < T , U > consumer ) { for ( T observable : map . keySet ( ) ) { for ( U listener : map . get ( observable ) ) { consumer . accept ( observable , listener ) ; } } map . clear ( ) ; }

This method is used to clear the given map. To do this you need to implement a BiConsumer that calls the specific
method to remove a listener from an observable.

This needs to be done as there is no common interface with a remove method that all types of observables are
implementing. Therefore the method call to the specific removeListener method needs to be done in an extra
function.

@param map
the multimap that contains the observables and listeners.
@param consumer
a function that calls the specific remove method for the given types.

This method is used to clear the given map . To do this you need to implement a BiConsumer that calls the specific method to remove a listener from an observable .


private void createIndexEvents() {
		selectionModel
                .selectedIndexProperty()
				.addListener((bean, oldVal, newVal) -> {
                    int index = newVal.intValue();
                    ListType item = index == -1 ? null : modelListProperty()
                            .get(index);
                    selectedItem.set(item);
                });

		selectedItem.addListener((observable, oldVal, newVal) -> {

            // Item null
            if (newVal == null) {
                selectionModel.select(-1);
                selectedItem.set(null);

            } else {
                int index = modelListProperty().get().indexOf(newVal);
                // Item not found
                if (index != -1) {
                    selectionModel.select(index);
                } else {
                    // If item not found - Rollback
                    selectedItem.set(oldVal);
                }
            }

        });
	}

private
void
createIndexEvents
(
)
{
selectionModel
.
selectedIndexProperty
(
)
.
addListener
(
(
bean
,
oldVal
,
newVal
)
->
{
int
index
=
newVal
.
intValue
(
)
;
ListType
item
=
index
==
1
:
modelListProperty
(
)
.
get
(
index
)
;
selectedItem
.
set
(
item
)
;
}
)
;
selectedItem
.
addListener
(
(
observable
,
oldVal
,
newVal
)
->
{
// Item null
if
(
newVal
==
)
{
selectionModel
.
select
(
1
)
;
selectedItem
.
set
(
)
;
}
else
{
int
index
=
modelListProperty
(
)
.
get
(
)
.
indexOf
(
newVal
)
;
// Item not found
if
(
index
!=
1
)
{
selectionModel
.
select
(
index
)
;
}
else
{
// If item not found - Rollback
selectedItem
.
set
(
oldVal
)
;
}
}
}
)
;
}

When the selected item changed we want to set the index property too


public static <T> T accessMember(final AccessibleObject member, final Callable<T> callable, String errorMessage) {
		if (callable == null) {
			return null;
		}
		return AccessController.doPrivileged((PrivilegedAction<T>) () -> {
			boolean wasAccessible = member.isAccessible();
			try {
				member.setAccessible(true);
				return callable.call();
			} catch (Exception exception) {
				throw new IllegalStateException(errorMessage, exception);
			} finally {
				member.setAccessible(wasAccessible);
			}
		});
	}

public static < T > T accessMember ( final AccessibleObject member , final Callable < T > callable , String errorMessage ) { if ( callable == null ) { return null ; } return AccessController . doPrivileged ( ( PrivilegedAction < T > ) ( ) -> { boolean wasAccessible = member . isAccessible ( ) ; try { member . setAccessible ( true ) ; return callable . call ( ) ; } catch ( Exception exception ) { throw new IllegalStateException ( errorMessage , exception ) ; } finally { member . setAccessible ( wasAccessible ) ; } } ) ; }

Helper method to execute a callback on a given member. This method encapsulates the error handling logic and the
handling of accessibility of the member.

After the callback is executed the accessibility of the member will be reset to the originally state.

@param member
the member that is made accessible to run the callback
@param callable
the callback that will be executed.
@param errorMessage
the error message that is used in the exception when something went wrong.

@return the return value of the given callback.

@throws IllegalStateException
when something went wrong.

Helper method to execute a callback on a given member . This method encapsulates the error handling logic and the handling of accessibility of the member .


public <ViewType extends View<? extends ViewModelType>, ViewModelType extends ViewModel> ViewTuple<ViewType, ViewModelType> loadFxmlViewTuple(
			Class<? extends ViewType> viewType, ResourceBundle resourceBundle, ViewType codeBehind, Object root,
			ViewModelType viewModel, Context context, Collection<Scope> providedScopes,
			List<BuilderFactory> builderFactories) {

        final String pathToFXML = createFxmlPath(viewType);
        return loadFxmlViewTuple(viewType, pathToFXML, resourceBundle, codeBehind, root, viewModel, context, providedScopes, builderFactories);
    }

public < ViewType extends View < ? extends ViewModelType > , ViewModelType extends ViewModel > ViewTuple < ViewType , ViewModelType > loadFxmlViewTuple ( Class < ? extends ViewType > viewType , ResourceBundle resourceBundle , ViewType codeBehind , Object root , ViewModelType viewModel , Context context , Collection < Scope > providedScopes , List < BuilderFactory > builderFactories ) { final String pathToFXML = createFxmlPath ( viewType ) ; return loadFxmlViewTuple ( viewType , pathToFXML , resourceBundle , codeBehind , root , viewModel , context , providedScopes , builderFactories ) ; }

Load the viewTuple by it`s ViewType.

@param viewType
the type of the view to be loaded.
@param resourceBundle
the resourceBundle that is passed to the
{@link javafx.fxml.FXMLLoader}.
@param codeBehind
the controller instance that is passed to the
{@link javafx.fxml.FXMLLoader}
@param root
the root object that is passed to the
{@link javafx.fxml.FXMLLoader}
@param viewModel
the viewModel instance that is used when loading the
viewTuple.
@param <ViewType>
the generic type of the view.
@param <ViewModelType>
the generic type of the viewModel.
@param builderFactories a list of custom builder factories. may be null
@return the loaded ViewTuple.


private String createFxmlPath(Class<?> viewType) {
        final StringBuilder pathBuilder = new StringBuilder();

        final FxmlPath pathAnnotation = viewType.getDeclaredAnnotation(FxmlPath.class); //Get annotation from view
        final String fxmlPath = Optional.ofNullable(pathAnnotation)
                .map(FxmlPath::value)
                .map(String::trim)
                .orElse("");

        if (fxmlPath.isEmpty()) {
            pathBuilder.append("/");

            if (viewType.getPackage() != null) {
                pathBuilder.append(viewType.getPackage().getName().replaceAll("\\.", "/"));
                pathBuilder.append("/");
            }

            pathBuilder.append(viewType.getSimpleName());
            pathBuilder.append(".fxml");
        } else {
            pathBuilder.append(fxmlPath);
        }

        return pathBuilder.toString();
    }

private
String
createFxmlPath
(
Class
<
>
viewType
)
{
final
StringBuilder
pathBuilder
=
new
StringBuilder
(
)
;
final
FxmlPath
pathAnnotation
=
viewType
.
getDeclaredAnnotation
(
FxmlPath
.
class
)
;
//Get annotation from view
final
String
fxmlPath
=
Optional
.
ofNullable
(
pathAnnotation
)
.
map
(
FxmlPath
::
value
)
.
map
(
String
::
trim
)
.
orElse
(
""
)
;
if
(
fxmlPath
.
isEmpty
(
)
)
{
pathBuilder
.
append
(
"/"
)
;
if
(
viewType
.
getPackage
(
)
!=
)
{
pathBuilder
.
append
(
viewType
.
getPackage
(
)
.
getName
(
)
.
replaceAll
(
"\\."
,
"/"
)
)
;
pathBuilder
.
append
(
"/"
)
;
}
pathBuilder
.
append
(
viewType
.
getSimpleName
(
)
)
;
pathBuilder
.
append
(
".fxml"
)
;
}
else
{
pathBuilder
.
append
(
fxmlPath
)
;
}
return
pathBuilder
.
toString
(
)
;
}

This method is used to create a String with the path to the FXML file for
a given View class.

This is done by taking the package of the view class (if any) and replace
"." with "/". After that the Name of the class and the file ending
".fxml" is appended.

If the View class is annotated with @FxmlPath then the String path supplied
in the annotation  value will be used.

Example: de.saxsys.myapp.ui.MainView as view class will be transformed to
"/de/saxsys/myapp/ui/MainView.fxml"

Example 2: MainView (located in the default package) will be transformed
to "/MainView.fxml"

@param viewType
the view class type.
@return the path to the fxml file as string.

This method is used to create a String with the path to the FXML file for a given View class .


public StyleRow queryForRow(StyleMappingRow styleMappingRow) {
        StyleRow styleRow = null;

        AttributesRow attributesRow = queryForIdRow(styleMappingRow
                .getRelatedId());
        if (attributesRow != null) {
            styleRow = getRow(attributesRow);
        }

        return styleRow;
    }

public StyleRow queryForRow ( StyleMappingRow styleMappingRow ) { StyleRow styleRow = null ; AttributesRow attributesRow = queryForIdRow ( styleMappingRow . getRelatedId ( ) ) ; if ( attributesRow != null ) { styleRow = getRow ( attributesRow ) ; } return styleRow ; }

Query for the style row from a style mapping row

@param styleMappingRow style mapping row
@return style row


public int getPixelValue(byte[] imageBytes, int x, int y) {

        PngReaderInt reader = new PngReaderInt(new ByteArrayInputStream(imageBytes));
        validateImageType(reader);
        ImageLineInt row = (ImageLineInt) reader.readRow(y);
        int pixelValue = row.getScanline()[x];
        reader.close();

        return pixelValue;
    }

public int getPixelValue ( byte [ ] imageBytes , int x , int y ) { PngReaderInt reader = new PngReaderInt ( new ByteArrayInputStream ( imageBytes ) ) ; validateImageType ( reader ) ; ImageLineInt row = ( ImageLineInt ) reader . readRow ( y ) ; int pixelValue = row . getScanline ( ) [ x ] ; reader . close ( ) ; return pixelValue ; }

Get the pixel value as a 16 bit unsigned integer value

@param imageBytes image bytes
@param x          x coordinate
@param y          y coordinate
@return pixel value


public int[] getPixelValues(byte[] imageBytes) {

        PngReaderInt reader = new PngReaderInt(new ByteArrayInputStream(imageBytes));
        validateImageType(reader);
        int[] pixels = new int[reader.imgInfo.cols * reader.imgInfo.rows];
        int rowNumber = 0;
        while (reader.hasMoreRows()) {
            ImageLineInt row = reader.readRowInt();
            int[] rowValues = row.getScanline();
            System.arraycopy(rowValues, 0, pixels, rowNumber * reader.imgInfo.cols, rowValues.length);
            rowNumber++;
        }
        reader.close();

        return pixels;
    }

public int [ ] getPixelValues ( byte [ ] imageBytes ) { PngReaderInt reader = new PngReaderInt ( new ByteArrayInputStream ( imageBytes ) ) ; validateImageType ( reader ) ; int [ ] pixels = new int [ reader . imgInfo . cols * reader . imgInfo . rows ] ; int rowNumber = 0 ; while ( reader . hasMoreRows ( ) ) { ImageLineInt row = reader . readRowInt ( ) ; int [ ] rowValues = row . getScanline ( ) ; System . arraycopy ( rowValues , 0 , pixels , rowNumber * reader . imgInfo . cols , rowValues . length ) ; rowNumber ++ ; } reader . close ( ) ; return pixels ; }

Get the pixel values of the image as 16 bit unsigned integer values

@param imageBytes image bytes
@return 16 bit unsigned integer pixel values


public static void validateImageType(PngReader reader) {
        if (reader == null) {
            throw new GeoPackageException("The image is null");
        }
        if (reader.imgInfo.channels != 1 || reader.imgInfo.bitDepth != 16) {
            throw new GeoPackageException(
                    "The coverage data tile is expected to be a single channel 16 bit unsigned short, channels: "
                            + reader.imgInfo.channels + ", bits: " + reader.imgInfo.bitDepth);
        }
    }

Validate that the image type is single channel 16 bit

@param reader png reader


public CoverageDataPngImage createImage(int tileWidth, int tileHeight) {
        ImageInfo imageInfo = new ImageInfo(tileWidth, tileHeight, 16, false, true, false);
        CoverageDataPngImage image = new CoverageDataPngImage(imageInfo);
        return image;
    }

public CoverageDataPngImage createImage ( int tileWidth , int tileHeight ) { ImageInfo imageInfo = new ImageInfo ( tileWidth , tileHeight , 16 , false , true , false ) ; CoverageDataPngImage image = new CoverageDataPngImage ( imageInfo ) ; return image ; }

Create a new 16 bit single channel image

@param tileWidth  tile width
@param tileHeight tile height
@return image


public void setTileData(Bitmap bitmap, CompressFormat format, int quality)
            throws IOException {
        byte[] tileData = BitmapConverter.toBytes(bitmap, format, quality);
        setTileData(tileData);
    }

public void setTileData ( Bitmap bitmap , CompressFormat format , int quality ) throws IOException { byte [ ] tileData = BitmapConverter . toBytes ( bitmap , format , quality ) ; setTileData ( tileData ) ; }

Set the tile data from a bitmap

@param bitmap  tile bitmap
@param format  compress format
@param quality quality
@throws IOException upon failure


public Cursor rawQuery(String sql, String[] args) {
        return db.rawQuery(sql, args);
    }

public Cursor rawQuery ( String sql , String [ ] args ) { return db . rawQuery ( sql , args ) ; }

Perform a raw database query

@param sql  sql command
@param args arguments
@return cursor
@since 1.2.1


public CursorResult wrapQuery(String sql,
                                  String[] selectionArgs) {
        return new CursorResult(rawQuery(sql, selectionArgs));
    }

public CursorResult wrapQuery ( String sql , String [ ] selectionArgs ) { return new CursorResult ( rawQuery ( sql , selectionArgs ) ) ; }

Perform the query and wrap as a result

@param sql           sql statement
@param selectionArgs selection arguments
@return result
@since 3.1.0


public TResult query(TResult previousResult) {
        UserQuery query = previousResult.getQuery();
        TResult result = query(query);
        return result;
    }

public TResult query ( TResult previousResult ) { UserQuery query = previousResult . getQuery ( ) ; TResult result = query ( query ) ; return result ; }

Query using the query from a previous query result

@param previousResult previous result
@return result
@since 2.0.0


public TResult query(UserQuery query) {
        Cursor cursor = null;

        String[] selectionArgs = query.getSelectionArgs();

        String sql = query.getSql();
        if (sql != null) {
            cursor = database.rawQuery(sql, selectionArgs);
        } else {

            String table = query.getTable();
            String[] columns = query.getColumns();
            String selection = query.getSelection();
            String groupBy = query.getGroupBy();
            String having = query.getHaving();
            String orderBy = query.getOrderBy();

            String[] columnsAs = query.getColumnsAs();
            String limit = query.getLimit();

            if (columnsAs != null && limit != null) {
                cursor = database.query(table, columns, columnsAs, selection, selectionArgs, groupBy, having, orderBy, limit);
            } else if (columnsAs != null) {
                cursor = database.query(table, columns, columnsAs, selection, selectionArgs, groupBy, having, orderBy);
            } else if (limit != null) {
                cursor = database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit);
            } else {
                cursor = database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
            }

        }

        TResult result = handleCursor(cursor, query);

        return result;
    }

public TResult query ( UserQuery query ) { Cursor cursor = null ; String [ ] selectionArgs = query . getSelectionArgs ( ) ; String sql = query . getSql ( ) ; if ( sql != null ) { cursor = database . rawQuery ( sql , selectionArgs ) ; } else { String table = query . getTable ( ) ; String [ ] columns = query . getColumns ( ) ; String selection = query . getSelection ( ) ; String groupBy = query . getGroupBy ( ) ; String having = query . getHaving ( ) ; String orderBy = query . getOrderBy ( ) ; String [ ] columnsAs = query . getColumnsAs ( ) ; String limit = query . getLimit ( ) ; if ( columnsAs != null && limit != null ) { cursor = database . query ( table , columns , columnsAs , selection , selectionArgs , groupBy , having , orderBy , limit ) ; } else if ( columnsAs != null ) { cursor = database . query ( table , columns , columnsAs , selection , selectionArgs , groupBy , having , orderBy ) ; } else if ( limit != null ) { cursor = database . query ( table , columns , selection , selectionArgs , groupBy , having , orderBy , limit ) ; } else { cursor = database . query ( table , columns , selection , selectionArgs , groupBy , having , orderBy ) ; } } TResult result = handleCursor ( cursor , query ) ; return result ; }

Query using the user query arguments

@param query user query
@return result
@since 2.0.0


private TResult handleCursor(Cursor cursor, UserQuery query) {
        TResult result = convertCursor(cursor);
        result.setQuery(query);
        if (table != null) {
            result.setTable(table);
        }
        return result;
    }

private TResult handleCursor ( Cursor cursor , UserQuery query ) { TResult result = convertCursor ( cursor ) ; result . setQuery ( query ) ; if ( table != null ) { result . setTable ( table ) ; } return result ; }

Convert the cursor to the result type cursor

@param cursor cursor
@param query  user query
@return result cursor


public FeatureRow getFeatureRow(GeometryIndex geometryIndex) {

        long geomId = geometryIndex.getGeomId();

        // Get the row or lock for reading
        FeatureRow row = featureRowSync.getRowOrLock(geomId);
        if (row == null) {
            // Query for the row and set in the sync
            try {
                row = featureDao.queryForIdRow(geomId);
            } finally {
                featureRowSync.setRow(geomId, row);
            }
        }

        return row;
    }

Get the feature row for the Geometry Index

@param geometryIndex geometry index
@return feature row


public void registerTable(String tableName,
                              GeoPackageCursorWrapper cursorWrapper) {

        // Remove an existing cursor wrapper
        tableCursors.remove(tableName);

        // Add the wrapper
        tableCursors.put(tableName, cursorWrapper);
        String quotedTableName = CoreSQLUtils.quoteWrap(tableName);
        tableCursors.put(quotedTableName, cursorWrapper);

        // The Android android.database.sqlite.SQLiteDatabase findEditTable method
        // finds the new cursor edit table name based upon the first space or comma.
        // Fix (hopefully temporary) to wrap with the expected cursor type
        int spacePosition = tableName.indexOf(' ');
        if (spacePosition > 0) {
            tableCursors.put(tableName.substring(0, spacePosition), cursorWrapper);
            tableCursors.put(quotedTableName.substring(0, quotedTableName.indexOf(' ')), cursorWrapper);
        }

    }

Register a cursor wrapper for the provided table name. Database queries
will wrap the returned cursor

@param tableName
@param cursorWrapper

Register a cursor wrapper for the provided table name . Database queries will wrap the returned cursor


private TileRow retrieveTileRow(int x, int y, int zoom) {
        return tileDao.queryForTile(x, y, zoom);
    }

private TileRow retrieveTileRow ( int x , int y , int zoom ) { return tileDao . queryForTile ( x , y , zoom ) ; }

Retrieve the tile row

@param x
@param y
@param zoom
@return


public static CoverageData<?> getCoverageData(GeoPackage geoPackage,
                                                  TileDao tileDao, Integer width, Integer height,
                                                  Projection requestProjection) {

        TileMatrixSet tileMatrixSet = tileDao.getTileMatrixSet();
        GriddedCoverageDao griddedCoverageDao = geoPackage
                .getGriddedCoverageDao();

        GriddedCoverage griddedCoverage = null;
        try {
            if (griddedCoverageDao.isTableExists()) {
                griddedCoverage = griddedCoverageDao.query(tileMatrixSet);
            }
        } catch (SQLException e) {
            throw new GeoPackageException(
                    "Failed to get Gridded Coverage for table name: "
                            + tileMatrixSet.getTableName(), e);
        }

        CoverageData<?> coverageData = null;

        GriddedCoverageDataType dataType = griddedCoverage.getDataType();
        switch (dataType) {
            case INTEGER:
                coverageData = new CoverageDataPng(geoPackage, tileDao, width,
                        height, requestProjection);
                break;
            case FLOAT:
                coverageData = new CoverageDataTiff(geoPackage, tileDao, width,
                        height, requestProjection);
                break;
            default:
                throw new GeoPackageException(
                        "Unsupported Gridded Coverage Data Type: " + dataType);
        }

        return coverageData;
    }

Get a Tiled Gridded Coverage Data

@param geoPackage        GeoPackage
@param tileDao           tile dao
@param width             coverage data response width
@param height            coverage data response height
@param requestProjection request projection
@return coverage data


public static CoverageData<?> getCoverageData(GeoPackage geoPackage,
                                                  TileDao tileDao) {
        return getCoverageData(geoPackage, tileDao, null, null,
                tileDao.getProjection());
    }

public static CoverageData < ? > getCoverageData ( GeoPackage geoPackage , TileDao tileDao ) { return getCoverageData ( geoPackage , tileDao , null , null , tileDao . getProjection ( ) ) ; }

Get a Tiled Gridded Coverage Data, use the coverage data pixel tile size
as the request size width and height

@param geoPackage GeoPackage
@param tileDao    tile dao
@return coverage data

Get a Tiled Gridded Coverage Data use the coverage data pixel tile size as the request size width and height


public static CoverageData<?> getCoverageData(GeoPackage geoPackage,
                                                  TileDao tileDao, Projection requestProjection) {
        return getCoverageData(geoPackage, tileDao, null, null,
                requestProjection);
    }

public static CoverageData < ? > getCoverageData ( GeoPackage geoPackage , TileDao tileDao , Projection requestProjection ) { return getCoverageData ( geoPackage , tileDao , null , null , requestProjection ) ; }

Get a Tiled Gridded Coverage Data, use the coverage data pixel tile size
as the request size width and height, request as the specified projection

@param geoPackage        GeoPackage
@param tileDao           tile dao
@param requestProjection request projection
@return coverage data

Get a Tiled Gridded Coverage Data use the coverage data pixel tile size as the request size width and height request as the specified projection


private CoverageDataTileMatrixResults getResults(CoverageDataRequest request,
                                                     BoundingBox requestProjectedBoundingBox, int overlappingPixels) {
        // Try to get the coverage data from the current zoom level
        TileMatrix tileMatrix = getTileMatrix(request);
        CoverageDataTileMatrixResults results = null;
        if (tileMatrix != null) {
            results = getResults(requestProjectedBoundingBox, tileMatrix,
                    overlappingPixels);

            // Try to zoom in or out to find a matching coverage data
            if (results == null) {
                results = getResultsZoom(requestProjectedBoundingBox,
                        tileMatrix, overlappingPixels);
            }
        }
        return results;
    }

Get the coverage data tile results by finding the tile matrix with values

@param request                     coverage data request
@param requestProjectedBoundingBox request projected bounding box
@param overlappingPixels           overlapping request pixels
@return tile matrix results


private CoverageDataTileMatrixResults getResultsZoom(
            BoundingBox requestProjectedBoundingBox, TileMatrix tileMatrix,
            int overlappingPixels) {

        CoverageDataTileMatrixResults results = null;

        if (zoomIn && zoomInBeforeOut) {
            results = getResultsZoomIn(requestProjectedBoundingBox, tileMatrix,
                    overlappingPixels);
        }
        if (results == null && zoomOut) {
            results = getResultsZoomOut(requestProjectedBoundingBox,
                    tileMatrix, overlappingPixels);
        }
        if (results == null && zoomIn && !zoomInBeforeOut) {
            results = getResultsZoomIn(requestProjectedBoundingBox, tileMatrix,
                    overlappingPixels);
        }

        return results;
    }

private CoverageDataTileMatrixResults getResultsZoom ( BoundingBox requestProjectedBoundingBox , TileMatrix tileMatrix , int overlappingPixels ) { CoverageDataTileMatrixResults results = null ; if ( zoomIn && zoomInBeforeOut ) { results = getResultsZoomIn ( requestProjectedBoundingBox , tileMatrix , overlappingPixels ) ; } if ( results == null && zoomOut ) { results = getResultsZoomOut ( requestProjectedBoundingBox , tileMatrix , overlappingPixels ) ; } if ( results == null && zoomIn && ! zoomInBeforeOut ) { results = getResultsZoomIn ( requestProjectedBoundingBox , tileMatrix , overlappingPixels ) ; } return results ; }

Get the coverage data tile results by zooming in or out as needed from the
provided tile matrix to find values

@param requestProjectedBoundingBox request projected bounding box
@param tileMatrix                  tile matrix
@param overlappingPixels           overlapping request pixels
@return tile matrix results


private CoverageDataTileMatrixResults getResultsZoomIn(
            BoundingBox requestProjectedBoundingBox, TileMatrix tileMatrix,
            int overlappingPixels) {

        CoverageDataTileMatrixResults results = null;
        for (long zoomLevel = tileMatrix.getZoomLevel() + 1; zoomLevel <= tileDao
                .getMaxZoom(); zoomLevel++) {
            TileMatrix zoomTileMatrix = tileDao.getTileMatrix(zoomLevel);
            if (zoomTileMatrix != null) {
                results = getResults(requestProjectedBoundingBox,
                        zoomTileMatrix, overlappingPixels);
                if (results != null) {
                    break;
                }
            }
        }
        return results;
    }

private CoverageDataTileMatrixResults getResultsZoomIn ( BoundingBox requestProjectedBoundingBox , TileMatrix tileMatrix , int overlappingPixels ) { CoverageDataTileMatrixResults results = null ; for ( long zoomLevel = tileMatrix . getZoomLevel ( ) + 1 ; zoomLevel <= tileDao . getMaxZoom ( ) ; zoomLevel ++ ) { TileMatrix zoomTileMatrix = tileDao . getTileMatrix ( zoomLevel ) ; if ( zoomTileMatrix != null ) { results = getResults ( requestProjectedBoundingBox , zoomTileMatrix , overlappingPixels ) ; if ( results != null ) { break ; } } } return results ; }

Get the coverage data tile results by zooming in from the provided tile
matrix

@param requestProjectedBoundingBox request projected bounding box
@param tileMatrix                  tile matrix
@param overlappingPixels           overlapping request pixels
@return tile matrix results


private CoverageDataTileMatrixResults getResultsZoomOut(
            BoundingBox requestProjectedBoundingBox, TileMatrix tileMatrix,
            int overlappingPixels) {

        CoverageDataTileMatrixResults results = null;
        for (long zoomLevel = tileMatrix.getZoomLevel() - 1; zoomLevel >= tileDao
                .getMinZoom(); zoomLevel--) {
            TileMatrix zoomTileMatrix = tileDao.getTileMatrix(zoomLevel);
            if (zoomTileMatrix != null) {
                results = getResults(requestProjectedBoundingBox,
                        zoomTileMatrix, overlappingPixels);
                if (results != null) {
                    break;
                }
            }
        }
        return results;
    }

private CoverageDataTileMatrixResults getResultsZoomOut ( BoundingBox requestProjectedBoundingBox , TileMatrix tileMatrix , int overlappingPixels ) { CoverageDataTileMatrixResults results = null ; for ( long zoomLevel = tileMatrix . getZoomLevel ( ) - 1 ; zoomLevel >= tileDao . getMinZoom ( ) ; zoomLevel -- ) { TileMatrix zoomTileMatrix = tileDao . getTileMatrix ( zoomLevel ) ; if ( zoomTileMatrix != null ) { results = getResults ( requestProjectedBoundingBox , zoomTileMatrix , overlappingPixels ) ; if ( results != null ) { break ; } } } return results ; }

Get the coverage data tile results by zooming out from the provided tile
matrix

@param requestProjectedBoundingBox request projected bounding box
@param tileMatrix                  tile matrix
@param overlappingPixels           overlapping request pixels
@return tile matrix results


private TileMatrix getTileMatrix(CoverageDataRequest request) {

        TileMatrix tileMatrix = null;

        // Check if the request overlaps coverage data bounding box
        if (request.overlap(coverageBoundingBox) != null) {

            // Get the tile distance
            BoundingBox projectedBoundingBox = request
                    .getProjectedBoundingBox();
            double distanceWidth = projectedBoundingBox.getMaxLongitude()
                    - projectedBoundingBox.getMinLongitude();
            double distanceHeight = projectedBoundingBox.getMaxLatitude()
                    - projectedBoundingBox.getMinLatitude();

            // Get the zoom level to request based upon the tile size
            Long zoomLevel = tileDao.getClosestZoomLevel(distanceWidth,
                    distanceHeight);

            // If there is a matching zoom level
            if (zoomLevel != null) {
                tileMatrix = tileDao.getTileMatrix(zoomLevel);
            }
        }

        return tileMatrix;
    }

Get the tile matrix for the zoom level as defined by the area of the
request

@param request coverage data request
@return tile matrix or null


public double getValue(TileRow tileRow, int x, int y) {
        GriddedTile griddedTile = getGriddedTile(tileRow.getId());
        double value = getValue(griddedTile, tileRow, x, y);
        return value;
    }

public double getValue ( TileRow tileRow , int x , int y ) { GriddedTile griddedTile = getGriddedTile ( tileRow . getId ( ) ) ; double value = getValue ( griddedTile , tileRow , x , y ) ; return value ; }

Get the coverage data value of the pixel in the tile row image

@param tileRow tile row
@param x       x coordinate
@param y       y coordinate
@return coverage data value


public void setIcon(IconRow iconRow, GeometryType geometryType) {
        if (geometryType != null) {
            if (iconRow != null) {
                icons.put(geometryType, iconRow);
            } else {
                icons.remove(geometryType);
            }
        } else {
            defaultIcon = iconRow;
        }
    }

public void setIcon ( IconRow iconRow , GeometryType geometryType ) { if ( geometryType != null ) { if ( iconRow != null ) { icons . put ( geometryType , iconRow ) ; } else { icons . remove ( geometryType ) ; } } else { defaultIcon = iconRow ; } }

Set the icon for the geometry type

@param iconRow      icon row
@param geometryType geometry type


public IconRow getIcon(GeometryType geometryType) {

        IconRow iconRow = null;

        if (geometryType != null && !icons.isEmpty()) {
            List<GeometryType> geometryTypes = GeometryUtils
                    .parentHierarchy(geometryType);
            geometryTypes.add(0, geometryType);
            for (GeometryType type : geometryTypes) {
                iconRow = icons.get(type);
                if (iconRow != null) {
                    break;
                }
            }
        }

        if (iconRow == null) {
            iconRow = defaultIcon;
        }

        if (iconRow == null && geometryType == null && icons.size() == 1) {
            iconRow = icons.values().iterator().next();
        }

        return iconRow;
    }

public IconRow getIcon ( GeometryType geometryType ) { IconRow iconRow = null ; if ( geometryType != null && ! icons . isEmpty ( ) ) { List < GeometryType > geometryTypes = GeometryUtils . parentHierarchy ( geometryType ) ; geometryTypes . add ( 0 , geometryType ) ; for ( GeometryType type : geometryTypes ) { iconRow = icons . get ( type ) ; if ( iconRow != null ) { break ; } } } if ( iconRow == null ) { iconRow = defaultIcon ; } if ( iconRow == null && geometryType == null && icons . size ( ) == 1 ) { iconRow = icons . values ( ) . iterator ( ) . next ( ) ; } return iconRow ; }

Get the icon for the geometry type

@param geometryType geometry type
@return icon


public org.sqlite.database.sqlite.SQLiteDatabase openOrGetBindingsDb() {
        if (bindingsDb == null) {
            synchronized (db) {
                if (bindingsDb == null) {
                    System.loadLibrary("sqliteX");
                    bindingsDb = org.sqlite.database.sqlite.SQLiteDatabase.openDatabase(db.getPath(), null, org.sqlite.database.sqlite.SQLiteDatabase.OPEN_READWRITE);
                }
            }
        }
        return bindingsDb;
    }

public org . sqlite . database . sqlite . SQLiteDatabase openOrGetBindingsDb ( ) { if ( bindingsDb == null ) { synchronized ( db ) { if ( bindingsDb == null ) { System . loadLibrary ( "sqliteX" ) ; bindingsDb = org . sqlite . database . sqlite . SQLiteDatabase . openDatabase ( db . getPath ( ) , null , org . sqlite . database . sqlite . SQLiteDatabase . OPEN_READWRITE ) ; } } } return bindingsDb ; }

Open or get a connection using the SQLite Android Bindings connection

@return bindings connection


public static Bitmap toBitmap(byte[] bytes, Options options) {
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length,
                options);
        return bitmap;
    }

public static Bitmap toBitmap ( byte [ ] bytes , Options options ) { Bitmap bitmap = BitmapFactory . decodeByteArray ( bytes , 0 , bytes . length , options ) ; return bitmap ; }

Decode the bytes to a bitmap, with options

@param bytes   image bytes
@param options decode options
@return image bitmap

Decode the bytes to a bitmap with options


public static byte[] toBytes(Bitmap bitmap, CompressFormat format,
                                 int quality) throws IOException {
        byte[] bytes = null;
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        try {
            bitmap.compress(format, quality, byteStream);
            bytes = byteStream.toByteArray();
        } finally {
            byteStream.close();
        }
        return bytes;
    }

public static byte [ ] toBytes ( Bitmap bitmap , CompressFormat format , int quality ) throws IOException { byte [ ] bytes = null ; ByteArrayOutputStream byteStream = new ByteArrayOutputStream ( ) ; try { bitmap . compress ( format , quality , byteStream ) ; bytes = byteStream . toByteArray ( ) ; } finally { byteStream . close ( ) ; } return bytes ; }

Compress the bitmap to a byte array

@param bitmap  bitmap image
@param format  compress format
@param quality quality
@return image bytes
@throws IOException upon failure


public void create(TableMetadata metadata) {
        ContentValues values = new ContentValues();
        values.put(TableMetadata.COLUMN_GEOPACKAGE_ID, metadata.getGeoPackageId());
        values.put(TableMetadata.COLUMN_TABLE_NAME, metadata.getTableName());
        values.put(TableMetadata.COLUMN_LAST_INDEXED, metadata.getLastIndexed());
        long insertId = db.insert(
                TableMetadata.TABLE_NAME, null,
                values);
        if (insertId == -1) {
            throw new GeoPackageException(
                    "Failed to insert table metadata. GeoPackage Id: "
                            + metadata.getGeoPackageId() + ", Table Name: "
                            + metadata.getTableName() + ", Last Indexed: "
                            + metadata.getLastIndexed());
        }
    }

Create a new table metadata

@param metadata table metadata


public boolean delete(long geoPackageId, String tableName) {

        GeometryMetadataDataSource geomDs = new GeometryMetadataDataSource(db);
        geomDs.delete(geoPackageId, tableName);

        String whereClause = TableMetadata.COLUMN_GEOPACKAGE_ID
                + " = ? AND " + TableMetadata.COLUMN_TABLE_NAME + " = ?";
        String[] whereArgs = new String[]{String.valueOf(geoPackageId), tableName};
        int deleteCount = db.delete(
                TableMetadata.TABLE_NAME,
                whereClause, whereArgs);
        return deleteCount > 0;
    }

public
boolean
delete
(
long
geoPackageId
,
String
tableName
)
{
GeometryMetadataDataSource
geomDs
=
new
GeometryMetadataDataSource
(
db
)
;
geomDs
.
delete
(
geoPackageId
,
tableName
)
;
String
whereClause
=
TableMetadata
.
COLUMN_GEOPACKAGE_ID
+
" = ? AND "
+
TableMetadata
.
COLUMN_TABLE_NAME
+
" = ?"
;
String
[
]
whereArgs
=
new
String
[
]
{
String
.
valueOf
(
geoPackageId
)
,
tableName
}
;
int
deleteCount
=
db
.
delete
(
TableMetadata
.
TABLE_NAME
,
whereClause
,
whereArgs
)
;
return
deleteCount
>
0
;
}

Delete the database table name

@param geoPackageId GeoPackage id
@param tableName    table name
@return deleted flag


public TableMetadata getOrCreate(String geoPackage, String tableName) {

        GeoPackageMetadataDataSource ds = new GeoPackageMetadataDataSource(db);
        GeoPackageMetadata geoPackageMetadata = ds.getOrCreate(geoPackage);

        TableMetadata metadata = get(geoPackageMetadata.getId(), tableName);

        if (metadata == null) {
            metadata = new TableMetadata();
            metadata.setGeoPackageId(geoPackageMetadata.getId());
            metadata.setTableName(tableName);
            create(metadata);
        }
        return metadata;
    }

public TableMetadata getOrCreate ( String geoPackage , String tableName ) { GeoPackageMetadataDataSource ds = new GeoPackageMetadataDataSource ( db ) ; GeoPackageMetadata geoPackageMetadata = ds . getOrCreate ( geoPackage ) ; TableMetadata metadata = get ( geoPackageMetadata . getId ( ) , tableName ) ; if ( metadata == null ) { metadata = new TableMetadata ( ) ; metadata . setGeoPackageId ( geoPackageMetadata . getId ( ) ) ; metadata . setTableName ( tableName ) ; create ( metadata ) ; } return metadata ; }

Get a table metadata or create if it does not exist

@param geoPackage GeoPackage name
@param tableName  table name
@return table metadata


public long getGeoPackageId(String geoPackage) {
        long id = -1;
        GeoPackageMetadataDataSource ds = new GeoPackageMetadataDataSource(db);
        GeoPackageMetadata metadata = ds.get(geoPackage);
        if (metadata != null) {
            id = metadata.getId();
        }
        return id;
    }

public long getGeoPackageId ( String geoPackage ) { long id = - 1 ; GeoPackageMetadataDataSource ds = new GeoPackageMetadataDataSource ( db ) ; GeoPackageMetadata metadata = ds . get ( geoPackage ) ; if ( metadata != null ) { id = metadata . getId ( ) ; } return id ; }

Get a GeoPackage id from the name

@param geoPackage GeoPackage name
@return id


private TableMetadata createTableMetadata(Cursor cursor) {
        TableMetadata metadata = new TableMetadata();
        metadata.setGeoPackageId(cursor.getLong(0));
        metadata.setTableName(cursor.getString(1));
        if (!cursor.isNull(2)) {
            metadata.setLastIndexed(cursor.getLong(2));
        }
        return metadata;
    }

private TableMetadata createTableMetadata ( Cursor cursor ) { TableMetadata metadata = new TableMetadata ( ) ; metadata . setGeoPackageId ( cursor . getLong ( 0 ) ) ; metadata . setTableName ( cursor . getString ( 1 ) ) ; if ( ! cursor . isNull ( 2 ) ) { metadata . setLastIndexed ( cursor . getLong ( 2 ) ) ; } return metadata ; }

Create a table metadata from the current cursor location

@param cursor cursor
@return table metadata


public Extensions create() {
        Extensions extension = null;
        if (!has()) {
            extension = rTree.create(featureDao.getTable());
            if (progress != null) {
                progress.addProgress(count());
            }
        }
        return extension;
    }

public Extensions create ( ) { Extensions extension = null ; if ( ! has ( ) ) { extension = rTree . create ( featureDao . getTable ( ) ) ; if ( progress != null ) { progress . addProgress ( count ( ) ) ; } } return extension ; }

Create the RTree extension for the feature table

@return extension


public FeatureRow getFeatureRow(UserCustomCursor cursor) {
        RTreeIndexTableRow row = getRow(cursor);
        return getFeatureRow(row);
    }

public FeatureRow getFeatureRow ( UserCustomCursor cursor ) { RTreeIndexTableRow row = getRow ( cursor ) ; return getFeatureRow ( row ) ; }

Get the feature row from the RTree Index Table row

@param cursor result cursor
@return feature row


public UserCustomCursor rawQuery(String sql, String[] selectionArgs) {
        validateRTree();

        Cursor cursor = database.rawQuery(sql, selectionArgs);
        UserCustomCursor customCursor = new UserCustomCursor(getTable(), cursor);
        return customCursor;
    }

public UserCustomCursor rawQuery ( String sql , String [ ] selectionArgs ) { validateRTree ( ) ; Cursor cursor = database . rawQuery ( sql , selectionArgs ) ; UserCustomCursor customCursor = new UserCustomCursor ( getTable ( ) , cursor ) ; return customCursor ; }

Perform a raw query

@param sql           sql statement
@param selectionArgs selection arguments
@return result cursor


public UserCustomCursor query(BoundingBox boundingBox,
                                  Projection projection) {
        BoundingBox featureBoundingBox = projectBoundingBox(boundingBox,
                projection);
        return query(featureBoundingBox);
    }

public UserCustomCursor query ( BoundingBox boundingBox , Projection projection ) { BoundingBox featureBoundingBox = projectBoundingBox ( boundingBox , projection ) ; return query ( featureBoundingBox ) ; }

Query for rows within the bounding box in the provided projection

@param boundingBox bounding box
@param projection  projection
@return cursor results


public UserCustomCursor query(GeometryEnvelope envelope) {
        return query(envelope.getMinX(), envelope.getMinY(),
                envelope.getMaxX(), envelope.getMaxY());
    }

public UserCustomCursor query ( GeometryEnvelope envelope ) { return query ( envelope . getMinX ( ) , envelope . getMinY ( ) , envelope . getMaxX ( ) , envelope . getMaxY ( ) ) ; }

Query for rows within the geometry envelope

@param envelope geometry envelope
@return cursor results


public long count(GeometryEnvelope envelope) {
        return count(envelope.getMinX(), envelope.getMinY(),
                envelope.getMaxX(), envelope.getMaxY());
    }

public long count ( GeometryEnvelope envelope ) { return count ( envelope . getMinX ( ) , envelope . getMinY ( ) , envelope . getMaxX ( ) , envelope . getMaxY ( ) ) ; }

Count the rows within the geometry envelope

@param envelope geometry envelope
@return count


public UserCustomCursor query(double minX, double minY, double maxX,
                                  double maxY) {
        String where = buildWhere(minX, minY, maxX, maxY);
        String[] whereArgs = buildWhereArgs(minX, minY, maxX, maxY);
        return query(where, whereArgs);
    }

public UserCustomCursor query ( double minX , double minY , double maxX , double maxY ) { String where = buildWhere ( minX , minY , maxX , maxY ) ; String [ ] whereArgs = buildWhereArgs ( minX , minY , maxX , maxY ) ; return query ( where , whereArgs ) ; }

Query for rows within the bounds

@param minX min x
@param minY min y
@param maxX max x
@param maxY max y
@return cursor results


private String buildWhere(double minX, double minY, double maxX, double maxY) {

        StringBuilder where = new StringBuilder();
        where.append(buildWhere(RTreeIndexExtension.COLUMN_MIN_X, maxX, "<="));
        where.append(" AND ");
        where.append(buildWhere(RTreeIndexExtension.COLUMN_MIN_Y, maxY, "<="));
        where.append(" AND ");
        where.append(buildWhere(RTreeIndexExtension.COLUMN_MAX_X, minX, ">="));
        where.append(" AND ");
        where.append(buildWhere(RTreeIndexExtension.COLUMN_MAX_Y, minY, ">="));

        return where.toString();
    }

private
String
buildWhere
(
double
minX
,
double
minY
,
double
maxX
,
double
maxY
)
{
StringBuilder
where
=
new
StringBuilder
(
)
;
where
.
append
(
buildWhere
(
RTreeIndexExtension
.
COLUMN_MIN_X
,
maxX
,
"<="
)
)
;
where
.
append
(
" AND "
)
;
where
.
append
(
buildWhere
(
RTreeIndexExtension
.
COLUMN_MIN_Y
,
maxY
,
"<="
)
)
;
where
.
append
(
" AND "
)
;
where
.
append
(
buildWhere
(
RTreeIndexExtension
.
COLUMN_MAX_X
,
minX
,
">="
)
)
;
where
.
append
(
" AND "
)
;
where
.
append
(
buildWhere
(
RTreeIndexExtension
.
COLUMN_MAX_Y
,
minY
,
">="
)
)
;
return
where
.
toString
(
)
;
}

Build a where clause from the bounds for overlapping ranges

@param minX min x
@param minY min y
@param maxX max x
@param maxY max y
@return where clause


public ManualFeatureQueryResults query(BoundingBox boundingBox,
                                           Projection projection) {
        BoundingBox featureBoundingBox = featureDao.projectBoundingBox(
                boundingBox, projection);
        return query(featureBoundingBox);
    }

public ManualFeatureQueryResults query ( BoundingBox boundingBox , Projection projection ) { BoundingBox featureBoundingBox = featureDao . projectBoundingBox ( boundingBox , projection ) ; return query ( featureBoundingBox ) ; }

Manually query for rows within the bounding box in the provided
projection

@param boundingBox bounding box
@param projection  projection
@return results


public long count(BoundingBox boundingBox, Projection projection) {
        BoundingBox featureBoundingBox = featureDao.projectBoundingBox(
                boundingBox, projection);
        return count(featureBoundingBox);
    }

public long count ( BoundingBox boundingBox , Projection projection ) { BoundingBox featureBoundingBox = featureDao . projectBoundingBox ( boundingBox , projection ) ; return count ( featureBoundingBox ) ; }

Manually count the rows within the bounding box in the provided
projection

@param boundingBox bounding box
@param projection  projection
@return count


public ManualFeatureQueryResults query(GeometryEnvelope envelope) {
        return query(envelope.getMinX(), envelope.getMinY(),
                envelope.getMaxX(), envelope.getMaxY());
    }

public ManualFeatureQueryResults query ( GeometryEnvelope envelope ) { return query ( envelope . getMinX ( ) , envelope . getMinY ( ) , envelope . getMaxX ( ) , envelope . getMaxY ( ) ) ; }

Manually query for rows within the geometry envelope

@param envelope geometry envelope
@return results


public ManualFeatureQueryResults query(double minX, double minY,
                                           double maxX, double maxY) {

        List<Long> featureIds = new ArrayList<>();

        long offset = 0;
        boolean hasResults = true;

        minX -= tolerance;
        maxX += tolerance;
        minY -= tolerance;
        maxY += tolerance;

        while (hasResults) {

            hasResults = false;

            FeatureCursor featureCursor = featureDao.queryForChunk(chunkLimit,
                    offset);
            try {
                while (featureCursor.moveToNext()) {
                    hasResults = true;

                    FeatureRow featureRow = featureCursor.getRow();
                    GeometryEnvelope envelope = featureRow
                            .getGeometryEnvelope();
                    if (envelope != null) {

                        double minXMax = Math.max(minX, envelope.getMinX());
                        double maxXMin = Math.min(maxX, envelope.getMaxX());
                        double minYMax = Math.max(minY, envelope.getMinY());
                        double maxYMin = Math.min(maxY, envelope.getMaxY());

                        if (minXMax <= maxXMin && minYMax <= maxYMin) {
                            featureIds.add(featureRow.getId());
                        }

                    }
                }
            } finally {
                featureCursor.close();
            }

            offset += chunkLimit;
        }

        ManualFeatureQueryResults results = new ManualFeatureQueryResults(
                featureDao, featureIds);

        return results;
    }

public ManualFeatureQueryResults query ( double minX , double minY , double maxX , double maxY ) { List < Long > featureIds = new ArrayList <> ( ) ; long offset = 0 ; boolean hasResults = true ; minX -= tolerance ; maxX += tolerance ; minY -= tolerance ; maxY += tolerance ; while ( hasResults ) { hasResults = false ; FeatureCursor featureCursor = featureDao . queryForChunk ( chunkLimit , offset ) ; try { while ( featureCursor . moveToNext ( ) ) { hasResults = true ; FeatureRow featureRow = featureCursor . getRow ( ) ; GeometryEnvelope envelope = featureRow . getGeometryEnvelope ( ) ; if ( envelope != null ) { double minXMax = Math . max ( minX , envelope . getMinX ( ) ) ; double maxXMin = Math . min ( maxX , envelope . getMaxX ( ) ) ; double minYMax = Math . max ( minY , envelope . getMinY ( ) ) ; double maxYMin = Math . min ( maxY , envelope . getMaxY ( ) ) ; if ( minXMax <= maxXMin && minYMax <= maxYMin ) { featureIds . add ( featureRow . getId ( ) ) ; } } } } finally { featureCursor . close ( ) ; } offset += chunkLimit ; } ManualFeatureQueryResults results = new ManualFeatureQueryResults ( featureDao , featureIds ) ; return results ; }

Manually query for rows within the bounds

@param minX min x
@param minY min y
@param maxX max x
@param maxY max y
@return results


public long count(double minX, double minY, double maxX, double maxY) {
        return query(minX, minY, maxX, maxY).count();
    }

public long count ( double minX , double minY , double maxX , double maxY ) { return query ( minX , minY , maxX , maxY ) . count ( ) ; }

Manually count the rows within the bounds

@param minX min x
@param minY min y
@param maxX max x
@param maxY max y
@return count


public void setData(Bitmap bitmap, Bitmap.CompressFormat format)
            throws IOException {
        setData(bitmap, format, 100);
    }

public void setData ( Bitmap bitmap , Bitmap . CompressFormat format ) throws IOException { setData ( bitmap , format , 100 ) ; }

Set the data from a full quality bitmap

@param bitmap bitmap
@param format compress format
@throws IOException upon failure
@since 3.2.0


public void setData(Bitmap bitmap, Bitmap.CompressFormat format, int quality)
            throws IOException {
        setData(BitmapConverter.toBytes(bitmap, format, quality));
    }

public void setData ( Bitmap bitmap , Bitmap . CompressFormat format , int quality ) throws IOException { setData ( BitmapConverter . toBytes ( bitmap , format , quality ) ) ; }

Set the data from a bitmap

@param bitmap  bitmap
@param format  compress format
@param quality quality
@throws IOException upon failure
@since 3.2.0


public void setBitmapCompressionConfig(Config config) {
        if (options == null) {
            options = new Options();
        }
        options.inPreferredConfig = config;
    }

public void setBitmapCompressionConfig ( Config config ) { if ( options == null ) { options = new Options ( ) ; } options . inPreferredConfig = config ; }

Set the Bitmap Compress Config

@param config bitmap config


public int getTileCount() {
        if (tileCount == null) {
            long count = 0;

            boolean degrees = projection.isUnit(Units.DEGREES);
            ProjectionTransform transformToWebMercator = null;
            if (!degrees) {
                transformToWebMercator = projection.getTransformation(ProjectionConstants.EPSG_WEB_MERCATOR);
            }

            for (int zoom = minZoom; zoom <= maxZoom; zoom++) {

                BoundingBox expandedBoundingBox = getBoundingBox(zoom);

                // Get the tile grid that includes the entire bounding box
                TileGrid tileGrid = null;
                if (degrees) {
                    tileGrid = TileBoundingBoxUtils.getTileGridWGS84(expandedBoundingBox, zoom);
                } else {
                    tileGrid = TileBoundingBoxUtils.getTileGrid(expandedBoundingBox.transform(transformToWebMercator), zoom);
                }

                count += tileGrid.count();
                tileGrids.put(zoom, tileGrid);
                tileBounds.put(zoom, expandedBoundingBox);
            }

            tileCount = (int) Math.min(count, Integer.MAX_VALUE);
        }
        return tileCount;
    }

Get the tile count of tiles to be generated

@return tile count


private void adjustBounds(BoundingBox boundingBox,
                              int zoom) {
        // Google Tile Format
        if (googleTiles) {
            adjustGoogleBounds();
        } else if (projection.isUnit(Units.DEGREES)) {
            adjustGeoPackageBoundsWGS84(boundingBox, zoom);
        } else {
            adjustGeoPackageBounds(boundingBox, zoom);
        }
    }

private
void
adjustBounds
(
BoundingBox
boundingBox
,
int
zoom
)
{
// Google Tile Format
if
(
googleTiles
)
{
adjustGoogleBounds
(
)
;
}
else
if
(
projection
.
isUnit
(
Units
.
DEGREES
)
)
{
adjustGeoPackageBoundsWGS84
(
boundingBox
,
zoom
)
;
}
else
{
adjustGeoPackageBounds
(
boundingBox
,
zoom
)
;
}
}

Adjust the tile matrix set and bounds

@param boundingBox bounding box
@param zoom        zoom


private void adjustGoogleBounds() {
        // Set the tile matrix set bounding box to be the world
        BoundingBox standardWgs84Box = new BoundingBox(-ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH,
                ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE,
                ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH,
                ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE);
        ProjectionTransform wgs84ToWebMercatorTransform = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM)
                .getTransformation(ProjectionConstants.EPSG_WEB_MERCATOR);
        tileGridBoundingBox = standardWgs84Box.transform(wgs84ToWebMercatorTransform);
    }

Adjust the tile matrix set and web mercator bounds for Google tile format


private void adjustGeoPackageBoundsWGS84(BoundingBox boundingBox, int zoom) {
        // Get the fitting tile grid and determine the bounding box that fits it
        TileGrid tileGrid = TileBoundingBoxUtils.getTileGridWGS84(boundingBox, zoom);
        tileGridBoundingBox = TileBoundingBoxUtils.getWGS84BoundingBox(tileGrid, zoom);
        matrixWidth = tileGrid.getMaxX() + 1 - tileGrid.getMinX();
        matrixHeight = tileGrid.getMaxY() + 1 - tileGrid.getMinY();
    }

Adjust the tile matrix set and WGS84 bounds for GeoPackage format.
Determine the tile grid width and height

@param boundingBox
@param zoom

Adjust the tile matrix set and WGS84 bounds for GeoPackage format . Determine the tile grid width and height


private void adjustGeoPackageBounds(
            BoundingBox requestWebMercatorBoundingBox, int zoom) {
        // Get the fitting tile grid and determine the bounding box that
        // fits it
        TileGrid tileGrid = TileBoundingBoxUtils.getTileGrid(
                requestWebMercatorBoundingBox, zoom);
        tileGridBoundingBox = TileBoundingBoxUtils.getWebMercatorBoundingBox(tileGrid, zoom);
        matrixWidth = tileGrid.getMaxX() + 1 - tileGrid.getMinX();
        matrixHeight = tileGrid.getMaxY() + 1 - tileGrid.getMinY();
    }

Adjust the tile matrix set and web mercator bounds for GeoPackage format.
Determine the tile grid width and height

@param requestWebMercatorBoundingBox
@param zoom

Adjust the tile matrix set and web mercator bounds for GeoPackage format . Determine the tile grid width and height


public BoundingBox getBoundingBox(long zoomLevel) {
        BoundingBox boundingBox = null;
        TileMatrix tileMatrix = getTileMatrix(zoomLevel);
        if (tileMatrix != null) {
            TileGrid tileGrid = queryForTileGrid(zoomLevel);
            if (tileGrid != null) {
                BoundingBox matrixSetBoundingBox = getBoundingBox();
                boundingBox = TileBoundingBoxUtils.getBoundingBox(
                        matrixSetBoundingBox, tileMatrix, tileGrid);
            }

        }
        return boundingBox;
    }

public BoundingBox getBoundingBox ( long zoomLevel ) { BoundingBox boundingBox = null ; TileMatrix tileMatrix = getTileMatrix ( zoomLevel ) ; if ( tileMatrix != null ) { TileGrid tileGrid = queryForTileGrid ( zoomLevel ) ; if ( tileGrid != null ) { BoundingBox matrixSetBoundingBox = getBoundingBox ( ) ; boundingBox = TileBoundingBoxUtils . getBoundingBox ( matrixSetBoundingBox , tileMatrix , tileGrid ) ; } } return boundingBox ; }

Get the bounding box of tiles

@param zoomLevel zoom level
@return bounding box of zoom level, or null if no tiles
@since 1.1.1


public TileGrid getTileGrid(long zoomLevel) {
        TileGrid tileGrid = null;
        TileMatrix tileMatrix = getTileMatrix(zoomLevel);
        if (tileMatrix != null) {
            tileGrid = new TileGrid(0, 0, tileMatrix.getMatrixWidth() - 1,
                    tileMatrix.getMatrixHeight() - 1);
        }
        return tileGrid;
    }

public TileGrid getTileGrid ( long zoomLevel ) { TileGrid tileGrid = null ; TileMatrix tileMatrix = getTileMatrix ( zoomLevel ) ; if ( tileMatrix != null ) { tileGrid = new TileGrid ( 0 , 0 , tileMatrix . getMatrixWidth ( ) - 1 , tileMatrix . getMatrixHeight ( ) - 1 ) ; } return tileGrid ; }

Get the tile grid of the zoom level

@param zoomLevel zoom level
@return tile grid at zoom level, null if not tile matrix at zoom level
@since 1.1.1


public TileRow queryForTile(long column, long row, long zoomLevel) {

        Map<String, Object> fieldValues = new HashMap<String, Object>();
        fieldValues.put(TileTable.COLUMN_TILE_COLUMN, column);
        fieldValues.put(TileTable.COLUMN_TILE_ROW, row);
        fieldValues.put(TileTable.COLUMN_ZOOM_LEVEL, zoomLevel);

        TileCursor cursor = queryForFieldValues(fieldValues);
        TileRow tileRow = null;
        try {
            if (cursor.moveToNext()) {
                tileRow = cursor.getRow();
            }
        } finally {
            cursor.close();
        }

        return tileRow;
    }

public TileRow queryForTile ( long column , long row , long zoomLevel ) { Map < String , Object > fieldValues = new HashMap < String , Object > ( ) ; fieldValues . put ( TileTable . COLUMN_TILE_COLUMN , column ) ; fieldValues . put ( TileTable . COLUMN_TILE_ROW , row ) ; fieldValues . put ( TileTable . COLUMN_ZOOM_LEVEL , zoomLevel ) ; TileCursor cursor = queryForFieldValues ( fieldValues ) ; TileRow tileRow = null ; try { if ( cursor . moveToNext ( ) ) { tileRow = cursor . getRow ( ) ; } } finally { cursor . close ( ) ; } return tileRow ; }

Query for a Tile

@param column    column
@param row       row
@param zoomLevel zoom level
@return tile row


public TileCursor queryForTileDescending(long zoomLevel) {
        return queryForEq(TileTable.COLUMN_ZOOM_LEVEL, zoomLevel, null, null,
                TileTable.COLUMN_TILE_ROW + " DESC, "
                        + TileTable.COLUMN_TILE_COLUMN + " DESC");
    }

public
TileCursor
queryForTileDescending
(
long
zoomLevel
)
{
return
queryForEq
(
TileTable
.
COLUMN_ZOOM_LEVEL
,
zoomLevel
,
,
,
TileTable
.
COLUMN_TILE_ROW
+
" DESC, "
+
TileTable
.
COLUMN_TILE_COLUMN
+
" DESC"
)
;
}

Query for Tiles at a zoom level in descending row and column order

@param zoomLevel zoom level
@return tile cursor, should be closed


public Long getClosestZoomLevel(double length) {

        Long zoomLevel = TileDaoUtils.getClosestZoomLevel(widths, heights,
                tileMatrices, length);
        return zoomLevel;
    }

public Long getClosestZoomLevel ( double length ) { Long zoomLevel = TileDaoUtils . getClosestZoomLevel ( widths , heights , tileMatrices , length ) ; return zoomLevel ; }

Get the closest zoom level for the provided width and height in the
default units

@param length in default units
@return zoom level
@since 1.3.1


public Long getApproximateZoomLevel(double length) {

        Long zoomLevel = TileDaoUtils.getApproximateZoomLevel(widths, heights,
                tileMatrices, length);
        return zoomLevel;
    }

public Long getApproximateZoomLevel ( double length ) { Long zoomLevel = TileDaoUtils . getApproximateZoomLevel ( widths , heights , tileMatrices , length ) ; return zoomLevel ; }

Get the approximate zoom level for the provided length in the default
units. Tiles may or may not exist for the returned zoom level. The
approximate zoom level is determined using a factor of 2 from the zoom
levels with tiles.

@param length length in default units
@return approximate zoom level
@since 2.0.2

Get the approximate zoom level for the provided length in the default units . Tiles may or may not exist for the returned zoom level . The approximate zoom level is determined using a factor of 2 from the zoom levels with tiles .


public TileGrid queryForTileGrid(long zoomLevel) {

        String where = buildWhere(TileTable.COLUMN_ZOOM_LEVEL, zoomLevel);
        String[] whereArgs = buildWhereArgs(new Object[]{zoomLevel});

        Integer minX = min(TileTable.COLUMN_TILE_COLUMN, where, whereArgs);
        Integer maxX = max(TileTable.COLUMN_TILE_COLUMN, where, whereArgs);
        Integer minY = min(TileTable.COLUMN_TILE_ROW, where, whereArgs);
        Integer maxY = max(TileTable.COLUMN_TILE_ROW, where, whereArgs);

        TileGrid tileGrid = null;
        if (minX != null && maxX != null && minY != null && maxY != null) {
            tileGrid = new TileGrid(minX, minY, maxX, maxY);
        }

        return tileGrid;
    }

public TileGrid queryForTileGrid ( long zoomLevel ) { String where = buildWhere ( TileTable . COLUMN_ZOOM_LEVEL , zoomLevel ) ; String [ ] whereArgs = buildWhereArgs ( new Object [ ] { zoomLevel } ) ; Integer minX = min ( TileTable . COLUMN_TILE_COLUMN , where , whereArgs ) ; Integer maxX = max ( TileTable . COLUMN_TILE_COLUMN , where , whereArgs ) ; Integer minY = min ( TileTable . COLUMN_TILE_ROW , where , whereArgs ) ; Integer maxY = max ( TileTable . COLUMN_TILE_ROW , where , whereArgs ) ; TileGrid tileGrid = null ; if ( minX != null && maxX != null && minY != null && maxY != null ) { tileGrid = new TileGrid ( minX , minY , maxX , maxY ) ; } return tileGrid ; }

Query for the bounding

@param zoomLevel zoom level
@return tile grid of tiles at the zoom level
@since 1.1.1


public int deleteTile(long column, long row, long zoomLevel) {

        StringBuilder where = new StringBuilder();

        where.append(buildWhere(TileTable.COLUMN_ZOOM_LEVEL, zoomLevel));

        where.append(" AND ");
        where.append(buildWhere(TileTable.COLUMN_TILE_COLUMN, column));

        where.append(" AND ");
        where.append(buildWhere(TileTable.COLUMN_TILE_ROW, row));

        String[] whereArgs = buildWhereArgs(new Object[]{zoomLevel, column,
                row});

        int deleted = delete(where.toString(), whereArgs);

        return deleted;
    }

public
int
deleteTile
(
long
column
,
long
row
,
long
zoomLevel
)
{
StringBuilder
where
=
new
StringBuilder
(
)
;
where
.
append
(
buildWhere
(
TileTable
.
COLUMN_ZOOM_LEVEL
,
zoomLevel
)
)
;
where
.
append
(
" AND "
)
;
where
.
append
(
buildWhere
(
TileTable
.
COLUMN_TILE_COLUMN
,
column
)
)
;
where
.
append
(
" AND "
)
;
where
.
append
(
buildWhere
(
TileTable
.
COLUMN_TILE_ROW
,
row
)
)
;
String
[
]
whereArgs
=
buildWhereArgs
(
new
Object
[
]
{
zoomLevel
,
column
,
row
}
)
;
int
deleted
=
delete
(
where
.
toString
(
)
,
whereArgs
)
;
return
deleted
;
}

Delete a Tile

@param column    column
@param row       row
@param zoomLevel zoom level
@return number deleted, should be 0 or 1


public int count(long zoomLevel) {
        String where = buildWhere(TileTable.COLUMN_ZOOM_LEVEL, zoomLevel);
        String[] whereArgs = buildWhereArgs(zoomLevel);
        return count(where, whereArgs);
    }

public int count ( long zoomLevel ) { String where = buildWhere ( TileTable . COLUMN_ZOOM_LEVEL , zoomLevel ) ; String [ ] whereArgs = buildWhereArgs ( zoomLevel ) ; return count ( where , whereArgs ) ; }

Count of Tiles at a zoom level

@param zoomLevel zoom level
@return count


public boolean isGoogleTiles() {

        // Convert the bounding box to wgs84
        BoundingBox boundingBox = tileMatrixSet.getBoundingBox();
        BoundingBox wgs84BoundingBox = boundingBox.transform(
                projection.getTransformation(
                        ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM));

        boolean googleTiles = false;

        // Verify the bounds are the entire world
        if (wgs84BoundingBox.getMinLatitude() <= ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE
                && wgs84BoundingBox.getMaxLatitude() >= ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE
                && wgs84BoundingBox.getMinLongitude() <= -ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH
                && wgs84BoundingBox.getMaxLongitude() >= ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH) {

            googleTiles = true;

            // Verify each tile matrix is the correct width and height
            for (TileMatrix tileMatrix : tileMatrices) {
                long zoomLevel = tileMatrix.getZoomLevel();
                long tilesPerSide = TileBoundingBoxUtils
                        .tilesPerSide((int) zoomLevel);
                if (tileMatrix.getMatrixWidth() != tilesPerSide
                        || tileMatrix.getMatrixHeight() != tilesPerSide) {
                    googleTiles = false;
                    break;
                }
            }
        }

        return googleTiles;
    }

Determine if the tiles are in the Google tile coordinate format

@return google tiles flag


public float getPixelValue(byte[] imageBytes, int x, int y) {

        TIFFImage tiffImage = TiffReader.readTiff(imageBytes);
        FileDirectory directory = tiffImage.getFileDirectory();
        validateImageType(directory);
        Rasters rasters = directory.readRasters();
        float pixelValue = rasters.getFirstPixelSample(x, y).floatValue();

        return pixelValue;
    }

public float getPixelValue ( byte [ ] imageBytes , int x , int y ) { TIFFImage tiffImage = TiffReader . readTiff ( imageBytes ) ; FileDirectory directory = tiffImage . getFileDirectory ( ) ; validateImageType ( directory ) ; Rasters rasters = directory . readRasters ( ) ; float pixelValue = rasters . getFirstPixelSample ( x , y ) . floatValue ( ) ; return pixelValue ; }

Get the pixel value as a float from the image and the coordinate

@param imageBytes image bytes
@param x          x coordinate
@param y          y coordinate
@return float pixel value


public float[] getPixelValues(byte[] imageBytes) {
        TIFFImage tiffImage = TiffReader.readTiff(imageBytes);
        FileDirectory directory = tiffImage.getFileDirectory();
        validateImageType(directory);
        Rasters rasters = directory.readRasters();
        float[] pixels = new float[rasters.getWidth() * rasters.getHeight()];
        for (int y = 0; y < rasters.getHeight(); y++) {
            for (int x = 0; x < rasters.getWidth(); x++) {
                int index = rasters.getSampleIndex(x, y);
                pixels[index] = rasters.getPixelSample(0, x, y).floatValue();
            }
        }
        return pixels;
    }

public float [ ] getPixelValues ( byte [ ] imageBytes ) { TIFFImage tiffImage = TiffReader . readTiff ( imageBytes ) ; FileDirectory directory = tiffImage . getFileDirectory ( ) ; validateImageType ( directory ) ; Rasters rasters = directory . readRasters ( ) ; float [ ] pixels = new float [ rasters . getWidth ( ) * rasters . getHeight ( ) ] ; for ( int y = 0 ; y < rasters . getHeight ( ) ; y ++ ) { for ( int x = 0 ; x < rasters . getWidth ( ) ; x ++ ) { int index = rasters . getSampleIndex ( x , y ) ; pixels [ index ] = rasters . getPixelSample ( 0 , x , y ) . floatValue ( ) ; } } return pixels ; }

Get the pixel values of the image as floats

@param imageBytes image bytes
@return float pixel values


public static void validateImageType(FileDirectory directory) {
        if (directory == null) {
            throw new GeoPackageException("The image is null");
        }

        int samplesPerPixel = directory.getSamplesPerPixel();
        Integer bitsPerSample = null;
        if (directory.getBitsPerSample() != null && !directory.getBitsPerSample().isEmpty()) {
            bitsPerSample = directory.getBitsPerSample().get(0);
        }
        Integer sampleFormat = null;
        if (directory.getSampleFormat() != null && !directory.getSampleFormat().isEmpty()) {
            sampleFormat = directory.getSampleFormat().get(0);
        }

        if (samplesPerPixel != SAMPLES_PER_PIXEL
                || bitsPerSample == null || bitsPerSample != BITS_PER_SAMPLE
                || sampleFormat == null || sampleFormat != TiffConstants.SAMPLE_FORMAT_FLOAT) {
            throw new GeoPackageException(
                    "The coverage data tile is expected to be a single sample 32 bit float. Samples Per Pixel: "
                            + samplesPerPixel
                            + ", Bits Per Sample: " + bitsPerSample
                            + ", Sample Format: " + sampleFormat);
        }

    }

Validate that the image type

@param directory file directory


public CoverageDataTiffImage createImage(int tileWidth, int tileHeight) {

        Rasters rasters = new Rasters(tileWidth, tileHeight, 1,
                BITS_PER_SAMPLE, TiffConstants.SAMPLE_FORMAT_FLOAT);

        int rowsPerStrip = rasters.calculateRowsPerStrip(TiffConstants.PLANAR_CONFIGURATION_CHUNKY);

        FileDirectory fileDirectory = new FileDirectory();
        fileDirectory.setImageWidth(tileWidth);
        fileDirectory.setImageHeight(tileHeight);
        fileDirectory.setBitsPerSample(BITS_PER_SAMPLE);
        fileDirectory.setCompression(TiffConstants.COMPRESSION_NO);
        fileDirectory.setPhotometricInterpretation(TiffConstants.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO);
        fileDirectory.setSamplesPerPixel(SAMPLES_PER_PIXEL);
        fileDirectory.setRowsPerStrip(rowsPerStrip);
        fileDirectory.setPlanarConfiguration(TiffConstants.PLANAR_CONFIGURATION_CHUNKY);
        fileDirectory.setSampleFormat(TiffConstants.SAMPLE_FORMAT_FLOAT);
        fileDirectory.setWriteRasters(rasters);

        CoverageDataTiffImage image = new CoverageDataTiffImage(fileDirectory);

        return image;
    }

Create a new image

@param tileWidth  tile width
@param tileHeight tile height
@return image


public void setPixelValue(CoverageDataTiffImage image, int x, int y,
                              float pixelValue) {
        image.getRasters().setFirstPixelSample(x, y, pixelValue);
    }

public void setPixelValue ( CoverageDataTiffImage image , int x , int y , float pixelValue ) { image . getRasters ( ) . setFirstPixelSample ( x , y , pixelValue ) ; }

Set the pixel value into the image

@param image      image
@param x          x coordinate
@param y          y coordinate
@param pixelValue pixel value


private GeoPackage getOrOpen(String name, boolean writable, boolean cache) {
        GeoPackage geoPackage = get(name);
        if (geoPackage == null) {
            geoPackage = manager.open(name, writable);
            if (cache) {
                add(geoPackage);
            }
        }
        return geoPackage;
    }

private GeoPackage getOrOpen ( String name , boolean writable , boolean cache ) { GeoPackage geoPackage = get ( name ) ; if ( geoPackage == null ) { geoPackage = manager . open ( name , writable ) ; if ( cache ) { add ( geoPackage ) ; } } return geoPackage ; }

Get the cached GeoPackage or open and cache the GeoPackage

@param name     GeoPackage name
@param writable writable true to open as writable, false as read only
@param cache    true to cache opened GeoPackages
@return GeoPackage


private TRow getCurrentRow() {

        TRow row = null;

        if (table != null) {

            int[] columnTypes = new int[table.columnCount()];
            Object[] values = new Object[table.columnCount()];

            boolean valid = true;

            for (TColumn column : table.getColumns()) {

                int index = column.getIndex();
                int columnType = getType(index);

                if (column.isPrimaryKey() && columnType == FIELD_TYPE_NULL) {
                    valid = false;
                }

                columnTypes[index] = columnType;
                values[index] = getValue(column);

            }

            row = getRow(columnTypes, values);

            if (!valid) {
                invalidPositions.add(getPosition());
                row.setValid(false);
            }
        }

        return row;
    }

private TRow getCurrentRow ( ) { TRow row = null ; if ( table != null ) { int [ ] columnTypes = new int [ table . columnCount ( ) ] ; Object [ ] values = new Object [ table . columnCount ( ) ] ; boolean valid = true ; for ( TColumn column : table . getColumns ( ) ) { int index = column . getIndex ( ) ; int columnType = getType ( index ) ; if ( column . isPrimaryKey ( ) && columnType == FIELD_TYPE_NULL ) { valid = false ; } columnTypes [ index ] = columnType ; values [ index ] = getValue ( column ) ; } row = getRow ( columnTypes , values ) ; if ( ! valid ) { invalidPositions . add ( getPosition ( ) ) ; row . setValid ( false ) ; } } return row ; }


private boolean moveToNextInvalid() {

        boolean hasNext = false;

        // If requery has not been performed, a requery dao has been set, and there are invalid positions
        if (invalidCursor == null && dao != null && hasInvalidPositions()) {

            // Close the original cursor when performing an invalid cursor query
            super.close();

            // Set the blob columns to return as null
            List<TColumn> blobColumns = dao.getTable().columnsOfType(GeoPackageDataType.BLOB);
            String[] columnsAs = dao.buildColumnsAsNull(blobColumns);
            query.set(UserQueryParamType.COLUMNS_AS, columnsAs);

            // Query without blob columns and create an invalid cursor
            UserCursor<TColumn, TTable, TRow> requeryCursor = dao.query(query);
            invalidCursor = createInvalidCursor(dao, requeryCursor, getInvalidPositions(), blobColumns);
        }

        if (invalidCursor != null) {
            hasNext = invalidCursor.moveToNext();
        }

        return hasNext;
    }

Move to the next position of invalid rows to requery.  Perform the requery the first time.

@return true if invalid rows are left

Move to the next position of invalid rows to requery . Perform the requery the first time .


public List<StyleMappingRow> queryByBaseFeatureId(long id) {
        List<StyleMappingRow> rows = new ArrayList<>();
        UserCustomCursor cursor = queryByBaseId(id);
        try {
            while (cursor.moveToNext()) {
                rows.add(getRow(cursor));
            }
        } finally {
            cursor.close();
        }
        return rows;
    }

public List < StyleMappingRow > queryByBaseFeatureId ( long id ) { List < StyleMappingRow > rows = new ArrayList <> ( ) ; UserCustomCursor cursor = queryByBaseId ( id ) ; try { while ( cursor . moveToNext ( ) ) { rows . add ( getRow ( cursor ) ) ; } } finally { cursor . close ( ) ; } return rows ; }

Query for style mappings by base id

@param id base id, feature contents id or feature geometry id
@return style mappings rows


public int deleteByBaseId(long id, GeometryType geometryType) {

        String geometryTypeName = null;
        if (geometryType != null) {
            geometryTypeName = geometryType.getName();
        }

        StringBuilder where = new StringBuilder();
        where.append(buildWhere(StyleMappingTable.COLUMN_BASE_ID, id));
        where.append(" AND ");
        where.append(buildWhere(StyleMappingTable.COLUMN_GEOMETRY_TYPE_NAME,
                geometryTypeName));

        List<Object> whereArguments = new ArrayList<>();
        whereArguments.add(id);
        if (geometryTypeName != null) {
            whereArguments.add(geometryTypeName);
        }

        String[] whereArgs = buildWhereArgs(whereArguments);

        int deleted = delete(where.toString(), whereArgs);

        return deleted;
    }

public
int
deleteByBaseId
(
long
id
,
GeometryType
geometryType
)
{
String
geometryTypeName
=
;
if
(
geometryType
!=
)
{
geometryTypeName
=
geometryType
.
getName
(
)
;
}
StringBuilder
where
=
new
StringBuilder
(
)
;
where
.
append
(
buildWhere
(
StyleMappingTable
.
COLUMN_BASE_ID
,
id
)
)
;
where
.
append
(
" AND "
)
;
where
.
append
(
buildWhere
(
StyleMappingTable
.
COLUMN_GEOMETRY_TYPE_NAME
,
geometryTypeName
)
)
;
List
<
Object
>
whereArguments
=
new
ArrayList
<>
(
)
;
whereArguments
.
add
(
id
)
;
if
(
geometryTypeName
!=
)
{
whereArguments
.
add
(
geometryTypeName
)
;
}
String
[
]
whereArgs
=
buildWhereArgs
(
whereArguments
)
;
int
deleted
=
delete
(
where
.
toString
(
)
,
whereArgs
)
;
return
deleted
;
}

Delete by base is and geometry type

@param id           base id
@param geometryType geometry type
@return rows deleted


public static Rect getRectangle(long width, long height,
                                    BoundingBox boundingBox, BoundingBox boundingBoxSection) {

        RectF rectF = getFloatRectangle(width, height, boundingBox,
                boundingBoxSection);

        Rect rect = new Rect(Math.round(rectF.left), Math.round(rectF.top),
                Math.round(rectF.right), Math.round(rectF.bottom));

        return rect;
    }

public static Rect getRectangle ( long width , long height , BoundingBox boundingBox , BoundingBox boundingBoxSection ) { RectF rectF = getFloatRectangle ( width , height , boundingBox , boundingBoxSection ) ; Rect rect = new Rect ( Math . round ( rectF . left ) , Math . round ( rectF . top ) , Math . round ( rectF . right ) , Math . round ( rectF . bottom ) ) ; return rect ; }

Get a rectangle using the tile width, height, bounding box, and the
bounding box section within the outer box to build the rectangle from

@param width              width
@param height             height
@param boundingBox        full bounding box
@param boundingBoxSection rectangle bounding box section
@return rectangle

Get a rectangle using the tile width height bounding box and the bounding box section within the outer box to build the rectangle from


public static RectF getRoundedFloatRectangle(long width, long height,
                                                 BoundingBox boundingBox, BoundingBox boundingBoxSection) {

        Rect rect = getRectangle(width, height, boundingBox,
                boundingBoxSection);

        RectF rectF = new RectF(rect);

        return rectF;
    }

public static RectF getRoundedFloatRectangle ( long width , long height , BoundingBox boundingBox , BoundingBox boundingBoxSection ) { Rect rect = getRectangle ( width , height , boundingBox , boundingBoxSection ) ; RectF rectF = new RectF ( rect ) ; return rectF ; }

Get a rectangle with rounded floating point boundaries using the tile width,
height, bounding box, and the bounding box section within the outer box
to build the rectangle from

@param width              width
@param height             height
@param boundingBox        full bounding box
@param boundingBoxSection rectangle bounding box section
@return floating point rectangle
@since 1.3.0

Get a rectangle with rounded floating point boundaries using the tile width height bounding box and the bounding box section within the outer box to build the rectangle from


private List<String> deleteMissingDatabases(List<String> databases) {
        List<String> filesExist = new ArrayList<>();
        for (String database : databases) {
            if (exists(database)) {
                filesExist.add(database);
            }
        }
        return filesExist;
    }

private List < String > deleteMissingDatabases ( List < String > databases ) { List < String > filesExist = new ArrayList <> ( ) ; for ( String database : databases ) { if ( exists ( database ) ) { filesExist . add ( database ) ; } } return filesExist ; }

Delete all databases that do not exist or the database file does not exist

@param databases list of databases
@return databases that exist


private void createAndCloseGeoPackage(GeoPackageDatabase db) {

        GeoPackageConnection connection = new GeoPackageConnection(db);

        // Set the GeoPackage application id and user version
        connection.setApplicationId();
        connection.setUserVersion();

        // Create the minimum required tables
        GeoPackageTableCreator tableCreator = new GeoPackageTableCreator(connection);
        tableCreator.createRequired();

        connection.close();
    }

Create the required GeoPackage application id and tables in the newly created and open database connection.  Then close the connection.

@param db

Create the required GeoPackage application id and tables in the newly created and open database connection . Then close the connection .


private boolean isValid(String database, boolean validateHeader, boolean validateIntegrity) {

        boolean valid = false;

        if (exists(database)) {
            GeoPackageCursorFactory cursorFactory = new GeoPackageCursorFactory();
            String path = null;
            SQLiteDatabase sqlite;
            GeoPackageMetadata metadata = getGeoPackageMetadata(database);
            if (metadata != null && metadata.isExternal()) {
                path = metadata.getExternalPath();
                try {
                    sqlite = SQLiteDatabase.openDatabase(path,
                            cursorFactory, SQLiteDatabase.OPEN_READWRITE
                                    | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
                } catch (Exception e) {
                    sqlite = SQLiteDatabase.openDatabase(path,
                            cursorFactory, SQLiteDatabase.OPEN_READONLY
                                    | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
                }
            } else {
                path = context.getDatabasePath(database).getAbsolutePath();
                sqlite = context.openOrCreateDatabase(database,
                        Context.MODE_PRIVATE, cursorFactory);
            }

            try {
                valid = (!validateHeader || isDatabaseHeaderValid(sqlite))
                        && (!validateIntegrity || sqlite.isDatabaseIntegrityOk());
            } catch (Exception e) {
                Log.e(GeoPackageManagerImpl.class.getSimpleName(), "Failed to validate database", e);
            } finally {
                sqlite.close();
            }
        }

        return valid;
    }

private
boolean
isValid
(
String
database
,
boolean
validateHeader
,
boolean
validateIntegrity
)
{
boolean
valid
=
false
;
if
(
exists
(
database
)
)
{
GeoPackageCursorFactory
cursorFactory
=
new
GeoPackageCursorFactory
(
)
;
String
path
=
;
SQLiteDatabase
sqlite
;
GeoPackageMetadata
metadata
=
getGeoPackageMetadata
(
database
)
;
if
(
metadata
!=
&&
metadata
.
isExternal
(
)
)
{
path
=
metadata
.
getExternalPath
(
)
;
try
{
sqlite
=
SQLiteDatabase
.
openDatabase
(
path
,
cursorFactory
,
SQLiteDatabase
.
OPEN_READWRITE
|
SQLiteDatabase
.
NO_LOCALIZED_COLLATORS
)
;
}
catch
(
Exception
e
)
{
sqlite
=
SQLiteDatabase
.
openDatabase
(
path
,
cursorFactory
,
SQLiteDatabase
.
OPEN_READONLY
|
SQLiteDatabase
.
NO_LOCALIZED_COLLATORS
)
;
}
}
else
{
path
=
context
.
getDatabasePath
(
database
)
.
getAbsolutePath
(
)
;
sqlite
=
context
.
openOrCreateDatabase
(
database
,
Context
.
MODE_PRIVATE
,
cursorFactory
)
;
}
try
{
valid
=
(
!
validateHeader
||
isDatabaseHeaderValid
(
sqlite
)
)
&&
(
!
validateIntegrity
||
sqlite
.
isDatabaseIntegrityOk
(
)
)
;
}
catch
(
Exception
e
)
{
Log
.
e
(
GeoPackageManagerImpl
.
class
.
getSimpleName
(
)
,
"Failed to validate database"
,
e
)
;
}
finally
{
sqlite
.
close
(
)
;
}
}
return
valid
;
}

Validate the GeoPackage database

@param database
@param validateHeader    true to validate the header of the database
@param validateIntegrity true to validate the integrity of the database
@return true if valid


private void validateDatabaseAndCloseOnError(SQLiteDatabase sqliteDatabase, boolean validateHeader, boolean validateIntegrity) {
        validateDatabase(sqliteDatabase, validateHeader, validateIntegrity, false, true);
    }

private void validateDatabaseAndCloseOnError ( SQLiteDatabase sqliteDatabase , boolean validateHeader , boolean validateIntegrity ) { validateDatabase ( sqliteDatabase , validateHeader , validateIntegrity , false , true ) ; }

Validate the database and close when validation fails. Throw an error when not valid.

@param sqliteDatabase    database
@param validateHeader    validate the header
@param validateIntegrity validate the integrity

Validate the database and close when validation fails . Throw an error when not valid .


private void validateDatabaseAndClose(SQLiteDatabase sqliteDatabase, boolean validateHeader, boolean validateIntegrity) {
        validateDatabase(sqliteDatabase, validateHeader, validateIntegrity, true, true);
    }

private void validateDatabaseAndClose ( SQLiteDatabase sqliteDatabase , boolean validateHeader , boolean validateIntegrity ) { validateDatabase ( sqliteDatabase , validateHeader , validateIntegrity , true , true ) ; }

Validate the database and close it. Throw an error when not valid.

@param sqliteDatabase    database
@param validateHeader    validate the header
@param validateIntegrity validate the integrity

Validate the database and close it . Throw an error when not valid .


private void validateDatabase(SQLiteDatabase sqliteDatabase, boolean validateHeader, boolean validateIntegrity, boolean close, boolean closeOnError) {
        try {
            if (validateHeader) {
                validateDatabaseHeader(sqliteDatabase);
            }
            if (validateIntegrity) {
                validateDatabaseIntegrity(sqliteDatabase);
            }
        } catch (Exception e) {
            if (closeOnError) {
                sqliteDatabase.close();
            }
            throw e;
        }

        if (close) {
            sqliteDatabase.close();
        }
    }

private void validateDatabase ( SQLiteDatabase sqliteDatabase , boolean validateHeader , boolean validateIntegrity , boolean close , boolean closeOnError ) { try { if ( validateHeader ) { validateDatabaseHeader ( sqliteDatabase ) ; } if ( validateIntegrity ) { validateDatabaseIntegrity ( sqliteDatabase ) ; } } catch ( Exception e ) { if ( closeOnError ) { sqliteDatabase . close ( ) ; } throw e ; } if ( close ) { sqliteDatabase . close ( ) ; } }

Validate the database header and integrity.  Throw an error when not valid.

@param sqliteDatabase    database
@param validateHeader    validate the header
@param validateIntegrity validate the integrity
@param close             close the database after validation
@param closeOnError      close the database if validation fails

Validate the database header and integrity . Throw an error when not valid .


private void validateDatabaseHeader(SQLiteDatabase sqliteDatabase) {

        boolean validHeader = isDatabaseHeaderValid(sqliteDatabase);
        if (!validHeader) {
            throw new GeoPackageException(
                    "GeoPackage SQLite header is not valid: " + sqliteDatabase.getPath());
        }
    }

Validate the header of the database file to verify it is a sqlite database

@param sqliteDatabase database