GDAL矢量驱动拓展

GDAL

GDAL文档
GDAL下载
GDAL开发依赖下载
GDAL是一个用于栅格和矢量地理空间数据格式的转换库,采用MIT开源许可,由开源地理空间基金会发布。它为调用程序提供了栅格抽象数据模型和矢量抽象数据模型并包含所有支持的格式。它还提供了一些实用的命令行程序用于数据转换和处理。

OGR

OGR是GDAL项目的一个分支,功能与GDAL类似,只不过它提供对矢量数据的读写支持,包括ESRI Shapefiles,PostGIS等。

矢量数据模型(Vector Data Model)

OGR为矢量数据提供了矢量数据模型。

类概况

  • 几何图形(ogr_geometry.h):几何类(OGRGeometry等),封装了OpenGIS模型矢量数据并提供一些几何操作,以及对WKB和WKT的转换。几何图形包含了空间参考系。


    geometry模型.png
  • 空间参考(ogr_spatialref.h):OGRSpatialRefence类,封装了投影和基准的定义。
  • 要素(ogr_feature.h):OGRFeature类,封装了feature的定义,包括一个几何和属性。
  • 要素类定义(ogr_feature.h):OGRFeatureDefn类,将一组相关要素(一个Layer)的概要数据进行封装,包括了集合类型、名称、属性域定义等。
  • 图层(ogrsf_frmts.h):OGRLayer类,一个抽象基类,代表了GDALDataset中的一个要素图层。
  • 数据集(gdal_priv.h):GDALDataset类,包含一个或多个OGRLayer对象的类,其通常代表一个文件或数据库。
  • 驱动(gdal_priv.h):GDALDriver类,为特定格式提供驱动,用以打开特定格式的数据得到GDALDataset,如ESRI Shapefile、PostGIS。所有的驱动都被GDALDriverManager管理。

体系架构:


体系架构.png

矢量驱动程序实现

  • 在程序中实现矢量驱动时,程序版本需与gdal库版本一致,如release版的gdal库,编译程序时也应为release版。

通过实现特定格式的驱动,来为OGR加入新的格式支持。这需要实例化GDALDriver类并实现GDALDatasetOGRLayer的子类。GDALDriver实例需要在运行时通过GDALDriverManager进行注册。

1.实现GDALDriver

特定格式的驱动程序类是作为GDALDriver的实例实现的。通常会创建一个GDALDriver实例并通过GDALDeiverManager进行注册。而实例化过程由一个全局C函数进行处理。该函数由自己实现并在内部实例化一个GDALDriver对象。

void registerOGRSPF()
{
    if( GDALGetDriverByName("SPF") != NULL )
        return;

    GDALDriver *poDriver = new GDALDriver();

    poDriver->SetDescription("SPF");
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Long name for SPF driver");
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "spf");
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drv_spf.html");

    // 为GDALDriver设置函数指针
    poDriver->pfnOpen = OGRSPFDriverOpen;
    poDriver->pfnIdentify = OGRSPFDriverIdentify;
    poDriver->pfnCreate = OGRSPFDriverCreate;

    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");

    GetGDALDriverManager()->RegisterDriver(poDriver);
}

SetDescription("SPF")指定了驱动程序的名称。
SetMetadataItem( const char * pszName, const char * pszValue)设置驱动的元数据信息:

  • GDAL_DCAP_VECTOR:设置为"YES"表示驱动程序将处理矢量数据。
  • GDAL_DCAP_VIRTUALIO:设置为"YES"表示驱动程序可以处理使用VSI*L GDAL API打开的文件。否则不应定义此元数据项。
  • GDAL_DMD_LONGNAME:文件格式的详细描述,长度一般在50-60个字符。(手工设置)
  • GDAL_DMD_HELPTOPIC:URL,相对于http://gdal.ogr/如果有的话, 是关于这个驱动帮助文档的名称。(可选)
  • GDAL_DMD_EXTENSION:该类型文件的扩展名。如果扩展名多于一个,则最好选择最通用的那个,或者直接为空。(可选)
  • GDAL_DMD_MIMETYPE:该格式数据的标准用途类型,例如“image/png”。(可选)
  • GDAL_DMD_CREATIONOPTIONLIST:用于描述创建时的选项。可以参考geotiff驱动的实现代码。(可选)
  • GDAL_DMD_CREATIONDATATYPES:支持创建数据集的全部类型列表。如果存在Create()方法这些类型都将被支持。如果存在CreateCopy()方法,该类型列表将是那些支持无损输出的类型。比如,一个格式使用了CreateCopy()方法,如果可以写为Float32类型,那么Byte、Int16和UInt16都应该被支持(因为它们都可以用Float32表示)

将自定义的实现函数赋值给GDALDriver的函数指针,通过调用GDALDriver的函数指针,以下函数被具体执行:

static GDALDataset* OGRSPFDriverOpen(GDALOpenInfo* poOpenInfo);
static int          OGRSPFDriverIdentify(GDALOpenInfo* poOpenInfo);
static GDALDataset* OGRSPFDriverCreate(const char* pszName, int nXSize, int nYSize,
                                    int nBands, GDALDataType eDT, char** papszOptions);

Open()方法用于打开数据源并返回一个GDALDataset实例,通常被委托给实际的GDALDataset子类,即在内部调用GDALDataset子类实例的Open()方法。

static GDALDataset *OGRSPFDriverOpen( GDALOpenInfo* poOpenInfo )
{
    if( !OGRSPFDriverIdentify(poOpenInfo) )
        return NULL;

    OGRSPFDataSource *poDS = new OGRSPFDataSource();
    if( !poDS->Open(poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update) )
    {
        delete poDS;
        return NULL;
    }

    return poDS;
}

Identify()方法可以对数据源进行校验,判断是否是合格数据源。

static int OGRSPFDriverIdentify( GDALOpenInfo* poOpenInfo )
{
    // Does this appear to be an .spf file?
    return EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "spf");
}

create()在用于创建数据,如该驱动用于驱动文件类型的数据时,则可在create()中实现文件的创建。

2.实现GDALDataset子类

GDALDataset子类以一个vector保存多个Layer数据。

class OGRSPFDataSource : public GDALDataset
{
    std::vector<OGRSPFLayer*>   papoLayers;
    int                 nLayers;

public:
  OGRSPFDataSource();
  ~OGRSPFDataSource();

    int                 Open( const char *pszFilename, int bUpdate );

    int                 GetLayerCount() { return nLayers; }
    OGRLayer            *GetLayer( int );

    int                 TestCapability( const char * ) { return FALSE; }
};

Open()方法将会访问数据源信息并创建OGRLayer的子类对象,在实现OGRLayer的子类时,访问数据源信息,并获取要素数据。

int  OGRSPFDataSource::Open( const char *pszFilename, int bUpdate )
{
    if( bUpdate )
    {
        CPLError(CE_Failure, CPLE_OpenFailed,
                "Update access not supported by the SPF driver.");
        return FALSE;
    }

    // Create a corresponding layer.s
    nLayers = 1;

    papoLayers.append(new OGRSPFLayer(pszFilename));

    pszName = CPLStrdup(pszFilename);

    return TRUE;
}

3.实现OGRLayer子类

自定义OGRSPFLayer类继承自OGRLayer。在内部拥有OGRFeatureDefn成员变量,以便访问该图层的元数据信息。

OGRLayer中的纯虚函数有:

  • virtual void ResetReading() = 0;
  • virtual OGRFeature *GetNextFeature() CPL_WARN_UNUSED_RESULT = 0;
  • virtual OGRFeatureDefn *GetLayerDefn() = 0;
  • virtual int TestCapability( const char * ) = 0;
class OGRSPFLayer : public OGRLayer
{
    OGRFeatureDefn     *poFeatureDefn;
    FILE               *fp;
    int                 nNextFID;

public:
    OGRSPFLayer( const char *pszFilename );
    ~OGRSPFLayer();

    void                ResetReading();
    OGRFeature *        GetNextFeature();

    OGRFeatureDefn *    GetLayerDefn() { return poFeatureDefn; }

    int                 TestCapability( const char * ) { return FALSE; }
};

类内部的OGRFeatureDefn将被多个OGRFeature引用,所有在该类的构造函数和析构函数中也应通过OGRFeatureDefn::Reference()OGRFeatureDefn::Release()对OGRFeatureDefn对象进行引用计数的+1和-1

OGRSPFLayer::OGRSPFLayer( const char *pszFilename )
{
    nNextFID = 0;

    poFeatureDefn = new OGRFeatureDefn(CPLGetBasename(pszFilename));
    SetDescription(poFeatureDefn->GetName());
    poFeatureDefn->Reference();
    poFeatureDefn->SetGeomType(wkbPoint);

    OGRFieldDefn oFieldTemplate("Name", OFTString);

    poFeatureDefn->AddFieldDefn(&oFieldTemplate);

    fp = VSIFOpenL(pszFilename, "r");
    if( fp == NULL )
        return;
}
OGRSPFLayer::~OGRSPFLayer()
{
    poFeatureDefn->Release();
    if( fp != NULL )
        VSIFCloseL(fp);
}

重写GetNextFeature(),在函数体内访问数据源获取到每一个feature的信息,并创建OGRFeature对象后返回。

OGRFeature *OGRSPFLayer::GetNextFeature()
{
  /* 从数据源获取到下一个feature的数据 */
  ......
  {
    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);

    poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY));
    poFeature->SetField(0, pszName);
    poFeature->SetFID(nNextFID++);
    if(/*match*/)
    {
      return poFeature;
    }
    else
    {
      delete poFeature;
    }
  }

}

OGRLayer基类拥有成员变量:OGRLayer::m_poFilterGeomOGRLayer::m_poAttrQuery分别对几何和属性进行过滤,以便筛选出符合要求的feature,可以在GetNextFeature()方法中进行判断:

        if( (m_poFilterGeom == NULL ||
            FilterGeometry(poFeature->GetGeometryRef())) &&
            (m_poAttrQuery == NULL ||
            m_poAttrQuery->Evaluate(poFeature)) )
            return poFeature;

        delete poFeature;

重写OGRLayer::ResetReading(),在函数体内重置feature的索引,代表重新获取feature,与OGRLayer::GetNextFeature()联动。

void OGRSPFLayer::ResetReading()
{
    VSIFSeekL(fp, 0, SEEK_SET);
    nNextFID = 0;
}

4.GDALDataset虚函数

重点实现:

  • virtual int GetLayerCount();
    获取图层数量,默认实现返回0。
  • virtual OGRLayer *GetLayer(int iLayer);
    根据下标获取图层,默认实现返回nullptr
  • virtual OGRLayer *GetLayerByName(const char *);
    根据图层名称返回图层,默认实现会根据GetLayerCount()GetLayer()遍历所有图层,并比较图层名后返回。其首先会大小写敏感判断图层名,如果没有符合的图层,会用大小写不敏感去匹配图层名。
  • virtual OGRErr DeleteLayer(int iLayer);
    根据下标删除指定图层,默认实现为空逻辑。
  • virtual void ResetReading();
    重置feature的读取为从第一个feature开始,该函数会影响GetNextFeature()
  • virtual OGRFeature* GetNextFeature( OGRLayer** ppoBelongingLayer, double* pdfProgressPct, GDALProgressFunc pfnProgress, void* pProgressData );
    通过循环图层,获取每个图层的feature,会调用图层的GetNextFeature()方法。
  • virtual int TestCapability( const char * );
    判断该数据源是否有某一功能,默认实现为返回false。传入的字符串有:
 <ul>
  <li> <b>ODsCCreateLayer</b>: True if this datasource can create new layers.<p>
  <li> <b>ODsCDeleteLayer</b>: True if this datasource can delete existing
          layers.<p>
  <li> <b>ODsCCreateGeomFieldAfterCreateLayer</b>: True if the layers of this
          datasource support CreateGeomField() just after layer creation.<p>
  <li> <b>ODsCCurveGeometries</b>: True if this datasource supports curve
          geometries.<p>
  <li> <b>ODsCTransactions</b>: True if this datasource supports (efficient)
          transactions.<p>
  <li> <b>ODsCEmulatedTransactions</b>: True if this datasource supports
          transactions through emulation.<p>
  <li> <b>ODsCRandomLayerRead</b>: True if this datasource has a dedicated
          GetNextFeature() implementation, potentially returning features from
          layers in a non sequential way.<p>
  <li> <b>ODsCRandomLayerWrite</b>: True if this datasource supports calling
         CreateFeature() on layers in a non sequential way.<p>
 </ul>o
  • virtual bool AddFieldDomain(std::unique_ptr<OGRFieldDomain>&& domain, std::string& failureReason);
    向数据集添加字段域,默认实现是空逻辑。
  • virtual bool DeleteFieldDomain(const std::string& name, std::string& failureReason);
    根据名称删除指定的字段域,默认实现是空逻辑。
  • virtual bool UpdateFieldDomain(std::unique_ptr<OGRFieldDomain>&& domain, std::string& failureReason);
    通过替换定义更新一个已经存在的字段域,将通过字段域的名称去匹配,默认实现是空逻辑。
  • virtual OGRLayer *ICreateLayer( const char *pszName, OGRSpatialReference *poSpatialRef = nullptr, OGRwkbGeometryType eGType = wkbUnknown, char ** papszOptions = nullptr );
    创建图层,默认实现是返回nullptr,该接口为protected。

非重点实现:

  • virtual void FlushCache(bool bAtClosing = false);
    将所有缓存数据写入硬盘,默认实现是调用每个raster bands的FlushCache()和每个Layer的SyncToDisk()
  • virtual const OGRSpatialReference* GetSpatialRef() const;
    获取数据集的空间参考系,默认实现是返回nullptr
  • virtual CPLErr SetSpatialRef(const OGRSpatialReference* poSRS);
    为数据集设置空间参考系,默认实现为空逻辑。
  • virtual CPLErr GetGeoTransform( double * padfTransform );
  • virtual CPLErr SetGeoTransform( double * padfTransform );
  • virtual void *GetInternalHandle( const char * pszHandleName );
    获取内部特定格式的有意义句柄,默认实现返回nullptr
  • virtual GDALDriver *GetDriver(void);
    有默认实现。
  • virtual const char* GetDriverName();
    有默认实现。
  • virtual char **GetFileList(void);
  • virtual const OGRSpatialReference* GetGCPSpatialRef() const;
    返回地理转换控制点的坐标系,默认实现返回nullptr
  • virtual int GetGCPCount();
    获取地理转换控制点数量,默认实现返回0。
  • virtual const GDAL_GCP *GetGCPs();
    默认实现返回nullptr
  • virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, const OGRSpatialReference * poGCP_SRS );
  • virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, int nBufXSize, int nBufYSize, GDALDataType eDT, int nBandCount, int *panBandList, char **papszOptions );
  • virtual CPLErr CreateMaskBand( int nFlagsIn );
  • virtual GDALAsyncReader* BeginAsyncReader(int nXOff, int nYOff, int nXSize, int nYSize, void *pBuf, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nBandCount, int* panBandMap, int nPixelSpace, int nLineSpace, int nBandSpace, char **papszOptions);
  • virtual void EndAsyncReader(GDALAsyncReader *);
  • virtual bool GetRawBinaryLayout(RawBinaryLayout&);
    获取数据集中原始二进制格式的图层,默认实现为返回空。
  • virtual void ClearStatistics();
  • virtual bool IsLayerPrivate(int iLayer) const;
    通过下标判断该图层是否是私有图层,默认实现返回false
  • virtual std::vector<std::string> GetFieldDomainNames(CSLConstList papszOptions = nullptr) const;
    返回字段域的名称列表,默认实现是从成员变量m_oMapFieldDomains中取出所有的key。
  • virtual const OGRFieldDomain* GetFieldDomain(const std::string& name) const;
    根据名称获取字段域,默认实现是从成员变量m_oMapFieldDomains中查找。
  • virtual OGRLayer *CreateLayer( const char *pszName, OGRSpatialReference *poSpatialRef = nullptr, OGRwkbGeometryType eGType = wkbUnknown, char ** papszOptions = nullptr );
    创建新图层,默认实现是委托给ICreateLayer(),而ICreateLayer()的默认实现是空逻辑。
  • virtual OGRLayer *CopyLayer( OGRLayer *poSrcLayer, const char *pszNewName, char **papszOptions = nullptr );
    复制一个已存在的图层,有默认实现,新图层的创建由ICreateLayer()完成,之后将原图层的各项数据复制给新图层。
  • virtual OGRStyleTable *GetStyleTable();
    获取数据集的样式表,默认实现是返回成员变量m_poStyleTable
  • virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable );
    直接设置样式表,默认实现是将形参直接赋值给成员变量m_poStyleTable
  • virtual void SetStyleTable(OGRStyleTable *poStyleTable);
    设置样式表,默认实现会先delete原指针,后执行OGRStyleTable::Clone()
  • virtual OGRLayer * ExecuteSQL( const char *pszStatement, OGRGeometry *poSpatialFilter, const char *pszDialect );
    对数据源执行SQL语句,返回执行后得到的图层,有默认实现。
  • virtual void ReleaseResultSet( OGRLayer * poResultsSet );
    释放ExecuteSQL()后得到的结果,默认实现是直接delete形参。
  • virtual OGRErr AbortSQL( );
  • virtual OGRErr StartTransaction(int bForce=FALSE);
  • virtual OGRErr CommitTransaction();
  • virtual OGRErr RollbackTransaction();
    以上API提供数据集级别的事务机制,可参考GDAL的RFC 54
  • virtual std::shared_ptr<GDALGroup> GetRootGroup() const;
    默认实现返回nullptr

5.OGRLayer虚函数

重点实现:

  • virtual OGRErr ISetFeature( OGRFeature *poFeature ) CPL_WARN_UNUSED_RESULT;
    默认实现为空逻辑,该接口为protected。
  • virtual OGRErr ICreateFeature( OGRFeature *poFeature ) CPL_WARN_UNUSED_RESULT;
    创建要素,默认实现为空逻辑,该接口为protected。
  • virtual void ResetReading() = 0;
    重置feature的遍历。
  • virtual OGRFeature *GetNextFeature() CPL_WARN_UNUSED_RESULT = 0;
    获取下一个feature。
  • virtual OGRErr DeleteFeature( GIntBig nFID ) CPL_WARN_UNUSED_RESULT;
    删除feature,默认实现为空逻辑。
  • virtual OGRFeatureDefn *GetLayerDefn() = 0;
    获取图层定义,返回的是OGRFeatureDefn 对象。
  • virtual GIntBig GetFeatureCount( int bForce = TRUE );
    获取feature数量,默认实现为遍历所有feature进行计数,计数完毕后会并调用ResetReading();
  • virtual int TestCapability( const char * ) = 0;
    验证Layer是否具有某能力,如创建feature、删除feature等。
  • virtual OGRErr Rename( const char* pszNewName ) CPL_WARN_UNUSED_RESULT;
    重命名图层,默认实现为空逻辑,根据ogrlayer.cpp中的描述,该方法的实现只存在于暴露了OLCRename能力的Layer和GDAL_DCAP_RENAME_LAYERS能力的驱动,如果数据集中已有同名Layer,应该返回False。
  • virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE );
    创建域,默认实现为空逻辑。
  • virtual OGRErr DeleteField( int iField );
    删除域,默认实现为空逻辑。
  • virtual OGRErr ReorderFields( int* panMap );
    重排序域,默认实现为空逻辑。
  • virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlagsIn );
    修改域定义,默认实现为空逻辑。
  • virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poField, int bApproxOK = TRUE );
    创建几何域,默认实现为空逻辑。

非重点实现:

  • virtual OGRGeometry *GetSpatialFilter();
    获取空间过滤几何,默认实现为返回成员变量m_poFilterGeom
  • virtual void SetSpatialFilter( OGRGeometry * );
    设置空间过滤,默认实现会委托给InstallFilter(),并调用ResetReading()
  • virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, double dfMaxX, double dfMaxY );
    设置空间过滤矩形,默认实现会构造一个矩形后,调用SetSpatialFilter()
  • virtual OGRErr SetAttributeFilter( const char * );
    设置属性过滤,有默认实现。
  • virtual OGRErr SetNextByIndex( GIntBig nIndex );
    通过下标设置下一个返回的feature,即GetNextFeature()的返回值,有默认实现。
  • virtual OGRFeature *GetFeature( GIntBig nFID ) CPL_WARN_UNUSED_RESULT;
    根据nFID获取feature对象。有默认实现。
  • virtual const char *GetName();
    获取图层名,默认实现委托给GetLayerDefn()->GetName()
  • virtual OGRwkbGeometryType GetGeomType();
    获取几何类型,默认实现委托给GetLayerDefn()->GetGeomType()
  • virtual int FindFieldIndex( const char *pszFieldName, int bExactMatch );
    根据名称获取域下标,默认实现委托给GetLayerDefn()->GetFieldIndex( pszFieldName )
  • virtual OGRSpatialReference *GetSpatialRef();
    获取空间参考系,默认实现委托给GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef()
  • virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) CPL_WARN_UNUSED_RESULT;
    获取范围,默认实现是委托给GetExtentInternal()GetExtentInternal()会遍历feature,并汇总feature的几何后得出矩形范围,最后会调用`ResetReading()``。
  • virtual OGRErr SyncToDisk();
    将缓存数据同步到硬盘,默认实现为空逻辑。
  • virtual OGRStyleTable *GetStyleTable();
    获取样式表,默认实现为返回成员变量m_poStyleTable
  • virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable );
    直接设置样式表,默认实现为将形参赋值给成员变量m_poStyleTable
  • virtual void SetStyleTable(OGRStyleTable *poStyleTable);
    设置样式表,默认实现为delete成员变量m_poStyleTable,之后调用OGRStyleTable::Clone()
  • virtual OGRErr StartTransaction() CPL_WARN_UNUSED_RESULT;
    开启事务,默认实现为空逻辑。
  • virtual OGRErr CommitTransaction() CPL_WARN_UNUSED_RESULT;
    提交事务,默认实现为空逻辑。
  • virtual OGRErr RollbackTransaction();
    回滚,默认实现为空逻辑。
  • virtual const char *GetFIDColumn();
    获取FID列名,默认实现为返回空字符串。
  • virtual const char *GetGeometryColumn();
    获取几何列,默认实现委托给GetLayerDefn()->GetGeomFieldDefn(0)->GetNameRef()
  • virtual OGRErr SetIgnoredFields( const char **papszFields );
    设置忽略的域,有默认实现。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容