代码拉取完成,页面将自动刷新
package eztools
import (
"crypto/md5"
"encoding/json"
"fmt"
"net/url"
"reflect"
"strconv"
"strings"
"time"
)
const (
// LocationTypeUnknown unknown
LocationTypeUnknown = iota
// areas from larger to smaller
// LocationTypeCountry country
LocationTypeCountry
// LocationTypeProvince province
LocationTypeProvince
// LocationTypeCity city
LocationTypeCity
// LocationTypeDistrict district
LocationTypeDistrict
// LocationTypeTown town
LocationTypeTown
// LocationTypeStreet street
LocationTypeStreet
// areas below should be small enough to calculate direction
// LocationTypeCbd CBD
LocationTypeCbd
// LocationTypeCommunity community
LocationTypeCommunity
// LocationTypeCrossing crossing
LocationTypeCrossing
// LocationTypePoi POI
LocationTypePoi
// LocationTypeNumber number
LocationTypeNumber
// LocationTypeMax quantity of location types
LocationTypeMax
// UnitTypeM factor for meters
UnitTypeM = 1.0
// UnitTypeKm factor for kilometers
UnitTypeKm = 1000.0
// UnitTypeSec factor for seconds
UnitTypeSec = 1.0 / 60.0
// UnitTypeMin factor for minutes
UnitTypeMin = 1.0
// UnitTypeHour factor for hours
UnitTypeHour = 60.0
// LocationTypeStrUnknown location type strings in configuration file
LocationTypeStrUnknown = "unknown"
// LocationTypeStrCountry location type strings in configuration file
LocationTypeStrCountry = "country"
// LocationTypeStrProvince location type strings in configuration file
LocationTypeStrProvince = "province"
// LocationTypeStrCity location type strings in configuration file
LocationTypeStrCity = "city"
// LocationTypeStrDistrict location type strings in configuration file
LocationTypeStrDistrict = "district"
// LocationTypeStrTown location type strings in configuration file
LocationTypeStrTown = "town"
// LocationTypeStrCbd location type strings in configuration file
LocationTypeStrCbd = "cbd"
// LocationTypeStrPoi location type strings in configuration file
LocationTypeStrPoi = "poi"
// LocationTypeStrStreet location type strings in configuration file
LocationTypeStrStreet = "street"
// LocationTypeStrCrossing location type strings in configuration file
LocationTypeStrCrossing = "crossing"
// LocationTypeStrNumber location type strings in configuration file
LocationTypeStrNumber = "number"
// LocationTypeStrCommunity location type strings in configuration file
LocationTypeStrCommunity = "community"
// UnitTypeStrKm is kilometer
UnitTypeStrKm = "km"
// UnitTypeStrM is meter. this is default
UnitTypeStrM = "m"
// UnitTypeStrHour is hour
UnitTypeStrHour = "hour"
// UnitTypeStrMin is minute. this is default
UnitTypeStrMin = "min"
// UnitTypeStrSec is second
UnitTypeStrSec = "sec"
// reply type strings in configuration file
// MapReplyTypeStatus is OK for results
MapReplyTypeStatus = "status"
// MapReplyTypeResults contains sublevel of results
MapReplyTypeResults = "results"
// MapReplyTypeLevel is the level/type of a location
MapReplyTypeLevel = "level"
// MapReplyTypeKnown is the level/type of a location
MapReplyTypeKnown = "known"
// MapReplyTypeLocation contains location info
MapReplyTypeLocation = "location"
// MapReplyTypeLati is latitude string in reply
MapReplyTypeLati = "lati"
// MapReplyTypeLongi is longitude string in reply
MapReplyTypeLongi = "long"
// MapReplyTypeDistance is the distance between two locations
MapReplyTypeDistance = "distance"
// MapReplyTypeDuration is the duration between two locations
MapReplyTypeDuration = "duration"
// MapReplyTypeDistanceUnit is the distance between two locations
MapReplyTypeDistanceUnit = "distanceUnit"
// MapReplyTypeDurationUnit is the duration between two locations
MapReplyTypeDurationUnit = "durationUnit"
// MapReplyStruMap map definition in config
MapReplyStruMap = "map"
// MapReplyStruMapslc slice definition in config
MapReplyStruMapslc = "slicedmap"
// PathTypeStrWalk is path calculation for walking
PathTypeStrWalk = "walk"
// MapLaloTypeStrLalo = latitude<separator>longitude
MapLaloTypeStrLalo = "lalo"
// MapLaloTypeStrLola = longitude<separator>latitude
MapLaloTypeStrLola = "lola"
)
// CfgMapParams params of a map in cfg
type CfgMapParams struct {
Cmt string `xml:",comment"`
Name string `xml:"name,attr"`
Max string `xml:"max,attr"`
Min string `xml:"min,attr"`
Text string `xml:",chardata"`
}
// CfgMapReply struct of a reply
type CfgMapReply struct {
Cmt string `xml:",comment"`
Type string `xml:"type,attr"`
Name string `xml:"name,attr"`
Stru string `xml:"stru,attr"`
Reply []CfgMapReply `xml:"reply"`
Text string `xml:",chardata"`
}
// CfgMapTypes map type config
type CfgMapTypes struct {
Cmt string `xml:",comment"`
// Text is not used
Text string `xml:",chardata"`
Level []struct {
Cmt string `xml:",comment"`
Type string `xml:"type,attr"`
Text string `xml:",chardata"`
} `xml:"level"`
Unit []struct {
Cmt string `xml:",comment"`
Type string `xml:"type,attr"`
Text string `xml:",chardata"`
} `xml:"unit"`
lvlCfg Pairs[string, string]
lvlPairs Pairs[string, int]
untCfg Pairs[string, string]
untPairs Pairs[string, float32]
}
// CfgMapStatic map static cfg
type CfgMapStatic struct {
Cmt string `xml:",comment"`
Name string `xml:"name,attr"`
URLHost string `xml:"urlHost,attr"`
URLPath string `xml:"urlPath,attr"`
Max int `xml:"max,attr"`
Param []CfgMapParams `xml:"param"`
Labels struct {
Cmt string `xml:",comment"`
Center string `xml:"center,attr"`
Position int `xml:"position,attr"`
Separator string `xml:"separator,attr"`
Lead string `xml:"lead,attr"`
Colon string `xml:"colon,attr"`
Text string `xml:",chardata"`
} `xml:"labels"`
// Text can be used as name for different kinds of maps
Text string `xml:",chardata"`
}
// CfgMap map config
type CfgMap struct {
Cmt string `xml:",comment"`
Name string `xml:"name,attr"`
Key string `xml:"key,attr"`
KeyName string `xml:"keyName,attr"`
Separator string `xml:"separator,attr"`
PathOri string `xml:"ori,attr"`
PathDst string `xml:"dst,attr"`
Md5Key string `xml:"md5Key,attr"`
Md5Pref string `xml:"md5Name,attr"`
Lalo string `xml:"lalo,attr"`
TimeStamp string `xml:"timestamp,attr"`
// Text is not used
Text string `xml:",chardata"`
Locate struct {
Cmt string `xml:",comment"`
URLHost string `xml:"urlHost,attr"`
URLPath string `xml:"urlPath,attr"`
Addr string `xml:"addr,attr"`
Param []CfgMapParams `xml:"param"`
Reply []CfgMapReply `xml:"reply"`
// Text is not used
Text string `xml:",chardata"`
} `xml:"locate"`
Types CfgMapTypes `xml:"types"`
Paths []struct {
Cmt string `xml:",comment"`
Type string `xml:"type,attr"`
Name string `xml:"name,attr"`
URLHost string `xml:"urlHost,attr"`
URLPath string `xml:"urlPath,attr"`
Param []CfgMapParams `xml:"param"`
Reply []CfgMapReply `xml:"reply"`
// Text is not used
Text string `xml:",chardata"`
} `xml:"path"`
Static []CfgMapStatic `xml:"static"`
}
// PathInfo info of a path
type PathInfo struct {
// there may be more members in this struct,
// so use member names during instance creation
Distance, Duration float32
UnitDistance, UnitDuration float32
}
// Normalize calculates a value in a unit to a default one
func (path PathInfo) Normalize(val, unit float32) (ret float32) {
if unit == 0 {
return val
}
return val * unit
}
// ParseUnit parses a unit
func (path *PathInfo) ParseUnit(types CfgMapTypes, v interface{}) (ret float32) {
str, ok := v.(string)
if !ok {
return
}
if types.untCfg.Len() < 1 {
for _, cfg1 := range types.Unit {
types.untCfg.Add(cfg1.Text, cfg1.Type)
}
}
if types.untPairs.Len() < 1 {
types.untPairs.Add(UnitTypeStrHour,
UnitTypeHour)
types.untPairs.Add(UnitTypeStrMin,
UnitTypeMin)
types.untPairs.Add(UnitTypeStrSec,
UnitTypeSec)
types.untPairs.Add(UnitTypeStrM,
UnitTypeM)
types.untPairs.Add(UnitTypeStrKm,
UnitTypeKm)
}
untStr, err := types.untCfg.FindKey(str)
if err != nil {
if Debugging && Verbose > 1 {
Log(str, "NOT matched")
}
return
}
found, err := types.untPairs.FindKey(untStr)
if err != nil {
if Debugging && Verbose > 1 {
LogPrint(err)
}
return
}
return found
}
// ParseFlt parses a float
func (path *PathInfo) ParseFlt(v interface{}) (ret32 float32) {
var ret64 float64
vs, ok := v.(string)
if ok {
if len(vs) < 1 {
return
}
ret64, _ = strconv.ParseFloat(vs, 32)
} else {
v3, ok := v.(float32)
if ok {
path.Distance = v3
return v3
}
ret64, ok = v.(float64)
if !ok {
return
}
}
return float32(ret64)
}
// LocationInfo location info
type LocationInfo struct {
// there may be more members in this struct,
// so use member names during instance creation
Latitude, Longitude float32
Type int
Label, TypeStr string
}
func (loc LocationInfo) String(separator, order string) string {
switch order {
case MapLaloTypeStrLola:
return fmt.Sprintf("%f%s%f",
loc.Longitude, separator, loc.Latitude)
default:
return fmt.Sprintf("%f%s%f",
loc.Latitude, separator, loc.Longitude)
}
}
// ParseLaLo sets a string or float64 to latitude or longitude
func (loc *LocationInfo) ParseLaLo(in interface{}, lalo string) bool {
lf, ok := in.(float64)
if !ok {
str, ok := in.(string)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(in).String() +
" got instead of float or string")
}
return false
}
lf, _ = strconv.ParseFloat(str, 32)
}
switch lalo {
case MapReplyTypeLati:
loc.Latitude = float32(lf)
case MapReplyTypeLongi:
loc.Longitude = float32(lf)
}
return true
}
// Parse parses latitude and longitude
// Parameter:
//
// if in is string, is needs to be like <l1><separator><l2>, l1/l2=latitude/longitude
// otherwise, in can be a slice, containing two strings or floats
func (loc *LocationInfo) Parse(in interface{}, separator, order string) bool {
var (
locArr [2]string
locFlt [2]float64
parsed bool
)
str, ok := in.(string)
if ok {
slc := strings.Split(str, separator)
if len(slc) < 2 {
return false
}
locArr[0], locArr[1] = slc[0], slc[1]
} else {
slc, ok := in.([]interface{})
if !ok || len(slc) < 2 {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(in).String() +
" got instead of string or slice of two")
}
return false
}
for n, i := range slc {
str, ok := i.(string)
if ok {
locArr[n] = str
} else {
flt, ok := i.(float64)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(i).String()+
" got instead of string slice or float", i)
}
return false
}
locFlt[n] = flt
parsed = true
}
if n >= 1 {
break
}
}
}
if !parsed {
for i := 0; i < len(locArr); i++ {
locFlt[i], _ = strconv.ParseFloat(locArr[i], 32)
}
}
switch order {
case MapLaloTypeStrLola:
loc.Latitude, loc.Longitude =
float32(locFlt[1]), float32(locFlt[0])
default:
loc.Latitude, loc.Longitude =
float32(locFlt[0]), float32(locFlt[1])
}
return true
}
// SetLabel sets label for a location
// Return value: whether set
func (loc *LocationInfo) SetLabel(lbl any) bool {
str, ok := lbl.(string)
if !ok {
return false
}
loc.Label = str
return true
}
// ParseLevel parses level/type from string or float64
//
// TypeStr is set to the string value,
// Type is set to recognized level/type, or,
// LOCATION_TYPE_UNKNOWN if the string is not configured for the map
// LOCATION_TYPE_MAX if the config string is not in code,
// which is a critical coding issue
func (loc *LocationInfo) ParseLevel(types *CfgMapTypes, v any) {
if v == nil {
return
}
str, ok := v.(string)
if !ok {
flt, ok := v.(float64)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(v).String() +
" got instead of string or float")
}
return
}
str = strconv.FormatFloat(flt, 'f', -1, 64)
}
loc.TypeStr = str
if types.lvlCfg.Len() < 1 {
for _, cfg1 := range types.Level {
types.lvlCfg.Add(cfg1.Text, cfg1.Type)
}
}
if types.lvlPairs.Len() < 1 {
types.lvlPairs.Add(LocationTypeStrNumber,
LocationTypeNumber)
types.lvlPairs.Add(LocationTypeStrCommunity,
LocationTypeCommunity)
types.lvlPairs.Add(LocationTypeStrCrossing,
LocationTypeCrossing)
types.lvlPairs.Add(LocationTypeStrCbd,
LocationTypeCbd)
types.lvlPairs.Add(LocationTypeStrCity,
LocationTypeCity)
types.lvlPairs.Add(LocationTypeStrCountry,
LocationTypeCountry)
types.lvlPairs.Add(LocationTypeStrDistrict,
LocationTypeDistrict)
types.lvlPairs.Add(LocationTypeStrPoi,
LocationTypePoi)
types.lvlPairs.Add(LocationTypeStrProvince,
LocationTypeProvince)
types.lvlPairs.Add(LocationTypeStrTown,
LocationTypeTown)
types.lvlPairs.Add(LocationTypeStrStreet,
LocationTypeStreet)
}
lvlStr, err := types.lvlCfg.FindKey(str)
if err != nil {
if Debugging && Verbose > 1 {
Log(str, "NOT matched", types.lvlCfg)
Log(err)
}
return
}
found, err := types.lvlPairs.FindKey(lvlStr)
if err != nil {
if Debugging && Verbose > 1 {
LogPrint(err)
}
loc.Type = LocationTypeMax
} else {
loc.Type = found
}
}
const (
mapCfgEqual = "="
paramSeparator = "&"
)
// ReorderParams4Map reorders Pairs and return a string for preMd5
//
// may be used as preMd5Params
func ReorderParams4Map(pi Pairs[string, string]) (
po Pairs[string, string], ret string) {
po = pi
po.Sort()
var (
i, v string
err error
)
for i, v, err = po.GetNMove(); err == nil; i, v, err = po.GetNMove() {
if len(ret) > 0 {
ret += paramSeparator
}
ret += i + mapCfgEqual + v
}
return
}
// EscapeStr4Map reorders Pairs and return a string for preMd5
//
// may be used as preMd5Whole
func EscapeStr4Map(str string) string {
return url.QueryEscape(str)
}
func md5FromParams(cfg CfgMap, uri string, params Pairs[string, string],
preMd5Params func(Pairs[string, string]) (Pairs[string, string], string),
preMd5Whole func(string) string) string {
var paramStr string
if preMd5Params != nil {
params, paramStr = preMd5Params(params)
}
if len(paramStr) < 1 {
params.Rewind()
for {
name, value, err := params.GetNMove()
if err != nil {
break
}
if len(paramStr) > 0 {
paramStr += paramSeparator
}
paramStr += name + mapCfgEqual + value
}
}
uri += paramStr
if len(cfg.Md5Key) < 1 && len(cfg.Md5Pref) < 1 {
if preMd5Whole != nil {
return preMd5Whole(uri)
}
return uri
}
str := uri + cfg.Md5Key
if preMd5Whole != nil {
str = preMd5Whole(uri + cfg.Md5Key)
if len(str) < 1 {
return uri
}
}
ret := md5.Sum([]byte(str))
return uri + paramSeparator + cfg.Md5Pref + mapCfgEqual + fmt.Sprintf("%x", ret)
}
// Return values:
//
// ErrNoValidResults = status in reply is not OK
func (cfg CfgMap) parseReply(cfgLvl []CfgMapReply,
json interface{}, in interface{},
funcNew func() interface{},
funcIng func(interface{}, *CfgMapReply, interface{}) bool,
funcEd func(lcl, sub bool,
in interface{}, inf interface{}) (interface{}, error)) (
out interface{}, err error) {
out = in
res, ok := json.(map[string]interface{})
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(json).String()+
" got instead of map", json)
}
return out, ErrOutOfBound
}
var foundLcl, foundSub bool
inf := funcNew()
if Debugging && Verbose > 2 {
LogPrint("trying to match to reply structure", cfgLvl)
}
for i, v := range res {
if Debugging && Verbose > 2 {
LogPrint("[", i, "]", "=", v)
}
if v == nil {
continue
}
var cfgLvl1 *CfgMapReply
for _, c := range cfgLvl {
if i == c.Name {
cfgLvl1 = &c
break
}
}
if cfgLvl1 == nil {
continue
}
switch cfgLvl1.Stru {
case MapReplyStruMapslc:
// should I care about type="results"?
slc, ok := v.([]interface{})
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(v).String() +
" got instead of slice")
}
} else {
for _, slc1 := range slc {
out, err = cfg.parseReply(cfgLvl1.Reply,
slc1, out, funcNew, funcIng, funcEd)
switch err {
case nil:
foundSub = true
case ErrNoValidResults:
return
}
}
}
default:
switch cfgLvl1.Type {
case MapReplyTypeResults:
out, err = cfg.parseReply(cfgLvl1.Reply,
v, out, funcNew, funcIng, funcEd)
switch err {
case nil:
foundSub = true
case ErrNoValidResults:
return
}
case MapReplyTypeStatus:
vs, ok := v.(string)
if !ok {
vi, ok := v.(float64)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(v).String() +
" got instead of string or float")
}
} else {
vf, err := strconv.ParseFloat(cfgLvl1.Text, 64)
if err != nil || vi != vf {
return nil, ErrNoValidResults
}
}
} else {
if vs != cfgLvl1.Text {
return nil, ErrNoValidResults
}
}
default:
foundLcl = funcIng(v, cfgLvl1, inf) || foundLcl
/*i, ok := inf.(*PathInfos)
if !ok {
Log(reflect.TypeOf(inf).String() +
" got instead of addr of path")
} else {
LogPrint(i)
}*/
}
}
}
return funcEd(foundLcl, foundSub, out, inf)
}
// Return values:
//
// ErrOutOfBound if json fails to parse
// ErrNoValidResults if not OK in response
// other errors from json.Unmarshal
func (cfg *CfgMap) parseLocation(cfgLvl []CfgMapReply,
body []byte, in []LocationInfo) (out []LocationInfo, err error) {
var strucJsn interface{}
if err := json.Unmarshal(body, &strucJsn); err != nil {
/*if Debugging && Verbose > 1 {
LogErr(err)
}*/
return nil, err
}
ret, err := cfg.parseReply(cfgLvl, strucJsn, in,
func() interface{} {
return new(LocationInfo)
},
func(v interface{}, cfgReply *CfgMapReply, inf interface{}) bool {
infT, ok := inf.(*LocationInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(inf).String() +
" got instead of Location pointer")
}
return false
}
switch cfgReply.Type {
case MapReplyTypeLevel:
infT.ParseLevel(&cfg.Types, v)
case MapReplyTypeKnown:
infT.SetLabel(v)
case MapReplyTypeLati, MapReplyTypeLongi:
infT.ParseLaLo(v, cfgReply.Type)
return true
case MapReplyTypeLocation:
infT.Parse(v, cfg.Separator, cfg.Lalo)
// as long as we have tried to parse real position,
// the result is returned
return true
}
return false
},
func(lcl, sub bool, in interface{}, inf interface{}) (interface{}, error) {
ins, ok := in.([]LocationInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(in).String() +
" got instead of slice")
}
return in, nil
}
inp, ok := inf.(*LocationInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(inf).String() +
" got instead of location")
}
return in, nil
}
if !lcl {
if sub {
if len(inp.Label) > 0 ||
len(inp.TypeStr) > 0 ||
inp.Type != 0 {
// sub level has progress,
// let's change it
ins[len(ins)-1].TypeStr,
ins[len(ins)-1].Label,
ins[len(ins)-1].Type =
inp.TypeStr,
inp.Label,
inp.Type
}
return ins, nil
}
return in, ErrNoValidResults
}
return append(ins, *inp), nil
})
if err != nil || ret == nil {
return nil, err
}
out, ok := ret.([]LocationInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(ret).String() +
" got instead of slice of location")
}
return nil, ErrOutOfBound
}
return
}
// Return values:
//
// ErrOutOfBound if json fails to parse
// ErrNoValidResults if not OK or no results parsed in response
// other errors from json.Unmarshal
func (cfg CfgMap) parsePathWalk(cfgLvl []CfgMapReply,
body []byte, in []PathInfo) (out []PathInfo, err error) {
var strucJsn interface{}
if err := json.Unmarshal(body, &strucJsn); err != nil {
/*if Debugging && Verbose > 1 {
LogErr(err)
}*/
return nil, err
}
ret, err := cfg.parseReply(cfgLvl, strucJsn, in,
func() interface{} {
return new(PathInfo)
},
func(v interface{}, cfgReply *CfgMapReply, inf interface{}) bool {
infT, ok := inf.(*PathInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(inf).String() +
" got instead of Path pointer")
}
return false
}
switch cfgReply.Type {
// only distance is regarded as a key info
case MapReplyTypeDistance:
infT.Distance = infT.ParseFlt(v)
return true
case MapReplyTypeDuration:
infT.Duration = infT.ParseFlt(v)
case MapReplyTypeDistanceUnit:
infT.UnitDistance = infT.ParseUnit(cfg.Types, v)
case MapReplyTypeDurationUnit:
infT.UnitDuration = infT.ParseUnit(cfg.Types, v)
}
return false
},
func(lcl, sub bool, in interface{}, inf interface{}) (interface{}, error) {
ins, ok := in.([]PathInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(in).String() +
" got instead of slice")
}
return in, nil
}
inp, ok := inf.(*PathInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(inf).String() +
" got instead of path")
}
return in, nil
}
if !lcl {
return in, nil
}
/*if true {
t := append(ins, *inp)
Log("appended=", t)
return t, nil
}*/
return append(ins, *inp), nil
})
if err != nil {
return nil, err
}
if ret == nil {
return nil, ErrNoValidResults
}
out, ok := ret.([]PathInfo)
if !ok {
if Debugging && Verbose > 1 {
Log(reflect.TypeOf(ret).String() +
" got instead of slice of path")
}
return nil, ErrOutOfBound
}
if len(out) < 1 {
return nil, ErrNoValidResults
}
return
}
// CanParseAddr checks whether a map can parse an address
func (cfg CfgMap) CanParseAddr() bool {
if len(cfg.Key) < 1 || len(cfg.Locate.Addr) < 1 || len(cfg.Locate.URLHost) < 1 {
return false
}
return true
}
// Addr2Location returns location info from address string
// Parameters:
//
// some maps require addr to be url.QueryEscape()'ed as a param
// preMd5Params & preMd5Whole are invoked, after all params are read,
// and to turn params into string, respectively, even if MD5 is not configured
//
// Return values:
//
// body in response returned for error analysis
// ErrOutOfBound if json fails to parse
// ErrNoValidResults if not OK in response
func (cfg CfgMap) Addr2Location(addr string,
preMd5Params func(Pairs[string, string]) (Pairs[string, string], string),
preMd5Whole func(string) string) ([]LocationInfo, []byte, error) {
if !cfg.CanParseAddr() {
return nil, nil, ErrInvalidInput
}
var params Pairs[string, string]
locate := cfg.Locate
params.Add(cfg.KeyName, cfg.Key)
params.Add(locate.Addr, addr)
for _, i := range locate.Param {
if len(i.Text) > 0 {
params.Add(i.Name, i.Text)
}
}
// TODO: url encode?
uri := md5FromParams(cfg, locate.URLPath, params, preMd5Params, preMd5Whole)
resp, err := HTTPSend(MethodGet, locate.URLHost+uri, nil)
if err != nil {
return nil, nil, err
}
_, _, body, _, err := RestParseBody(resp, "", nil, nil)
// sometimes content type is not written as json, so we have to unmarshal it anyway
if err != nil {
return nil, nil, err
}
if body == nil {
return nil, nil, ErrNoValidResults
}
loc, err := cfg.parseLocation(locate.Reply, body, nil)
return loc, body, err
}
// CanRouteWalk checks whether a map can calculate route by walk
func (cfg CfgMap) CanRouteWalk() bool {
if len(cfg.Key) < 1 || cfg.Paths == nil ||
len(cfg.PathOri) < 1 || len(cfg.PathDst) < 1 {
return false
}
for _, p := range cfg.Paths {
if p.Type == PathTypeStrWalk {
if len(p.URLHost) > 0 || len(p.URLPath) > 0 {
return true
}
}
}
return false
}
// CalcRouteWalk returns route info between two locations
// Parameters: preMd5Params & preMd5Whole are invoked, after all params are read,
//
// and to turn params into string, respectively, even if MD5 is not configured
//
// Return values:
//
// ErrOutOfBound if json fails to parse
// ErrNoValidResults if not OK in response
func (cfg CfgMap) CalcRouteWalk(loc [2]LocationInfo,
preMd5Params func(Pairs[string, string]) (Pairs[string, string], string),
preMd5Whole func(string) string) (
[]PathInfo, []byte, error) {
if !cfg.CanRouteWalk() {
return nil, nil, ErrInvalidInput
}
for _, path := range cfg.Paths {
if path.Type != PathTypeStrWalk ||
(len(path.URLHost) < 1 && len(path.URLPath) < 1) {
continue
}
var params Pairs[string, string]
params.Add(cfg.KeyName, cfg.Key)
params.Add(cfg.PathOri, loc[0].String(cfg.Separator, cfg.Lalo))
params.Add(cfg.PathDst, loc[1].String(cfg.Separator, cfg.Lalo))
if len(cfg.TimeStamp) > 0 {
params.Add(cfg.TimeStamp,
strconv.FormatUint(uint64(time.Now().Unix()),
10))
}
for _, i := range path.Param {
if len(i.Text) > 0 {
params.Add(i.Name, i.Text)
}
}
//uri = url.PathEscape(uri)
// TODO: url encode?
uri := md5FromParams(cfg, path.URLPath, params, preMd5Params, preMd5Whole)
resp, err := HTTPSend(MethodGet, path.URLHost+uri, nil)
if err != nil {
return nil, nil, err
}
_, _, body, _, err := RestParseBody(resp, "", nil, nil)
// sometimes content type is not written as json, so we have to unmarshal it anyway
if err != nil {
return nil, body, err
}
if body == nil {
return nil, body, ErrNoValidResults
}
//LogPrint(body)
inf, err := cfg.parsePathWalk(path.Reply, body, nil)
return inf, body, err
}
return nil, nil, ErrOutOfBound
}
// CanStaticMap checks whether a map is configured to make static maps
// if quan is non-zero, the map is able to hold that number of label/pushpins
func (cfg CfgMapStatic) CanStaticMap(quan int) bool {
if len(cfg.URLHost) < 1 && len(cfg.URLPath) < 1 {
return false
}
if quan <= 0 {
return true
}
return quan <= cfg.Max
}
// CanStaticMap checks whether a map is configured to make static maps
// if quan is non-zero, the map is able to hold that number of label/pushpins
// if name is non-zero, the map is named, whose text is, as it
func (cfg CfgMap) CanStaticMap(quan int) bool {
if len(cfg.Key) < 1 || cfg.Static == nil {
return false
}
for _, s := range cfg.Static {
if s.CanStaticMap(quan) {
return true
}
}
return false
}
// StaticMap2File saves a map picture with locations to a file
//
// it tries all static map configured and saves all
//
// Parameters:
//
// size of file should be same as static maps configured, including invalid
// loc[0] is the center, if center is configured
// preMd5Params & preMd5Whole are invoked, after all params are read,
// and to turn params into string, respectively, even if MD5 is not configured
//
// Return value: error is the first error of a map
//
// ErrInvalidInput=not configured
// ErrOutOfBound=no file saved
func (cfg CfgMap) StaticMap2File(file []string, loc []LocationInfo,
preMd5Params func(Pairs[string, string]) (Pairs[string, string], string),
preMd5Whole func(string) string) (ret error) {
if cfg.Static == nil || len(cfg.Static) < 1 {
return ErrInvalidInput
}
for i, c := range cfg.Static {
var fn string
if file != nil && len(file) > i {
fn = file[i]
}
_, err := c.StaticMap2File(cfg, fn, loc,
preMd5Params, preMd5Whole)
if ret == nil {
ret = err
}
}
return
}
// StaticMap2File saves a map picture with locations to a file
// Parameter: loc[0] is the center
// Return value:
//
// ErrInvalidInput=not configured
// ErrOutOfBound=no file saved
func (cfg CfgMapStatic) StaticMap2File(cfgM CfgMap,
file string, loc []LocationInfo,
preMd5Params func(Pairs[string, string]) (Pairs[string, string], string),
preMd5Whole func(string) string) ([]byte, error) {
if !cfgM.CanStaticMap(len(loc)) {
return nil, ErrInvalidInput
}
var params Pairs[string, string]
params.Add(cfgM.KeyName, cfgM.Key)
for _, i := range cfg.Param {
if len(i.Text) > 0 {
params.Add(i.Name, i.Text)
}
}
lbl := cfg.Labels
if len(lbl.Center) > 0 {
params.Add(lbl.Center, loc[0].String(cfgM.Separator, cfgM.Lalo))
}
if len(lbl.Text) > 0 {
labels := ""
addLbl := func(i LocationInfo) (labels string) {
if len(lbl.Lead) > 0 {
labels += lbl.Lead
}
switch lbl.Position {
case 2:
labels += i.String(cfgM.Separator, cfgM.Lalo)
if len(i.Label) > 0 && len(lbl.Colon) > 0 {
labels += lbl.Colon + i.Label
}
default: //case 0
if len(i.Label) > 0 && len(lbl.Colon) > 0 {
labels += i.Label + lbl.Colon
}
labels += i.String(cfgM.Separator, cfgM.Lalo)
}
return labels
}
for n, i := range loc {
if len(lbl.Center) > 0 && n == 0 {
// skip center
continue
}
if len(lbl.Separator) > 0 {
if len(labels) > 0 {
labels += lbl.Separator
}
labels += addLbl(i)
} else {
params.Add(lbl.Text, addLbl(i))
}
}
if len(labels) > 0 {
params.Add(lbl.Text, labels)
}
}
// TODO: url encode?
uri := md5FromParams(cfgM, cfg.URLPath, params, preMd5Params, preMd5Whole)
resp, err := HTTPSend(MethodGet, cfg.URLHost+uri, nil)
if err != nil {
return nil, err
}
bodyType, _, body, _, err := RestParseBody(resp, file, nil, nil)
if err != nil {
return body, err
}
if bodyType != BodyTypeFile {
if Debugging && Verbose > 1 {
Log("body type", resp.Header.Get("Content-Type"))
Log("body", string(body))
}
return body, ErrOutOfBound
}
return body, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。