locationtech / geowave

GeoWave provides geospatial and temporal indexing on top of Accumulo, HBase, BigTable, Cassandra, Kudu, Redis, RocksDB, and DynamoDB.
Apache License 2.0
502 stars 190 forks source link

Use Geowave to quickly get the sum of field in SimpleFeature #1524

Closed scially closed 5 years ago

scially commented 5 years ago

i have some SimpleFeature

        SimpleFeatureTypeBuilder sftb = new SimpleFeatureTypeBuilder();
        sftb.setName("PointSimpleFeatureType");
        sftb.add("name",String.class);
        sftb.add("area", Integer.class);
        sftb.add("the_geom", Geometry.class);

and i use geowave wirte this SimleFeature to HBase, and i query the Statistics, and i only get the histogram about area field, and can i quickly get the sum and average?
If i can't get through Statistics, is there any other way?

rfecher commented 5 years ago

There are ways to inject your own implementation of Statistics which probably would simply suit you best in this case. The brute force way would be to extend FeatureDataAdapter with your own adapter that adds or modifies the typical stats. These are the relevant methods. However, you can also do it through configuration. This doc describes the method of using a JSON file on ingest, and then there's a section within the example at the bottom that assigns specific statistics to that feature type. The latter is probably best, but of course for either of these approaches you'll need to make sure any classes that you write are on the classpath in any context you are running geowave (to include the distributed classpath if you're using serverside processing).

scially commented 5 years ago

thanks.. i solve it.

rfecher commented 5 years ago

great to hear!

scially commented 5 years ago

shared my code....

  1. write code
    
    package cn.gzpi.gxngis.adapter;

import org.locationtech.geowave.core.index.Mergeable; import org.locationtech.geowave.core.store.adapter.statistics.AbstractDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.FieldStatisticsQueryBuilder; import org.locationtech.geowave.core.store.adapter.statistics.FieldStatisticsType; import org.locationtech.geowave.core.store.entities.GeoWaveRow; import org.opengis.feature.simple.SimpleFeature;

import java.nio.ByteBuffer;

public class SumDataStatistics extends AbstractDataStatistics<SimpleFeature, Double, FieldStatisticsQueryBuilder> {

public static final FieldStatisticsType<Double> STATS_TYPE = new FieldStatisticsType<>(
        "FEATURE_NUMERIC_SUM");

private Double sum = 0d;
private boolean isNumber = false;

protected SumDataStatistics() {
    super();
}

public SumDataStatistics(String fieldName){
    this(null,
            fieldName);
}

public SumDataStatistics(
        final Short internalDataAdapterId,
        final String fieldName ) {
    super(
            internalDataAdapterId,
            STATS_TYPE,
            fieldName);
}
@Override
protected String resultsName() {
    return "sum";
}

@Override
protected Object resultsValue() {
    return sum;
}

@Override
public Double getResult() {
    return sum;
}

@Override
public void merge(Mergeable merge) {
    if(merge != null && merge instanceof SumDataStatistics){
        SumDataStatistics statistics = (SumDataStatistics)merge;
        this.sum += statistics.sum;
    }
}

/**
 * Convert fields and data within an object to binary form for transmission
 * or storage.
 *
 * @return an array of bytes representing a binary stream representation of
 * the object.
 */
@Override
public byte[] toBinary() {
    final ByteBuffer buffer = super.binaryBuffer(8);
    buffer.putDouble(sum);
    return buffer.array();
}

/**
 * Convert a stream of binary bytes to fields and data within an object.
 *
 * @param bytes
 */
@Override
public void fromBinary(byte[] bytes) {
    final ByteBuffer buffer = super.binaryBuffer(bytes);
    sum = buffer.getDouble();
}

/**
 * This will be called after an entry is successfully ingested with the row
 * IDs that were used
 *
 * @param entry the entry that was ingested
 * @param rows
 */
@Override
public void entryIngested(SimpleFeature entry, GeoWaveRow... rows) {
    Object attribute = entry.getAttribute(getExtendedId());
    // may be some simple feature doesn't have this field
    if(null!= attribute && Number.class.isAssignableFrom(attribute.getClass())){
        Number num = (Number)attribute;
        sum += num.doubleValue();
        isNumber = true;
    }else{
        isNumber = false;
    }
}

public boolean isNumber() {
    return isNumber;
}

@Override
public String toString() {
    return "SumDataStatistics[" +
            "sum=" + sum +
            ", statisticsType=" + statisticsType +
            ", extendedId='" + extendedId + '\'' +
            ']';
}

}

2. register this class to geowave

package cn.gzpi.gxngis.adapter;

import cn.gzpi.gxngis.adapter.FeatureDataAdapterEx; import org.locationtech.geowave.core.index.persist.PersistableRegistrySpi;

public class FeatureAdapterExPersistableRegistry implements PersistableRegistrySpi {

@Override
public PersistableIdAndConstructor[] getSupportedPersistables() {
    return new PersistableIdAndConstructor[]{
            new PersistableIdAndConstructor(
                    (short) 1000,
                    FeatureDataAdapterEx::new),
            new PersistableIdAndConstructor(
                    (short) 1001,
                    SumDataStatistics::new)
    };
}

}


3. expand FeatureDataAdapter
rfecher commented 5 years ago

thanks for sharing!