/repast.simphony/tags/RS-2.0b/repast.simphony.core/src/repast/simphony/space/gis/ShapefileLoader.java

# · Java · 257 lines · 177 code · 23 blank · 57 comment · 30 complexity · 8f15069cc644cf6e7d41a539faad6a95 MD5 · raw file

  1. package repast.simphony.space.gis;
  2. import org.geotools.data.shapefile.ShapefileDataStore;
  3. import org.geotools.feature.*;
  4. import org.geotools.geometry.jts.JTS;
  5. import org.geotools.referencing.CRS;
  6. import org.geotools.referencing.FactoryFinder;
  7. import org.opengis.referencing.FactoryException;
  8. import org.opengis.referencing.operation.MathTransform;
  9. import org.opengis.referencing.operation.OperationNotFoundException;
  10. import org.opengis.referencing.operation.TransformException;
  11. import repast.simphony.context.Context;
  12. import simphony.util.messages.MessageCenter;
  13. import java.beans.BeanInfo;
  14. import java.beans.IntrospectionException;
  15. import java.beans.Introspector;
  16. import java.beans.PropertyDescriptor;
  17. import java.io.IOException;
  18. import java.lang.reflect.Constructor;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.lang.reflect.Method;
  21. import java.net.URL;
  22. import java.util.HashMap;
  23. import java.util.Map;
  24. /**
  25. * Creates and sets agents properties from a features in shapefile.
  26. *
  27. * @author Nick Collier
  28. */
  29. public class ShapefileLoader<T> {
  30. private static final MessageCenter msg = MessageCenter.getMessageCenter(ShapefileLoader.class);
  31. private static Map<Class, Class> primToObject = new HashMap<Class, Class>();
  32. static {
  33. primToObject.put(int.class, Integer.class);
  34. primToObject.put(long.class, Long.class);
  35. primToObject.put(double.class, Double.class);
  36. primToObject.put(float.class, Float.class);
  37. primToObject.put(boolean.class, Boolean.class);
  38. primToObject.put(byte.class, Byte.class);
  39. primToObject.put(char.class, Character.class);
  40. }
  41. private MathTransform transform;
  42. private Geography geography;
  43. private Context context;
  44. private Map<String, Method> attributeMethodMap = new HashMap<String, Method>();
  45. private Class agentClass;
  46. private FeatureIterator iter;
  47. /**
  48. * Creates a shapefile loader for agents of the specified
  49. * class and whose data source is the specified shapefile. The
  50. * agents will be placed into the specified Geography according to
  51. * the geometry specified in the shapefile and transformed according
  52. * to the geography's CRS.
  53. *
  54. * @param clazz the agent class
  55. * @param shapefile the shapefile that serves as the datasource for the agent
  56. * properties
  57. * @param geography the geography to hold spatial locations of the agents
  58. * @param context the context to add the agents to
  59. */
  60. public ShapefileLoader(Class<T> clazz, URL shapefile, Geography geography, Context context) {
  61. this.geography = geography;
  62. this.agentClass = clazz;
  63. this.context = context;
  64. try {
  65. BeanInfo info = Introspector.getBeanInfo(clazz, Object.class);
  66. Map<String, Method> methodMap = new HashMap<String, Method>();
  67. PropertyDescriptor[] pds = info.getPropertyDescriptors();
  68. for (PropertyDescriptor pd : pds) {
  69. if (pd.getWriteMethod() != null) {
  70. methodMap.put(pd.getName().toLowerCase(), pd.getWriteMethod());
  71. }
  72. }
  73. ShapefileDataStore store = new ShapefileDataStore(shapefile);
  74. FeatureType schema = store.getSchema(store.getTypeNames()[0]);
  75. for (int i = 0, n = schema.getAttributeCount(); i < n; i++) {
  76. AttributeType type = schema.getAttributeType(i);
  77. String name = type.getName();
  78. if (name.equals("the_geom")) {
  79. initTransform(geography, type);
  80. } else {
  81. Method method = methodMap.get(name.toLowerCase());
  82. if (method == null) method = methodMap.get(name.replace("_", "").toLowerCase());
  83. if (method != null && isCompatible(method.getParameterTypes()[0], (type.getType()))) {
  84. attributeMethodMap.put(name, method);
  85. }
  86. }
  87. }
  88. iter = store.getFeatureSource().getFeatures().features();
  89. } catch (IntrospectionException ex) {
  90. msg.error("Error while introspecting class", ex);
  91. } catch (IOException e) {
  92. msg.error(String.format("Error opening shapefile '%S'", shapefile), e);
  93. } catch (FactoryException e) {
  94. msg.error(String.format("Error creating transform between shapefile CRS and Geography CRS"), e);
  95. }
  96. }
  97. private boolean isCompatible(Class methodParam, Class attributeType) {
  98. if (methodParam.equals(attributeType)) return true;
  99. Class clazz = primToObject.get(methodParam);
  100. if (clazz != null) return clazz.equals(attributeType);
  101. return false;
  102. }
  103. private void initTransform(Geography geography, AttributeType type) throws FactoryException {
  104. GeometryAttributeType gType = (GeometryAttributeType) type;
  105. if (geography != null) {
  106. try {
  107. transform = FactoryFinder.getCoordinateOperationFactory(null).createOperation(gType.getCoordinateSystem(),
  108. geography.getCRS()).getMathTransform();
  109. } catch (OperationNotFoundException ex) {
  110. // bursa wolf params may be missing so try lenient.
  111. transform = CRS.transform(gType.getCoordinateSystem(), geography.getCRS(), true);
  112. }
  113. }
  114. }
  115. /**
  116. * Creates all the agents for the shapefile features,
  117. * setting each agent's properteis to the value of a
  118. * feature's relevant attributes.
  119. */
  120. public void load() {
  121. while (hasNext()) next();
  122. }
  123. /**
  124. * Creates the next agent from the next feature
  125. * in the shapefile, setting that agent's properties to the value of
  126. * that feature's relevant attributes.
  127. *
  128. * @return the created agent
  129. */
  130. public T next() {
  131. T obj = null;
  132. try {
  133. obj = (T) agentClass.newInstance();
  134. obj = processNext(obj);
  135. } catch (InstantiationException e) {
  136. msg.error("Error creating agent instance from class", e);
  137. } catch (IllegalAccessException e) {
  138. msg.error("Error creating agent instance from class", e);
  139. }
  140. return obj;
  141. }
  142. private T processNext(T obj) {
  143. try {
  144. Feature feature = iter.next();
  145. obj = fillAgent(feature, obj);
  146. if (!context.contains(obj)) context.add(obj);
  147. if (geography != null) geography.move(obj, JTS.transform(feature.getDefaultGeometry(), transform));
  148. return obj;
  149. } catch (IllegalAccessException e) {
  150. msg.error("Error setting agent property from feature attribute", e);
  151. } catch (InvocationTargetException e) {
  152. msg.error("Error setting agent property from feature attribute", e);
  153. } catch (TransformException e) {
  154. msg.error("Error transforming feature geometry to geography's CRS", e);
  155. }
  156. return null;
  157. }
  158. /**
  159. * Creates the next agent from the next feature
  160. * in the shapefile, setting that agent's properties to the value of
  161. * that feature's relevant attributes. Note that the constructor
  162. * matching is somewhat naive and looks for exact matches.
  163. *
  164. * @param constructorArgs parameters to pass to the constructor when
  165. * creating the agent.
  166. * @return the created agent
  167. * @throws IllegalArgumentException if the constructor args don't match a
  168. * constructor.
  169. */
  170. public T nextWithArgs(Object... constructorArgs) throws IllegalArgumentException {
  171. Constructor constructor = findConstructor(constructorArgs);
  172. if (constructor == null) throw new IllegalArgumentException("Unable to find matching constructor for arguments");
  173. try {
  174. T obj = (T) constructor.newInstance(constructorArgs);
  175. return processNext(obj);
  176. } catch (InstantiationException e) {
  177. msg.error("Error creating agent instance from class", e);
  178. } catch (IllegalAccessException e) {
  179. msg.error("Error creating agent instance from class", e);
  180. } catch (InvocationTargetException e) {
  181. msg.error("Error creating agent instance from class", e);
  182. }
  183. return null;
  184. }
  185. /**
  186. * Sets the specified object's properties to the relevant
  187. * attributes values of the next feature.
  188. *
  189. * @param obj the object whose properties we want to set.
  190. * @return the object with is properties now set.
  191. */
  192. public T next(T obj) {
  193. return processNext(obj);
  194. }
  195. private Constructor findConstructor(Object[] constructorArgs) {
  196. Class[] args = new Class[constructorArgs.length];
  197. int i = 0;
  198. for (Object obj : constructorArgs) {
  199. args[i++] = obj.getClass();
  200. }
  201. for (Constructor constructor : agentClass.getConstructors()) {
  202. Class[] params = constructor.getParameterTypes();
  203. if (params.length == args.length) {
  204. boolean pass = true;
  205. for (i = 0; i < args.length; i++) {
  206. if (!isCompatible(params[i], args[i])) {
  207. pass = false;
  208. break;
  209. }
  210. }
  211. if (pass) return constructor;
  212. }
  213. }
  214. return null;
  215. }
  216. /**
  217. * Returns true if there are more features left to process,
  218. * otherwise false.
  219. *
  220. * @return true if there are more features left to process,
  221. * otherwise false.
  222. */
  223. public boolean hasNext() {
  224. return iter.hasNext();
  225. }
  226. private T fillAgent(Feature feature, T agent) throws IllegalAccessException, InvocationTargetException {
  227. for (String attribName : attributeMethodMap.keySet()) {
  228. Object val = feature.getAttribute(attribName);
  229. Method write = attributeMethodMap.get(attribName);
  230. write.invoke(agent, val);
  231. }
  232. return agent;
  233. }
  234. }