根据经纬度获取当前所在城市————自己开发城市逆地址解析2(城市数据导入方法)

上篇文章讲述了根据经纬度坐标,查询所在城市的接口实现方法。本篇讲述,如何将下载的城市geojson格式的轮廓,导入到postgresql数据库中。

示例开源地址:zzgeoapi

导入遇到的问题

1、geojson中城市中心“centrer”类型为OFTRealList,在geodjango的LayerMapping内无法对应PointField,“bbox”也希望有类型无法对应的问题。

解决方法:自定义GeojsonLayerMapping,在导入时使用轮廓自己计算轮廓中心和bbox(参见_set_extern_fields方法)

2、有些城市轮廓有些问题,在判断内点时会报错。

解决方法:引入shapely库,在导入数据时判断轮廓是否可用,如果有问题,使用shapely.make_valid进行修复,修复后的GeometryCollection存入mpoly2中。

废话不说,上代码

import os
import sys
from pathlib import Path
from django.contrib.gis.utils import LayerMapping, LayerMapError
from django.contrib.gis.gdal import DataSource
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
from django.contrib.gis.db.models import GeometryField
from django.contrib.gis.gdal import (
    CoordTransform, DataSource, GDALException, OGRGeometry, OGRGeomType,
    SpatialReference,
)
from django.db import connections, models, router, transaction
from django.contrib.gis.db.models.functions import MakeValid
from django.contrib.gis.geos.error import GEOSException
# from django.contrib.gis.geos import Polygon, MultiPolygon, GeometryCollection

import shapely


from apps.zzgeo.models import AdArea

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


ERR_GEOS = []


class GeojsonLayerMapping(LayerMapping):
    def check_layer(self):
        """
        Check the Layer metadata and ensure that it's compatible with the
        mapping information and model. Unlike previous revisions, there is no
        need to increment through each feature in the Layer.
        """
        # The geometry field of the model is set here.
        # TODO: Support more than one geometry field / model.  However, this
        # depends on the GDAL Driver in use.
        self.geom_field = False
        self.fields = {}

        # Getting lists of the field names and the field types available in
        # the OGR Layer.
        ogr_fields = self.layer.fields
        ogr_field_types = self.layer.field_types

        # Function for determining if the OGR mapping field is in the Layer.
        def check_ogr_fld(ogr_map_fld):
            try:
                idx = ogr_fields.index(ogr_map_fld)
            except ValueError:
                raise LayerMapError('Given mapping OGR field "%s" not found in OGR Layer.' % ogr_map_fld)
            return idx

        # No need to increment through each feature in the model, simply check
        # the Layer metadata against what was given in the mapping dictionary.
        for field_name, ogr_name in self.mapping.items():
            # Ensuring that a corresponding field exists in the model
            # for the given field name in the mapping.
            try:
                model_field = self.model._meta.get_field(field_name)
            except FieldDoesNotExist:
                raise LayerMapError('Given mapping field "%s" not in given Model fields.' % field_name)

            # Getting the string name for the Django field class (e.g., 'PointField').
            fld_name = model_field.__class__.__name__

            if isinstance(model_field, GeometryField):
                if self.geom_field:
                    raise LayerMapError('LayerMapping does not support more than one GeometryField per model.')

                # Getting the coordinate dimension of the geometry field.
                coord_dim = model_field.dim

                try:
                    print('ogr_name: ' + ogr_name)
                    if coord_dim == 3:
                        gtype = OGRGeomType(ogr_name + '25D')
                    else:
                        gtype = OGRGeomType(ogr_name)
                except GDALException as ee:
                    raise LayerMapError('Invalid mapping for GeometryField "%s".' % field_name)

                # Making sure that the OGR Layer's Geometry is compatible.
                # ltype = self.layer.geom_type
                # if not (ltype.name.startswith(gtype.name) or self.make_multi(ltype, model_field)):
                #     raise LayerMapError('Invalid mapping geometry; model has %s%s, '
                #                         'layer geometry type is %s.' %
                #                         (fld_name, '(dim=3)' if coord_dim == 3 else '', ltype))

                # Setting the `geom_field` attribute w/the name of the model field
                # that is a Geometry.  Also setting the coordinate dimension
                # attribute.
                self.geom_field = field_name
                self.coord_dim = coord_dim
                fields_val = model_field
            elif isinstance(model_field, models.ForeignKey):
                if isinstance(ogr_name, dict):
                    # Is every given related model mapping field in the Layer?
                    rel_model = model_field.remote_field.model
                    for rel_name, ogr_field in ogr_name.items():
                        idx = check_ogr_fld(ogr_field)
                        try:
                            rel_model._meta.get_field(rel_name)
                        except FieldDoesNotExist:
                            raise LayerMapError('ForeignKey mapping field "%s" not in %s fields.' %
                                                (rel_name, rel_model.__class__.__name__))
                    fields_val = rel_model
                else:
                    raise TypeError('ForeignKey mapping must be of dictionary type.')
            else:
                # Is the model field type supported by LayerMapping?
                if model_field.__class__ not in self.FIELD_TYPES:
                    raise LayerMapError('Django field type "%s" has no OGR mapping (yet).' % fld_name)

                # Is the OGR field in the Layer?
                idx = check_ogr_fld(ogr_name)
                ogr_field = ogr_field_types[idx]

                # Can the OGR field type be mapped to the Django field type?
                if not issubclass(ogr_field, self.FIELD_TYPES[model_field.__class__]):
                    raise LayerMapError('OGR field "%s" (of type %s) cannot be mapped to Django %s.' %
                                        (ogr_field, ogr_field.__name__, fld_name))
                fields_val = model_field

            self.fields[field_name] = fields_val
            self.layer_code = int(self.layer.name)

    def _valid_mpoly(self, m):
        try:
            m.mpoly.contains(m.mpoly.centroid)
        except GEOSException:
            ERR_GEOS.append(m.mpoly)
            print('=================geo contains error==================')
            for poly in m.mpoly:
                for ring in poly:
                    print(list(ring))
            print('=================geo contains error==================')
            import shapely
            polys = []
            for poly in m.mpoly:
                _p = [shapely.Polygon(item) for item in poly]
                polys.extend(_p)
            valid_mpoly = shapely.make_valid(shapely.MultiPolygon(polys))
            from shapely.geometry.collection import GeometryCollection as _GeometryCollection
            from shapely.geometry import MultiPolygon as _MultiPolygon
            if isinstance(valid_mpoly, shapely.geometry.collection.GeometryCollection):
                setattr(m, 'mpoly2', OGRGeometry(valid_mpoly.wkt).wkt)
                m.mpoly2.contains(m.mpoly.centroid)
            elif isinstance(valid_mpoly, _MultiPolygon):
                setattr(m, 'mpoly', OGRGeometry(valid_mpoly.wkt).wkt)
                m.mpoly.contains(m.mpoly.centroid)
            else:
                print('valid_mpoly class: %s' % valid_mpoly.__class__)
                raise

    def _set_extern_fields(self, m):
        self._valid_mpoly(m)
        m.parent_code = self.layer_code
        m.center = m.mpoly.centroid
        m.bbox = m.mpoly.envelope

        print(m.parent_code)
        print(m.center)
        print(m.bbox)

    def save(self, verbose=False, fid_range=False, step=False,
             progress=False, silent=False, stream=sys.stdout, strict=False):
        default_range = self.check_fid_range(fid_range)
        # Setting the progress interval, if requested.
        if progress:
            if progress is True or not isinstance(progress, int):
                progress_interval = 1000
            else:
                progress_interval = progress

        def _save(feat_range=default_range, num_feat=0, num_saved=0):
            if feat_range:
                layer_iter = self.layer[feat_range]
            else:
                layer_iter = self.layer

            for feat in layer_iter:
                code = feat.get('code')
                if code == 0 or code is None:
                    continue
                num_feat += 1
                # Getting the keyword arguments
                try:
                    kwargs = self.feature_kwargs(feat)
                except LayerMapError as msg:
                    # Something borked the validation
                    if strict:
                        raise
                    elif not silent:
                        stream.write('Ignoring Feature ID %s because: %s\n' % (feat.fid, msg))
                else:
                    # Constructing the model using the keyword args
                    is_update = False
                    if self.unique:
                        # If we want unique models on a particular field, handle the
                        # geometry appropriately.
                        try:
                            # Getting the keyword arguments and retrieving
                            # the unique model.
                            u_kwargs = self.unique_kwargs(kwargs)
                            m = self.model.objects.using(self.using).get(**u_kwargs)
                            is_update = True

                            # Getting the geometry (in OGR form), creating
                            # one from the kwargs WKT, adding in additional
                            # geometries, and update the attribute with the
                            # just-updated geometry WKT.
                            geom_value = getattr(m, self.geom_field)
                            if geom_value is None:
                                geom = OGRGeometry(kwargs[self.geom_field])
                            else:
                                geom = geom_value.ogr
                                new = OGRGeometry(kwargs[self.geom_field])
                                for g in new:
                                    geom.add(g)
                            setattr(m, self.geom_field, geom.wkt)
                        except ObjectDoesNotExist:
                            # No unique model exists yet, create.
                            m = self.model(**kwargs)
                    else:
                        print(type(kwargs.get('mply')))
                        m = self.model(**kwargs)

                    try:
                        # Attempting to save.
                        self._set_extern_fields(m)
                        m.save(using=self.using)
                        num_saved += 1
                        if verbose:
                            stream.write('%s: %s\n' % ('Updated' if is_update else 'Saved', m))
                    except Exception as msg:
                        if strict:
                            # Bailing out if the `strict` keyword is set.
                            if not silent:
                                stream.write(
                                    'Failed to save the feature (id: %s) into the '
                                    'model with the keyword arguments:\n' % feat.fid
                                )
                                stream.write('%s\n' % kwargs)
                            raise
                        elif not silent:
                            stream.write('Failed to save %s:\n %s\nContinuing\n' % (kwargs, msg))

                # Printing progress information, if requested.
                if progress and num_feat % progress_interval == 0:
                    stream.write('Processed %d features, saved %d ...\n' % (num_feat, num_saved))

            # Only used for status output purposes -- incremental saving uses the
            # values returned here.
            return num_saved, num_feat

        if self.transaction_decorator is not None:
            _save = self.transaction_decorator(_save)

        nfeat = self.layer.num_feat
        if step and isinstance(step, int) and step < nfeat:
            # Incremental saving is requested at the given interval (step)
            if default_range:
                raise LayerMapError('The `step` keyword may not be used in conjunction with the `fid_range` keyword.')
            beg, num_feat, num_saved = (0, 0, 0)
            indices = range(step, nfeat, step)
            n_i = len(indices)

            for i, end in enumerate(indices):
                # Constructing the slice to use for this step; the last slice is
                # special (e.g, [100:] instead of [90:100]).
                if i + 1 == n_i:
                    step_slice = slice(beg, None)
                else:
                    step_slice = slice(beg, end)

                try:
                    num_feat, num_saved = _save(step_slice, num_feat, num_saved)
                    beg = end
                except Exception:  # Deliberately catch everything
                    stream.write('%s\nFailed to save slice: %s\n' % ('=-' * 20, step_slice))
                    raise
        else:
            # Otherwise, just calling the previously defined _save() function.
            _save()



area_mapping = {
    'code': 'code',
    'name': 'name',
    'fullname': 'fullname',
    # 'center': 'center',
    'children_num': 'childrenNum',
    'level': 'level',
    # 'bbox': 'bbox',
    "mpoly": "MULTIPOLYGON",
}


def import_ds(ds, verbose):
    print(ds)
    lm = GeojsonLayerMapping(AdArea, ds, area_mapping, transform=False)
    lm.save(strict=True, verbose=verbose)



def run(verbose=True):
    china_geojson = os.path.join(BASE_DIR, 'data', 'geojson', '100000.json')
    ds = DataSource(china_geojson)
    import_ds(ds, verbose)
    for item in ds[0]:
        code = item.get('code')
        if code == 0 or code is None:
            continue
        item_geojson = os.path.join(BASE_DIR, 'data', 'geojson', '%d.json' % code)
        if not os.path.exists(item_geojson):
            continue
        item_ds = DataSource(item_geojson)
        import_ds(item_ds, verbose)

导入方法

1、运行django环境

python manage.py shell

2、导入脚本

from tools import load_china_city_data as load

load.run()

 

本文抛砖引玉,欢迎讨论

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要根据经纬度获取城市信息,你需要使用地理编码(reverse geocoding)服务。以下是使用 Java 实现的示例代码,基于百度地图的地理编码 API: ```java import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; public class GeoCodingService { private static final String BAIDU_MAP_API = "http://api.map.baidu.com/geocoder/v2/?ak=YOUR_AK&location=%s,%s&output=json&pois=0"; public static void main(String[] args) throws IOException { String lat = "39.983424"; String lng = "116.322987"; String address = getAddressByGeoCoding(lat, lng); System.out.println("Address: " + address); } public static String getAddressByGeoCoding(String lat, String lng) throws IOException { String urlStr = String.format(BAIDU_MAP_API, lat, lng); URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == 200) { InputStream inputStream = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder responseBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { responseBuilder.append(line); } reader.close(); inputStream.close(); String response = responseBuilder.toString(); // 解析 JSON 响应,获取地址信息 // 这里需要使用 JSON 解析库,例如 Jackson、Gson 等 // 这里以 Jackson 为例 ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(response); JsonNode resultNode = rootNode.get("result"); if (resultNode != null) { JsonNode addressComponentNode = resultNode.get("addressComponent"); if (addressComponentNode != null) { String province = addressComponentNode.get("province").asText(); String city = addressComponentNode.get("city").asText(); String district = addressComponentNode.get("district").asText(); String street = addressComponentNode.get("street").asText(); return province + city + district + street; } } } return null; } } ``` 在示例代码中,我们使用了百度地图的地理编码服务,需要在 `BAIDU_MAP_API` 常量中填写你的百度地图 API 密钥。`getAddressByGeoCoding` 方法接收经纬度参数 `lat` 和 `lng`,发送 HTTP GET 请求到地理编码 API,获取响应后解析 JSON 响应,提取地址信息并返回。注意,示例代码仅供参考,你需要根据具体情况进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卓伙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值