=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java 2014-03-24 09:51:04 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/organisationunit/OrganisationUnitService.java 2014-04-17 15:52:45 +0000 @@ -390,10 +390,37 @@ * * @return collection of objects. */ - Collection getWithinCoordinateArea( double longitude, double latitude, double distance ); - - boolean isInUserHierarchy( OrganisationUnit organisationUnit ); - + Collection getOrganisationUnitWithinDistance( double longitude, double latitude, double distance ); + + /** + * Retrieves the orgunit(s) by coordinate. + * + * @param longitude The longitude of the location. + * @param latitude The latitude of the location. + * @param topOrgUnitUid Optional. Uid of the search top level org unit (ex. + * Country level orgunit) + * @param targetLevel Optional. The level being searched. + * + * @return collection of objects. + */ + Collection getOrganisationUnitByCoordinate( double longitude, double latitude, String topOrgUnitUid, + Integer targetLevel ); + + + /** + * find the orgunit(s) that contains the coordinate. + * + * @param source orgunits to search for. + * @param longitude The longitude of the location. + * @param latitude The latitude of the location. + * + * @return collection of objects. + */ + Collection filterOrganisationUnitsByCoordinate( Collection orgUnits_source, + double longitude, double latitude ); + + boolean isInUserHierarchy( OrganisationUnit organisationUnit ); + // ------------------------------------------------------------------------- // OrganisationUnitHierarchy // ------------------------------------------------------------------------- === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java 2014-04-17 04:48:57 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/organisationunit/DefaultOrganisationUnitService.java 2014-04-17 15:52:45 +0000 @@ -846,12 +846,12 @@ return organisationUnitLevelStore.getMaxLevels(); } - public Collection getWithinCoordinateArea( double longitude, double latitude, double distance ) + + public Collection getOrganisationUnitWithinDistance( double longitude, double latitude, double distance ) { Collection objects = organisationUnitStore.getWithinCoordinateArea( GeoUtils.getBoxShape( longitude, latitude, distance ) ); - // Go through the list and remove the ones located outside radius - + // Go through the list and remove the ones located farther than the distance. if ( objects != null && objects.size() > 0 ) { Iterator iter = objects.iterator(); @@ -867,6 +867,8 @@ if ( distancebetween > distance ) { + // Remove the orgUnits that is outside of the distance range + // - due to the 'getWithinCoordinateArea' looking at square area instead of circle. iter.remove(); } } @@ -875,6 +877,158 @@ return objects; } + + public Collection getOrganisationUnitByCoordinate( double longitude, double latitude, String topOrgUnitUid, + Integer targetLevel ) + { + Collection orgUnits = new ArrayList(); + + if ( GeoUtils.checkGeoJsonPointValid( longitude, latitude ) ) + { + + OrganisationUnit topOrgUnit = null; + + // 1. Get top org unit of search. + if ( topOrgUnitUid != null && topOrgUnitUid != "" ) + { + topOrgUnit = getOrganisationUnit( topOrgUnitUid ); + } + else + { + // Get top orgunit that contains the coordinate - to get a top + // search point. + Collection orgUnits_TopLevel = getTopLevelOrgUnitWithPoint( longitude, latitude, 1, + getNumberOfOrganisationalLevels() - 1 ); + + if ( orgUnits_TopLevel.size() == 1 ) + { + topOrgUnit = orgUnits_TopLevel.iterator().next(); + } + else + { + System.out.print( "Multiple Top Level Org Unit found with the coordinate: " ); + + for ( OrganisationUnit ou : orgUnits_TopLevel ) + { + System.out.print( " [" + ou.getLevel() + "] " + ou.getName() + " " + ou.getUid() + " " ); + } + + System.out.println( "" ); + } + + } + + // 2. From the found top level orgunit, search children orgunits to + // get the lowest level orgunit that contains the coordinate + if ( topOrgUnit == null ) + { + System.out.println( "Could not retrieve a top org Unit." ); + } + else + { + Collection orgUnits_childrens = new ArrayList(); + + if ( targetLevel != null ) + { + orgUnits_childrens = getOrganisationUnitsAtLevel( targetLevel, topOrgUnit ); + } + else + { + orgUnits_childrens = getOrganisationUnitWithChildren( topOrgUnit.getId() ); + } + + Collection orgUnits_found = filterOrganisationUnitsByCoordinate( orgUnits_childrens, + longitude, latitude ); + + // 3. for all the org units with polygon containing the coordinate + // , get polygon with highest level + + int bottomLvl = topOrgUnit.getLevel(); + + for ( OrganisationUnit ou : orgUnits_found ) + { + + if ( ou.getLevel() > bottomLvl ) + { + bottomLvl = ou.getLevel(); + } + } + + for ( OrganisationUnit ou : orgUnits_found ) + { + if ( ou.getLevel() == bottomLvl ) + { + System.out.println( "Bottom Lvl OU Found: [" + ou.getLevel() + "], " + ou.getName() + ", " + + ou.getUid() ); + + orgUnits.add( ou ); + } + } + } + + } + else + { + System.out.println( "Could not create a valid GeoJson Point from coordinate. Longitude: " + longitude + + ", latitude: " + latitude ); + } + + return orgUnits; + } + + + public Collection filterOrganisationUnitsByCoordinate( Collection orgUnits_source, + double longitude, double latitude ) + { + Collection orgUnits = new ArrayList(); + + for ( OrganisationUnit ou : orgUnits_source ) + { + String ou_featureType = ou.getFeatureType(); + String ou_coordinate = ou.getCoordinates(); + + if ( ou_featureType != null + && ou_coordinate != null + && !ou_coordinate.isEmpty() + && (ou_featureType.compareTo( OrganisationUnit.FEATURETYPE_POLYGON ) == 0 || ou_featureType + .compareTo( OrganisationUnit.FEATURETYPE_MULTIPOLYGON ) == 0) ) + { + if ( GeoUtils.checkPointWithMultiPolygon( longitude, latitude, ou.getCoordinates(), ou_featureType ) ) + { + orgUnits.add( ou ); + System.out.println( "Location Match OU: [" + ou.getLevel() + "], " + ou.getName() + ", " + + ou.getUid() ); + } + } + } + + return orgUnits; + } + + private Collection getTopLevelOrgUnitWithPoint( double longitude, double latitude, int searchLvl, + int stopLvl ) + { + Collection orgUnits = new ArrayList(); + + // Search down the level until it finds a orgunit with + // polygon/multipolygon that contains the point + + for ( int i = searchLvl; i <= stopLvl; i++ ) + { + Collection orgUnits_onLevel = filterOrganisationUnitsByCoordinate( + getOrganisationUnitsAtLevel( i ), longitude, latitude ); + + if ( orgUnits_onLevel.size() > 0 ) + { + orgUnits = orgUnits_onLevel; + break; + } + } + + return orgUnits; + } + + // ------------------------------------------------------------------------- // Version // ------------------------------------------------------------------------- === modified file 'dhis-2/dhis-support/dhis-support-system/pom.xml' --- dhis-2/dhis-support/dhis-support-system/pom.xml 2014-03-24 09:51:04 +0000 +++ dhis-2/dhis-support/dhis-support-system/pom.xml 2014-04-17 15:52:45 +0000 @@ -59,6 +59,11 @@ org.geotools gt-referencing + + org.geotools + gt-geojson + 10.4 + === modified file 'dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/GeoUtils.java' --- dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/GeoUtils.java 2014-03-24 09:51:04 +0000 +++ dhis-2/dhis-support/dhis-support-system/src/main/java/org/hisp/dhis/system/util/GeoUtils.java 2014-04-17 15:52:45 +0000 @@ -29,8 +29,15 @@ */ import java.awt.geom.Point2D; +import java.io.StringReader; +import org.geotools.geojson.geom.GeometryJSON; import org.geotools.referencing.GeodeticCalculator; +import org.hisp.dhis.organisationunit.OrganisationUnit; + +import com.vividsolutions.jts.geom.MultiPolygon; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.geom.Polygon; /** * @author Lars Helge Overland @@ -93,5 +100,77 @@ calc.setDestinationGeographicPoint( to); return calc.getOrthodromicDistance(); - } + } + + private static Point getGeoJsonPoint( double longitude, double latitude ) + { + Point point = null; + + try + { + GeometryJSON gtjson = new GeometryJSON(); + + point = gtjson.readPoint( new StringReader( "{\"type\":\"Point\", \"coordinates\":[" + longitude + "," + + latitude + "]}" ) ); + } + catch ( Exception ex ) + { + System.out.println( "Error during GeoJson point create - " + ex.getMessage() ); + } + + return point; + } + + public static boolean checkGeoJsonPointValid( double longitude, double latitude ) + { + return getGeoJsonPoint( longitude, latitude ).isValid(); + } + + // Check if the point is within the polygon/multiPolygon + public static boolean checkPointWithMultiPolygon( double longitude, double latitude, String multiPolygonJson, + String featureType ) + { + boolean contains = false; + + GeometryJSON gtjson = new GeometryJSON(); + + // Note: Could create this point once from calling package and reuse it + // , but then, each calling package need to reference + // 'com.vividsolutions.jts.geom.Point' + Point point = getGeoJsonPoint( longitude, latitude ); + + if ( point != null && point.isValid() ) + { + try + { + if ( featureType.compareTo( OrganisationUnit.FEATURETYPE_POLYGON ) == 0 ) + { + Polygon polygon = gtjson.readPolygon( new StringReader( "{\"type\":\"Polygon\", \"coordinates\":" + + multiPolygonJson + "}" ) ); + + contains = polygon.contains( point ); + } + else if ( featureType.compareTo( OrganisationUnit.FEATURETYPE_MULTIPOLYGON ) == 0 ) + { + MultiPolygon multiPolygon = gtjson.readMultiPolygon( new StringReader( + "{\"type\":\"MultiPolygon\", \"coordinates\":" + multiPolygonJson + "}" ) ); + + contains = multiPolygon.contains( point ); + } + + } + catch ( Exception exception ) + { + System.out.println( "Error during GeoJson MultiPolygon create - " + exception.toString() ); + } + + } + else + { + System.out.println( "Point coordinate is not valid. Longitude: " + longitude + ", latitude: " + latitude ); + } + + return contains; + } + } === modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/organisationunit/OrganisationUnitController.java' --- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/organisationunit/OrganisationUnitController.java 2014-03-26 12:33:30 +0000 +++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/api/controller/organisationunit/OrganisationUnitController.java 2014-04-17 15:52:45 +0000 @@ -58,6 +58,7 @@ import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Collections; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -270,12 +271,12 @@ } @RequestMapping(value = "/withinRange", method = RequestMethod.GET, produces = { "*/*", "application/json" }) - public void getEntitiesWithinRange( @RequestParam Double longitude, @RequestParam Double latitude, + public void getEntitiesWithinRange( @RequestParam Double longitude, @RequestParam Double latitude, @RequestParam Double distance, @RequestParam(required = false) String orgUnitGroupSetId, Model model, HttpServletRequest request, HttpServletResponse response ) throws Exception { - List entityList = new ArrayList( organisationUnitService.getWithinCoordinateArea( longitude, latitude, distance ) ); - + List entityList = new ArrayList( organisationUnitService.getOrganisationUnitWithinDistance( longitude, latitude, distance ) ); + for ( OrganisationUnit orgunit : entityList ) { Set attributeValues = orgunit.getAttributeValues(); @@ -311,4 +312,81 @@ JacksonUtils.toJson( response.getOutputStream(), entityList ); } + + @RequestMapping( value = "/orgUnitByLocation", method = RequestMethod.GET, produces = { "*/*", "application/json" } ) + public void getParentByLocation( @RequestParam + Double longitude, @RequestParam + Double latitude, @RequestParam + Map parameters, Model model, HttpServletRequest request, HttpServletResponse response ) + throws Exception + { + + // 1. Get parameters + WebOptions options = new WebOptions( parameters ); + + String topOrgUnit = null; + Integer targetLevel = null; + + topOrgUnit = options.getOptions().get( "topOrgUnit" ); + + if ( options.getOptions().containsKey( "targetLevel" ) ) + { + try + { + targetLevel = Integer.parseInt( options.getOptions().get( "targetLevel" ) ); + } + catch ( NumberFormatException ignored ) + { + } + } + + // 2. Retrieve list - lowest level orgunit(s) containing the coordinate. + List entityList = new ArrayList( + organisationUnitService.getOrganisationUnitByCoordinate( longitude, latitude, topOrgUnit, targetLevel ) ); + + // 3. Remove unrelated details and output in JSON format. + for ( OrganisationUnit orgunit : entityList ) + { + Set attributeValues = orgunit.getAttributeValues(); + attributeValues.clear(); + + // Clear out all data not needed for this task + orgunit.removeAllDataSets(); + orgunit.removeAllUsers(); + orgunit.removeAllOrganisationUnitGroups(); + } + + JacksonUtils.toJson( response.getOutputStream(), entityList ); + } + + @RequestMapping( value = "/locationWithinOrgUnitBoundary", method = RequestMethod.GET, produces = { "*/*", "application/json" } ) + public void checkLocationWithinOrgUnit( @RequestParam String orgUnitUid + , @RequestParam Double longitude, @RequestParam Double latitude + , Model model, HttpServletRequest request, HttpServletResponse response ) + throws Exception + { + + boolean withinOrgUnit = false; + + Collection orgUnits = new ArrayList(); + + // Reuse 'filterOrganisationUnitsByCoordinate' Method for this. + // Send orgUnit collection with one orgUnit to 'getOrganisationUnitByLocation'. + // If it returns orgUnit collection with one orgUnit, it means the orgUnit sent as collection + // has the coordinate within that orgUnit. + + orgUnits.add( organisationUnitService.getOrganisationUnit( orgUnitUid ) ); + + Collection orgUnits_found = organisationUnitService.filterOrganisationUnitsByCoordinate( orgUnits, longitude, latitude ); + + if ( orgUnits_found.size() == 1 ) + { + withinOrgUnit = true; + } + + JacksonUtils.toJson( response.getOutputStream(), withinOrgUnit ); + } + + + }